Skip to content

Commit c89df9e

Browse files
committed
Mandatory optimizations: constant fold boolean literals before the DefiniteInitialization 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 } } } ```
1 parent d4730e0 commit c89df9e

21 files changed

+280
-31
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===--- BooleanLiteralFolding.swift ---------------------------------------==//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Constant folds conditional branches with boolean literals as operands.
16+
///
17+
/// ```
18+
/// %1 = integer_literal -1
19+
/// %2 = apply %bool_init(%1) // Bool.init(_builtinBooleanLiteral:)
20+
/// %3 = struct_extract %2, #Bool._value
21+
/// cond_br %3, bb1, bb2
22+
/// ```
23+
/// ->
24+
/// ```
25+
/// ...
26+
/// br bb1
27+
/// ```
28+
///
29+
/// This pass is intended to run before DefiniteInitialization, where mandatory inlining and
30+
/// constant folding didn't run, yet (which would perform this kind of optimization).
31+
///
32+
/// This optimization is required to let DefiniteInitialization handle boolean literals correctly.
33+
/// For example in infinite loops:
34+
///
35+
/// ```
36+
/// init() {
37+
/// while true { // DI need to know that there is no loop exit from this while-statement
38+
/// if some_condition {
39+
/// member_field = init_value
40+
/// break
41+
/// }
42+
/// }
43+
/// }
44+
/// ```
45+
///
46+
let booleanLiteralFolding = FunctionPass(name: "boolean-literal-folding") {
47+
(function: Function, context: FunctionPassContext) in
48+
49+
for block in function.blocks {
50+
if let condBr = block.terminator as? CondBranchInst {
51+
fold(condBranch: condBr, context)
52+
}
53+
}
54+
}
55+
56+
private func fold(condBranch: CondBranchInst, _ context: FunctionPassContext) {
57+
guard let structExtract = condBranch.condition as? StructExtractInst,
58+
let initApply = structExtract.struct as? ApplyInst,
59+
initApply.hasSemanticsAttribute("bool.literal_init"),
60+
initApply.arguments.count == 2,
61+
let literal = initApply.arguments[0] as? IntegerLiteralInst,
62+
let literalValue = literal.value else
63+
{
64+
return
65+
}
66+
67+
let builder = Builder(before: condBranch, context)
68+
builder.createBranch(to: literalValue == 0 ? condBranch.falseBlock : condBranch.trueBlock)
69+
context.erase(instruction: condBranch)
70+
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ swift_compiler_sources(Optimizer
1010
AllocVectorLowering.swift
1111
AssumeSingleThreaded.swift
1212
AsyncDemotion.swift
13+
BooleanLiteralFolding.swift
1314
CleanupDebugSteps.swift
1415
ComputeEscapeEffects.swift
1516
ComputeSideEffects.swift

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private func registerSwiftPasses() {
6767
// Function passes
6868
registerPass(allocVectorLowering, { allocVectorLowering.run($0) })
6969
registerPass(asyncDemotion, { asyncDemotion.run($0) })
70+
registerPass(booleanLiteralFolding, { booleanLiteralFolding.run($0) })
7071
registerPass(letPropertyLowering, { letPropertyLowering.run($0) })
7172
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })
7273
registerPass(computeEscapeEffects, { computeEscapeEffects.run($0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ PASS(BasicInstructionPropertyDumper, "basic-instruction-property-dump",
128128
"Print SIL Instruction MemBehavior and ReleaseBehavior Information")
129129
PASS(BasicCalleePrinter, "basic-callee-printer",
130130
"Print Basic Callee Analysis for Testing")
131+
SWIFT_FUNCTION_PASS(BooleanLiteralFolding, "boolean-literal-folding",
132+
"Constant folds initializers of boolean literals")
131133
PASS(CFGPrinter, "view-cfg",
132134
"View Function CFGs")
133135
PASS(COWArrayOpts, "cowarray-opt",

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
133133

134134
P.addAllocBoxToStack();
135135
P.addNoReturnFolding();
136+
P.addBooleanLiteralFolding();
136137
addDefiniteInitialization(P);
137138

138139
P.addAddressLowering();

test/AutoDiff/SILOptimizer/activity_analysis.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-emit-sil -verify -Xllvm -debug-only=differentiation %s 2>&1 | %FileCheck %s
1+
// TODO: re-enable the boolean-literal-folding pass and fix the test accordingly
2+
// RUN: %target-swift-emit-sil -Xllvm -sil-disable-pass=boolean-literal-folding -verify -Xllvm -debug-only=differentiation %s 2>&1 | %FileCheck %s
23
// REQUIRES: asserts
34

45
import _Differentiation

test/AutoDiff/compiler_crashers_fixed/58660-conflicting-debug-info-inlining.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ struct MyModel: Differentiable {
5555
property2 = localVar
5656

5757
// `false` may instead be any expression that returns a `Bool`.
58-
if false {
58+
// TODO: cannot use literal `false` because it crashes
59+
if 1 == 0 {
5960
localVar = member3
6061
}
6162
}

test/AutoDiff/compiler_crashers_fixed/issue-58123-mutating-functions-with-control-flow.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ struct BatchNorm<Scalar>: Layer { // Crash requires conformance to `Layer`
6262
@differentiable(reverse)
6363
func callAsFunction(_ input: Tensor<Scalar>) -> Tensor<Scalar> {
6464
var offset = self.offset
65-
if true { // Crash requires `if true`
65+
// TODO: cannot use literal `true` because it crashes
66+
if 1 == 1 { // Crash requires `if true`
6667
offset += offset // Using `offset = offset + offset` stops the crash
6768
}
6869
return offset

test/AutoDiff/validation-test/address_only_tangentvector.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ AddressOnlyTangentVectorTests.test("LoadableClassAddressOnlyTangentVector") {
4343
@differentiable(reverse)
4444
func conditional<T: Differentiable>(_ s: LoadableClass<T>) -> T {
4545
var tuple = (s, (s, s))
46-
if false {}
46+
// TODO: cannot use literal `false` because it crashes
47+
if 1 == 0 {}
4748
return tuple.1.0.stored
4849
}
4950
expectEqual(.init(stored: 1), gradient(at: LoadableClass<Float>(10), of: conditional))

test/AutoDiff/validation-test/control_flow.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ ControlFlowTests.test("Conditionals") {
6666

6767
func cond4_var(_ x: Float) -> Float {
6868
var outer = x
69-
outerIf: if true {
69+
// TODO: cannot use literal `true` because it crashes
70+
outerIf: if 1 == 1 {
7071
var inner = outer
7172
inner = inner * x
72-
if false {
73+
// TODO: cannot use literal `false` because it crashes
74+
if 1 == 0 {
7375
break outerIf
7476
}
7577
outer = inner
@@ -386,8 +388,9 @@ ControlFlowTests.test("NestedConditionals") {
386388
@differentiable(reverse, wrt: self) // wrt only self is important
387389
func callAsFunction(_ input: Float) -> Float {
388390
var x = input
389-
if true {
390-
if true {
391+
// TODO: cannot use literal `true` because it crashes
392+
if 1 == 1 {
393+
if 1 == 1 {
391394
// Function application below should make `self` have non-zero
392395
// derivative.
393396
x = x * w
@@ -405,8 +408,9 @@ ControlFlowTests.test("NestedConditionals") {
405408
@differentiable(reverse, wrt: x)
406409
func TF_781(_ x: Float, _ y: Float) -> Float {
407410
var result = y
408-
if true {
409-
if true {
411+
// TODO: cannot use literal `true` because it crashes
412+
if 1 == 1 {
413+
if 1 == 1 {
410414
result = result * x
411415
}
412416
}
@@ -791,7 +795,8 @@ ControlFlowTests.test("ThrowingCalls") {
791795
func testComplexControlFlow(_ x: Float) -> Float {
792796
rethrowing({})
793797
for _ in 0..<Int(x) {
794-
if true {
798+
// TODO: cannot use literal `true` because it crashes
799+
if 1 == 1 {
795800
rethrowing({})
796801
}
797802
rethrowing({}) // non-active `try_apply`
@@ -805,7 +810,8 @@ ControlFlowTests.test("ThrowingCalls") {
805810
func testComplexControlFlowGeneric<T: Differentiable>(_ x: T) -> T {
806811
rethrowing({})
807812
for _ in 0..<10 {
808-
if true {
813+
// TODO: cannot use literal `true` because it crashes
814+
if 1 == 1 {
809815
rethrowing({})
810816
}
811817
rethrowing({}) // non-active `try_apply`

0 commit comments

Comments
 (0)