Skip to content

Mandatory optimizations: constant fold boolean literals before the DefiniteInitialization pass #70787

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 11, 2024

Conversation

eeckstein
Copy link
Contributor

@eeckstein eeckstein commented Jan 9, 2024

Add a new mandatory BooleanLiteralFolding pass which constant folds conditional branches with boolean literals as operands.

  %1 = integer_literal -1
  %2 = apply %bool_init(%1)   // Bool.init(_builtinBooleanLiteral:)
  %3 = struct_extract %2, #Bool._value
  cond_br %3, bb1, bb2

->

  ...
  br bb1

This pass is intended to run before DefiniteInitialization, where mandatory inlining and constant folding didn't run, yet (which would perform this kind of optimization).
This optimization is required to let DefiniteInitialization handle boolean literals correctly.
For example in infinite loops:

   init() {
     while true {           // DI needs to know that there is no loop exit from this while-statement
       if some_condition {
         member_field = init_value
         break
       }
     }
   }

@eeckstein eeckstein requested a review from a team as a code owner January 9, 2024 12:53
@eeckstein eeckstein requested review from jckarter and atrick January 9, 2024 12:54
@eeckstein
Copy link
Contributor Author

@swift-ci test

1 similar comment
@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci test

This is required for being able to constant fold boolean literals before the DefiniteInitialization pass
…finiteInitialization pass

Add a new mandatory BooleanLiteralFolding pass which constant folds conditional branches with boolean literals as operands.

```
  %1 = integer_literal -1
  %2 = apply %bool_init(%1)   // Bool.init(_builtinBooleanLiteral:)
  %3 = struct_extract %2, #Bool._value
  cond_br %3, bb1, bb2
```
->
```
  ...
  br bb1
```

This pass is intended to run before DefiniteInitialization, where mandatory inlining and constant folding didn't run, yet (which would perform this kind of optimization).
This optimization is required to let DefiniteInitialization handle boolean literals correctly.
For example in infinite loops:

```
   init() {
     while true {           // DI need to know that there is no loop exit from this while-statement
       if some_condition {
         member_field = init_value
         break
       }
     }
   }
```
@eeckstein eeckstein force-pushed the boolean-literal-folding branch from cd73481 to c89df9e Compare January 10, 2024 15:30
@eeckstein
Copy link
Contributor Author

@swift-ci test

@karwa
Copy link
Contributor

karwa commented Jan 10, 2024

Nice. I was looking in to a related issue which might also be fixed by this: #70655

I diffed the SIL produced by each version (I think it's the optimised SIL; I compiled with -O -emit-silgen), and there is one difference. For precondition(!x):

sil private [transparent] [ossa] @$s6outputSbyXEfu_ : $@convention(thin) (Bool) -> Bool {
bb0(%0 : @closureCapture $Bool):
  debug_value %0 : $Bool, let, name "x", argno 1, loc "/app/example.swift":3:13, scope 8 // id: %1
  %2 = metatype $@thin Bool.Type, loc "/app/example.swift":4:22, scope 8 // user: %4
  %3 = function_ref @$sSb1nopyS2bFZ : $@convention(method) (Bool, @thin Bool.Type) -> Bool, loc "/app/example.swift":4:22, scope 8 // user: %4
  %4 = apply %3(%0, %2) : $@convention(method) (Bool, @thin Bool.Type) -> Bool, loc "/app/example.swift":4:22, scope 8 // user: %5
  return %4 : $Bool, loc "/app/example.swift":4:23, scope 8 // id: %5
} // end sil function '$s6outputSbyXEfu_'

sil [transparent] [serialized] @$sSb1nopyS2bFZ : $@convention(method) (Bool, @thin Bool.Type) -> Bool

Whereas for precondition(x == false):

sil private [transparent] [ossa] @$s6outputSbyXEfu_ : $@convention(thin) (Bool) -> Bool {
bb0(%0 : @closureCapture $Bool):
  debug_value %0 : $Bool, let, name "x", argno 1, loc "/app/example.swift":3:13, scope 8 // id: %1
  %2 = metatype $@thin Bool.Type, loc "/app/example.swift":4:24, scope 8 // user: %8
  %3 = integer_literal $Builtin.Int1, 0, loc "/app/example.swift":4:27, scope 8 // user: %6
  %4 = metatype $@thin Bool.Type, loc "/app/example.swift":4:27, scope 8 // user: %6
  %5 = function_ref @$sSb22_builtinBooleanLiteralSbBi1__tcfC : $@convention(method) (Builtin.Int1, @thin Bool.Type) -> Bool, loc "/app/example.swift":4:27, scope 8 // user: %6
  %6 = apply %5(%3, %4) : $@convention(method) (Builtin.Int1, @thin Bool.Type) -> Bool, loc "/app/example.swift":4:27, scope 8 // user: %8
  %7 = function_ref @$sSb2eeoiyS2b_SbtFZ : $@convention(method) (Bool, Bool, @thin Bool.Type) -> Bool, loc "/app/example.swift":4:24, scope 8 // user: %8
  %8 = apply %7(%0, %6, %2) : $@convention(method) (Bool, Bool, @thin Bool.Type) -> Bool, loc "/app/example.swift":4:24, scope 8 // user: %9
  return %8 : $Bool, loc "/app/example.swift":4:27, scope 8 // id: %9
} // end sil function '$s6outputSbyXEfu_'

sil [transparent] [serialized] @$sSb22_builtinBooleanLiteralSbBi1__tcfC : $@convention(method) (Builtin.Int1, @thin Bool.Type) -> Bool

sil [transparent] [serialized] @$sSb2eeoiyS2b_SbtFZ : $@convention(method) (Bool, Bool, @thin Bool.Type) -> Bool

Note the call to sSb22_builtinBooleanLiteralSbBi1__tcfC (Swift.Bool.init(_builtinBooleanLiteral: Builtin.Int1) -> Swift.Bool) at %5.

Sorry if it's not related - I don't mean to just mention random issues, but the pattern seems very similar to the pattern that you mention in the PR description. Is this something that would also be resolved by this new mandatory pass?

@Azoy
Copy link
Contributor

Azoy commented Jan 10, 2024

@karwa calls to Bool(_builtinBooleanLiteral:) always get optimized away and only show up in SILGen which is the lowering step from AST to SIL. To see optimized SIL you want to use -O -emit-sil and you'll note that that call gets eliminated.

@asl
Copy link
Contributor

asl commented Feb 8, 2024

@eeckstein I am having one question / concern. This is a mandatory pass written in Swift. If swift is not in the compiler (e.g. on Windows or non-bootstrapped build), then the pass is just silently disabled, however, it's a mandatory one.

Should there be some diagnostics or something like this?

@eeckstein
Copy link
Contributor Author

We will soon be requiring swift compiler sources to be in the build. Unfortunately this is not possible right now on Windows because of a miscompile. Hopefully this will be resolved soon. So the situation on Windows is not ideal right now, but hopefully not for long.

@asl
Copy link
Contributor

asl commented Feb 9, 2024

We will soon be requiring swift compiler sources to be in the build. Unfortunately this is not possible right now on Windows because of a miscompile. Hopefully this will be resolved soon. So the situation on Windows is not ideal right now, but hopefully not for long.

Ok, is there some progress on speeding up the bootstrapping build? Or there will be some other things? Essentially I'm using non-bootstrapped build for development as otherwise the rebuild step after change is quite long as it rebuilds modules as well...

@eeckstein
Copy link
Contributor Author

You should be using hosttools mode for developing. This mode will be the default soon (#70343).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants