Merge pull request #8432 from eeckstein/box2stack_v2
diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp
index 7817786..a1b9089 100644
--- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp
+++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp
@@ -19,6 +19,7 @@
#include "swift/SIL/SILCloner.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/Local.h"
+#include "swift/SILOptimizer/Utils/StackNesting.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
@@ -395,8 +396,7 @@
/// rewriteAllocBoxAsAllocStack - Replace uses of the alloc_box with a
/// new alloc_stack, but do not delete the alloc_box yet.
-static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI,
- llvm::SmallVectorImpl<TermInst *> &Returns) {
+static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
DEBUG(llvm::dbgs() << "*** Promoting alloc_box to stack: " << *ABI);
llvm::SmallVector<SILInstruction*, 4> FinalReleases;
@@ -405,8 +405,7 @@
// Promote this alloc_box to an alloc_stack. Insert the alloc_stack
// at the beginning of the function.
- auto &Entry = ABI->getFunction()->front();
- SILBuilder BuildAlloc(&Entry, Entry.begin());
+ SILBuilder BuildAlloc(ABI);
BuildAlloc.setCurrentDebugScope(ABI->getDebugScope());
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
&& "rewriting multi-field box not implemented");
@@ -440,25 +439,18 @@
.getTypeLowering(ABI->getBoxType()->getFieldType(ABI->getModule(), 0));
auto Loc = CleanupLocation::get(ABI->getLoc());
- // For non-trivial types, insert destroys for each final release-like
- // instruction we found that isn't an explicit dealloc_box.
- if (!Lowering.isTrivial()) {
- for (auto LastRelease : FinalReleases) {
- if (isa<DeallocBoxInst>(LastRelease))
- continue;
-
- SILBuilderWithScope BuildDestroy(LastRelease);
- BuildDestroy.emitDestroyAddrAndFold(Loc, PointerResult);
+ for (auto LastRelease : FinalReleases) {
+ SILBuilderWithScope Builder(LastRelease);
+ if (!isa<DeallocBoxInst>(LastRelease)&& !Lowering.isTrivial()) {
+ // For non-trivial types, insert destroys for each final release-like
+ // instruction we found that isn't an explicit dealloc_box.
+ Builder.emitDestroyAddrAndFold(Loc, PointerResult);
}
+ Builder.createDeallocStack(Loc, ASI);
}
- for (auto Return : Returns) {
- SILBuilderWithScope BuildDealloc(Return);
- BuildDealloc.createDeallocStack(Loc, ASI);
- }
-
- // Remove any retain and release instructions. Since all uses of result #1
- // are gone, this only walks through uses of result #0 (the retain count
+ // Remove any retain and release instructions. Since all uses of project_box
+ // are gone, this only walks through uses of the box itself (the retain count
// pointer).
while (!ABI->use_empty()) {
auto *User = (*ABI->use_begin())->getUser();
@@ -825,7 +817,6 @@
static unsigned
rewritePromotedBoxes(llvm::SmallVectorImpl<AllocBoxInst *> &Promoted,
llvm::SmallVectorImpl<Operand *> &PromotedOperands,
- llvm::SmallVectorImpl<TermInst *> &Returns,
bool &CFGChanged) {
// First we'll rewrite any partial applies that we can to remove the
// box container pointer from the operands.
@@ -835,7 +826,7 @@
auto rend = Promoted.rend();
for (auto I = Promoted.rbegin(); I != rend; ++I) {
auto *ABI = *I;
- if (rewriteAllocBoxAsAllocStack(ABI, Returns)) {
+ if (rewriteAllocBoxAsAllocStack(ABI)) {
++Count;
ABI->eraseFromParent();
}
@@ -849,13 +840,8 @@
void run() override {
llvm::SmallVector<AllocBoxInst *, 8> Promotable;
llvm::SmallVector<Operand *, 8> PromotedOperands;
- llvm::SmallVector<TermInst *, 8> Returns;
for (auto &BB : *getFunction()) {
- auto *Term = BB.getTerminator();
- if (Term->isFunctionExiting())
- Returns.push_back(Term);
-
for (auto &I : BB)
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
if (canPromoteAllocBox(ABI, PromotedOperands))
@@ -864,9 +850,14 @@
if (!Promotable.empty()) {
bool CFGChanged = false;
- auto Count = rewritePromotedBoxes(Promotable, PromotedOperands, Returns,
+ auto Count = rewritePromotedBoxes(Promotable, PromotedOperands,
CFGChanged);
NumStackPromoted += Count;
+ if (Count) {
+ StackNesting SN;
+ if (SN.correctStackNesting(getFunction()) == StackNesting::Changes::CFG)
+ CFGChanged = true;
+ }
invalidateAnalysis(CFGChanged ?
SILAnalysis::InvalidationKind::FunctionBody :
diff --git a/lib/SILOptimizer/Utils/StackNesting.cpp b/lib/SILOptimizer/Utils/StackNesting.cpp
index f6f6e1a..f333e9f 100644
--- a/lib/SILOptimizer/Utils/StackNesting.cpp
+++ b/lib/SILOptimizer/Utils/StackNesting.cpp
@@ -119,7 +119,8 @@
// More locations are alive around the StackInst's location.
// Update the AlivaLocs bitset, which contains all those alive
// locations.
- assert(Bits.test(BitNr) && "no dealloc found for alloc stack");
+ assert((Bits.test(BitNr) || (!BI.ExitReachable && !Bits.any()))
+ && "no dealloc found for alloc stack");
StackLocs[BitNr].AliveLocs = Bits;
changed = true;
isNested = true;
diff --git a/test/DebugInfo/ProtocolContainer.swift b/test/DebugInfo/ProtocolContainer.swift
index c591db4..8a8261d 100644
--- a/test/DebugInfo/ProtocolContainer.swift
+++ b/test/DebugInfo/ProtocolContainer.swift
@@ -13,7 +13,7 @@
}
// CHECK: define hidden {{.*}}void @_T017ProtocolContainer3foo{{[_0-9a-zA-Z]*}}F
// CHECK-NEXT: entry:
-// CHECK-NEXT: %[[X:.*]] = alloca %T17ProtocolContainer9AProtocolP, align {{(4|8)}}
+// CHECK: %[[X:.*]] = alloca %T17ProtocolContainer9AProtocolP, align {{(4|8)}}
// CHECK: call void @llvm.dbg.declare(metadata %T17ProtocolContainer9AProtocolP* %[[X]], metadata ![[XMD:.*]], metadata !{{[0-9]+}})
// CHECK-NOT: !DILocalVariable({{.*}} name: "x"
// CHECK-NOT: !DILocalVariable({{.*}} name: "x"
diff --git a/test/DebugInfo/letstring.swift b/test/DebugInfo/letstring.swift
index d5b2f0f..bd07910 100644
--- a/test/DebugInfo/letstring.swift
+++ b/test/DebugInfo/letstring.swift
@@ -7,15 +7,16 @@
// CHECK: define hidden {{.*}}i1 {{.*}}11AppDelegateC1f
func f() -> Bool {
// Test for -O0 shadow copies.
- // CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[B:.*]], metadata !{{[0-9]+}})
- // CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[B]], metadata !{{[0-9]+}})
// CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[SELF:.*]], metadata !{{[0-9]+}})
- let a = "let"
- // CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[SELF]], metadata !{{[0-9]+}})
+ // CHECK-NOT: call void @llvm.dbg.value
// CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[A:.*]], metadata !{{[0-9]+}})
- // CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[A]], metadata !{{[0-9]+}})
- // CHECK-DAG: ![[A]] = !DILocalVariable(name: "a",{{.*}} line: [[@LINE-4]],
+ let a = "let"
+ // CHECK-NOT: call void @llvm.dbg.value
+ // CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[B:.*]], metadata !{{[0-9]+}})
+ // CHECK-NOT: call void @llvm.dbg.value
+ // CHECK: ret
// CHECK-DAG: ![[SELF]] = !DILocalVariable(name: "self", arg: 1{{.*}} line: [[@LINE-10]],
+ // CHECK-DAG: ![[A]] = !DILocalVariable(name: "a",{{.*}} line: [[@LINE-6]],
// CHECK-DAG: ![[B]] = !DILocalVariable(name: "b",{{.*}} line: [[@LINE+1]],
var b = "var"
self.window = UIWindow()
@@ -32,7 +33,7 @@
// DWARF-CHECK: DW_AT_name {{.*}} "self"
//
// DWARF-CHECK: DW_TAG_variable
-// DWARF-CHECK: DW_AT_name {{.*}} "b"
+// DWARF-CHECK: DW_AT_name {{.*}} "a"
//
// DWARF-CHECK: DW_TAG_variable
-// DWARF-CHECK: DW_AT_name {{.*}} "a"
+// DWARF-CHECK: DW_AT_name {{.*}} "b"
diff --git a/test/DebugInfo/linetable-cleanups.swift b/test/DebugInfo/linetable-cleanups.swift
index caa9b8a..9183355 100644
--- a/test/DebugInfo/linetable-cleanups.swift
+++ b/test/DebugInfo/linetable-cleanups.swift
@@ -1,5 +1,8 @@
// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s
+// TODO: check why this is failing on linux
+// REQUIRES: OS=macosx
+
func markUsed<T>(_ t: T) {}
class Person {
@@ -21,12 +24,10 @@
// CHECK: call void {{.*[rR]}}elease{{.*}} {{#[0-9]+}}, !dbg ![[LOOPHEADER_LOC]]
// CHECK: call {{.*}}void @_T04main8markUsedyxlF
// The cleanups should share the line number with the ret stmt.
-// CHECK: call void {{.*[rR]}}elease{{.*}} {{#[0-9]+}}, !dbg ![[CLEANUPS:.*]]
+// CHECK: call void @swift_bridgeObjectRelease({{.*}}) {{#[0-9]+}}, !dbg ![[CLEANUPS:.*]]
// CHECK-NEXT: !dbg ![[CLEANUPS]]
-// CHECK-NEXT: bitcast
// CHECK-NEXT: llvm.lifetime.end
// CHECK-NEXT: bitcast
-// CHECK-NEXT: llvm.lifetime.end
// CHECK-NEXT: bitcast
// CHECK-NEXT: llvm.lifetime.end
// CHECK-NEXT: ret void, !dbg ![[CLEANUPS]]
diff --git a/test/DebugInfo/linetable.swift b/test/DebugInfo/linetable.swift
index 33b5201..f9ba1c2 100644
--- a/test/DebugInfo/linetable.swift
+++ b/test/DebugInfo/linetable.swift
@@ -34,9 +34,9 @@
var result = my_class.do_something(x)
markUsed(result)
// CHECK: call {{.*}} @swift_rt_swift_release {{.*}}
+// CHECK: bitcast
+// CHECK: llvm.lifetime.end
// CHECK: call {{.*}} @swift_rt_swift_release {{.*}}, !dbg ![[CLOSURE_END:.*]]
-// CHECK-NEXT: bitcast
-// CHECK-NEXT: llvm.lifetime.end
// CHECK-NEXT: ret void, !dbg ![[CLOSURE_END]]
// CHECK: ![[CLOSURE_END]] = !DILocation(line: [[@LINE+1]],
}
diff --git a/test/DebugInfo/protocolarg.swift b/test/DebugInfo/protocolarg.swift
index 80ea033..c7ed868 100644
--- a/test/DebugInfo/protocolarg.swift
+++ b/test/DebugInfo/protocolarg.swift
@@ -8,21 +8,21 @@
}
// CHECK: define {{.*}}@_T011protocolarg16printSomeNumbersyAA12IGiveOutInts_pF
-// CHECK: @llvm.dbg.declare(metadata %T11protocolarg12IGiveOutIntsP* %
-// CHECK-SAME: metadata ![[VAR:.*]], metadata ![[EMPTY:.*]])
// CHECK: @llvm.dbg.declare(metadata %T11protocolarg12IGiveOutIntsP** %
// CHECK-SAME: metadata ![[ARG:.*]], metadata ![[DEREF:.*]])
+// CHECK: @llvm.dbg.declare(metadata %T11protocolarg12IGiveOutIntsP* %
+// CHECK-SAME: metadata ![[VAR:.*]], metadata ![[EMPTY:.*]])
// CHECK: ![[EMPTY]] = !DIExpression()
public func printSomeNumbers(_ gen: IGiveOutInts) {
var gen = gen
- // CHECK: ![[VAR]] = !DILocalVariable(name: "gen", {{.*}} line: [[@LINE-1]]
// FIXME: Should be DW_TAG_interface_type
- // CHECK: ![[PT:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "IGiveOutInts"
// CHECK: ![[ARG]] = !DILocalVariable(name: "gen", arg: 1,
- // CHECK-SAME: line: [[@LINE-6]], type: ![[PT]]
+ // CHECK-SAME: line: [[@LINE-4]], type: ![[PT:[0-9]+]]
+ // CHECK: ![[PT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "IGiveOutInts"
// CHECK: ![[DEREF]] = !DIExpression(DW_OP_deref)
+ // CHECK: ![[VAR]] = !DILocalVariable(name: "gen", {{.*}} line: [[@LINE-6]]
markUsed(gen.callMe())
use(&gen)
}
diff --git a/test/IRGen/dynamic_lookup.sil b/test/IRGen/dynamic_lookup.sil
index 3390828..bcfa4d8 100644
--- a/test/IRGen/dynamic_lookup.sil
+++ b/test/IRGen/dynamic_lookup.sil
@@ -76,6 +76,8 @@
br bb3
bb3:
+ strong_release %1 : $<τ_0_0> { var τ_0_0 } <AnyObject>
+ strong_release %3 : $<τ_0_0> { var τ_0_0 } <Optional<() -> ()>>
%43 = tuple ()
return %43 : $()
}
@@ -119,6 +121,7 @@
br bb3
bb3:
+ strong_release %1 : $<τ_0_0> { var τ_0_0 } <AnyObject>
%43 = tuple ()
return %43 : $()
}
@@ -153,6 +156,8 @@
br bb3
bb3:
+ strong_release %3 : $<τ_0_0> { var τ_0_0 } <Int>
+ strong_release %2 : $<τ_0_0> { var τ_0_0 } <AnyObject>
%30 = tuple ()
return %30 : $()
}
diff --git a/test/SILOptimizer/allocbox_to_stack.sil b/test/SILOptimizer/allocbox_to_stack.sil
index 1a72426..1e05a7d 100644
--- a/test/SILOptimizer/allocbox_to_stack.sil
+++ b/test/SILOptimizer/allocbox_to_stack.sil
@@ -243,8 +243,8 @@
strong_release %1 : ${ var SomeUnion }
%10 = tuple ()
return %10 : $()
-// CHECK: [[T0:%.*]] = tuple ()
// CHECK: dealloc_stack [[UNION]]
+// CHECK: [[T0:%.*]] = tuple ()
// CHECK-NEXT: return [[T0]] : $()
}
@@ -276,24 +276,27 @@
//
// CHECK-LABEL: sil @box_reachable_from_release_test
sil @box_reachable_from_release_test : $@convention(thin) () -> () {
-// CHECK: bb0:
-// CHECK: alloc_stack
bb0:
br bb1
+// CHECK: bb1:
+// CHECK-NEXT: alloc_stack
bb1:
%1 = alloc_box ${ var Bool }
cond_br undef, bb2, bb3
+// CHECK: bb2:
+// CHECK-NEXT: dealloc_stack
bb2:
strong_release %1 : ${ var Bool }
br bb1
+// CHECK: bb3:
+// CHECK-NEXT: dealloc_stack
bb3:
strong_release %1 : ${ var Bool }
%2 = tuple ()
-// CHECK: dealloc_stack
-// CHECK-NEXT: return
+// CHECK: return
return %2 : $()
}
@@ -334,18 +337,19 @@
strong_release %2 : ${ var SomeClass }
// CHECK: destroy_addr [[MUI]]
+ // CHECK-NEXT: dealloc_stack [[STACK]]
br bb2
// CHECK: bb2
bb2:
%17 = tuple ()
- // CHECK: dealloc_stack [[STACK]]
- // CHECK-NEXT: return
+ // CHECK: return
return %17 : $()
bb3:
strong_release %2 : ${ var SomeClass }
// CHECK: destroy_addr [[MUI]]
+ // CHECK-NEXT: dealloc_stack [[STACK]]
br bb2
}
@@ -486,13 +490,13 @@
sil @callWithAutoclosure : $@convention(thin) <T where T : P> (@in T) -> () {
// CHECK: bb0
bb0(%0 : $*T):
- // CHECK: [[STACK:%[0-9a-zA-Z_]+]] = alloc_stack $T
// CHECK: debug_value_addr
debug_value_addr %0 : $*T
// CHECK: function_ref @mightApply
%2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
%3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
// CHECK-NOT: alloc_box
+ // CHECK: [[STACK:%[0-9a-zA-Z_]+]] = alloc_stack $T
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>
%4a = project_box %4 : $<τ_0_0> { var τ_0_0 } <T>, 0
// CHECK: copy_addr %0 to [initialization] [[STACK]] : $*T
@@ -502,10 +506,10 @@
%6 = partial_apply %3<T>(%4) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
%7 = apply %2<T>(%6) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
// CHECK: destroy_addr [[STACK]] : $*T
+ // CHECK: dealloc_stack [[STACK]] : $*T
destroy_addr %0 : $*T
%9 = tuple ()
- // CHECK: dealloc_stack [[STACK]] : $*T
- // CHECK: return
+ // CHECK: return
return %9 : $()
}
@@ -745,3 +749,284 @@
strong_release %2 : ${ var ThrowDerivedClass }
throw %25 : $Error
}
+
+// CHECK-LABEL: sil @deal_with_wrong_nesting
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK: bb1:
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK: bb2:
+// CHECK: store
+// CHECK: [[STACK2:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: dealloc_stack [[STACK2]]
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK: bb3:
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @deal_with_wrong_nesting : $(Int) -> () {
+bb0(%0 : $Int):
+ %as1 = alloc_stack $Bool
+ %1 = alloc_box ${ var Int }
+ cond_br undef, bb1, bb2
+
+bb1:
+ strong_release %1 : ${ var Int }
+ dealloc_stack %as1 : $*Bool
+ br bb3
+
+bb2:
+ %1a = project_box %1 : ${ var Int }, 0
+ %2 = store %0 to %1a : $*Int
+ dealloc_stack %as1 : $*Bool
+ %3 = load %1a : $*Int
+ %as2 = alloc_stack $Bool
+ strong_release %1 : ${ var Int }
+ dealloc_stack %as2 : $*Bool
+ br bb3
+
+bb3:
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @wrong_nesting_with_alloc_ref
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[REF:%[0-9]+]] = alloc_ref [stack] $SomeClass
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK: store
+// CHECK: dealloc_stack [[BOX]]
+// CHECK-NEXT: dealloc_ref [stack] [[REF]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @wrong_nesting_with_alloc_ref : $(Int) -> () {
+bb0(%0 : $Int):
+ %as1 = alloc_ref [stack] $SomeClass
+ %1 = alloc_box ${ var Int }
+ %1a = project_box %1 : ${ var Int }, 0
+ %2 = store %0 to %1a : $*Int
+ dealloc_ref [stack] %as1 : $SomeClass
+ %3 = load %1a : $*Int
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @nesting_and_unreachable1
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK: bb1:
+// CHECK-NEXT: [[STACK2:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: dealloc_stack [[STACK2]]
+// CHECK-NEXT: unreachable
+// CHECK: bb2:
+// CHECK: store
+// CHECK: dealloc_stack [[BOX]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @nesting_and_unreachable1 : $(Int) -> () {
+bb0(%0 : $Int):
+ %as1 = alloc_stack $Bool
+ %1 = alloc_box ${ var Int }
+ %1a = project_box %1 : ${ var Int }, 0
+ cond_br undef, bb1, bb2
+
+bb1:
+ %as2 = alloc_stack $Bool
+ dealloc_stack %as2 : $*Bool
+ unreachable
+
+bb2:
+ %2 = store %0 to %1a : $*Int
+ dealloc_stack %as1 : $*Bool
+ %3 = load %1a : $*Int
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @nesting_and_unreachable2
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK: bb1:
+// CHECK-NEXT: [[STACK2:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: dealloc_stack [[STACK2]]
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK-NEXT: unreachable
+// CHECK: bb2:
+// CHECK: store
+// CHECK: dealloc_stack [[BOX]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @nesting_and_unreachable2 : $(Int) -> () {
+bb0(%0 : $Int):
+ %as1 = alloc_stack $Bool
+ %1 = alloc_box ${ var Int }
+ %1a = project_box %1 : ${ var Int }, 0
+ cond_br undef, bb1, bb2
+
+bb1:
+ %as2 = alloc_stack $Bool
+ strong_release %1 : ${ var Int }
+ dealloc_stack %as2 : $*Bool
+ unreachable
+
+bb2:
+ %2 = store %0 to %1a : $*Int
+ dealloc_stack %as1 : $*Bool
+ %3 = load %1a : $*Int
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @nesting_and_unreachable3
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK-NEXT: [[STACK:%[0-9]+]] = alloc_stack $Bool
+// CHECK: bb1:
+// CHECK-NEXT: dealloc_stack [[STACK]]
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: unreachable
+// CHECK: bb2:
+// CHECK: store
+// CHECK: dealloc_stack [[STACK]]
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @nesting_and_unreachable3 : $(Int) -> () {
+bb0(%0 : $Int):
+ %1 = alloc_box ${ var Int }
+ %as1 = alloc_stack $Bool
+ %1a = project_box %1 : ${ var Int }, 0
+ cond_br undef, bb1, bb2
+
+bb1:
+ strong_release %1 : ${ var Int }
+ unreachable
+
+bb2:
+ %2 = store %0 to %1a : $*Int
+ %3 = load %1a : $*Int
+ dealloc_stack %as1 : $*Bool
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @nesting_and_unreachable4
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK: bb1:
+// CHECK-NEXT: unreachable
+// CHECK: bb2:
+// CHECK: store
+// CHECK: dealloc_stack [[BOX]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @nesting_and_unreachable4 : $(Int) -> () {
+bb0(%0 : $Int):
+ %1 = alloc_box ${ var Int }
+ %1a = project_box %1 : ${ var Int }, 0
+ cond_br undef, bb1, bb2
+
+bb1:
+ unreachable
+
+bb2:
+ %2 = store %0 to %1a : $*Int
+ %3 = load %1a : $*Int
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @nesting_and_unreachable5
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK: bb1:
+// CHECK-NEXT: {{.*}} = alloc_stack $Bool
+// CHECK-NEXT: {{.*}} = alloc_stack $Bool
+// CHECK-NEXT: unreachable
+// CHECK: bb2:
+// CHECK: store
+// CHECK: dealloc_stack [[BOX]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+sil @nesting_and_unreachable5 : $(Int) -> () {
+bb0(%0 : $Int):
+ %1 = alloc_box ${ var Int }
+ %1a = project_box %1 : ${ var Int }, 0
+ cond_br undef, bb1, bb2
+
+bb1:
+ %as1 = alloc_stack $Bool
+ %as2 = alloc_stack $Bool
+ unreachable
+
+bb2:
+ %2 = store %0 to %1a : $*Int
+ %3 = load %1a : $*Int
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+}
+
+// CHECK-LABEL: sil @nesting_and_unreachable_critical_edge
+// CHECK: bb0(%0 : $Int):
+// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
+// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: cond_br
+// CHECK: bb1:
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK-NEXT: br bb5
+// CHECK: bb2:
+// CHECK-NEXT: [[STACK2:%[0-9]+]] = alloc_stack $Bool
+// CHECK-NEXT: cond_br
+// CHECK: bb3:
+// CHECK-NEXT: dealloc_stack [[STACK2]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK-NEXT: br bb5
+// CHECK: bb4:
+// CHECK: store
+// CHECK: dealloc_stack [[STACK2]]
+// CHECK-NEXT: dealloc_stack [[STACK1]]
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+// CHECK: bb5:
+// CHECK-NEXT: dealloc_stack [[BOX]]
+// CHECK-NEXT: unreachable
+sil @nesting_and_unreachable_critical_edge : $(Int) -> () {
+bb0(%0 : $Int):
+ %1 = alloc_box ${ var Int }
+ %as1 = alloc_stack $Bool
+ %1a = project_box %1 : ${ var Int }, 0
+ cond_br undef, bb1, bb3
+
+bb1:
+ %as2 = alloc_stack $Bool
+ cond_br undef, bb2, bb3
+
+bb2:
+ %2 = store %0 to %1a : $*Int
+ %3 = load %1a : $*Int
+ dealloc_stack %as2 : $*Bool
+ dealloc_stack %as1 : $*Bool
+ strong_release %1 : ${ var Int }
+ %r = tuple ()
+ %5 = return %r : $()
+
+bb3:
+ strong_release %1 : ${ var Int }
+ unreachable
+
+}
+
diff --git a/test/SILOptimizer/definite_init_failable_initializers.swift b/test/SILOptimizer/definite_init_failable_initializers.swift
index ea5c4c8..d5b7e4f 100644
--- a/test/SILOptimizer/definite_init_failable_initializers.swift
+++ b/test/SILOptimizer/definite_init_failable_initializers.swift
@@ -40,10 +40,10 @@
// CHECK: [[SELF_BOX:%.*]] = alloc_stack $FailableStruct
// CHECK: br bb1
// CHECK: bb1:
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[SELF:%.*]] = enum $Optional<FailableStruct>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[SELF]]
init?(failBeforeInitialization: ()) {
return nil
@@ -59,10 +59,10 @@
// CHECK: bb1:
// CHECK-NEXT: [[X_ADDR:%.*]] = struct_element_addr [[SELF_BOX]]
// CHECK-NEXT: strong_release [[CANARY]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[SELF:%.*]] = enum $Optional<FailableStruct>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[SELF]]
init?(failAfterPartialInitialization: ()) {
x = Canary()
@@ -82,10 +82,10 @@
// CHECK: bb1:
// CHECK-NEXT: [[SELF:%.*]] = struct $FailableStruct ([[CANARY1]] : $Canary, [[CANARY2]] : $Canary)
// CHECK-NEXT: release_value [[SELF]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableStruct>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[NEW_SELF]]
init?(failAfterFullInitialization: ()) {
x = Canary()
@@ -101,10 +101,10 @@
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: release_value [[CANARY]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[SELF_VALUE:%.*]] = enum $Optional<FailableStruct>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[SELF_VALUE]]
init?(failAfterWholeObjectInitializationByAssignment: ()) {
self = FailableStruct(noFail: ())
@@ -120,10 +120,10 @@
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: release_value [[NEW_SELF]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableStruct>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[NEW_SELF]]
init?(failAfterWholeObjectInitializationByDelegation: ()) {
self.init(noFail: ())
@@ -146,14 +146,15 @@
// CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]]
// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableStruct>, #Optional.some!enumelt.1, [[SELF_VALUE]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: br [[EPILOG_BB:bb[0-9]+]]([[NEW_SELF]] : $Optional<FailableStruct>)
//
// CHECK: [[FAIL_EPILOG_BB]]:
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableStruct>, #Optional.none!enumelt
// CHECK-NEXT: br [[EPILOG_BB]]([[NEW_SELF]] : $Optional<FailableStruct>)
//
// CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional<FailableStruct>)
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[NEW_SELF]]
// Optional to optional
init?(failDuringDelegation: ()) {
@@ -204,11 +205,11 @@
// CHECK: [[SELF_BOX:%.*]] = alloc_stack $FailableAddrOnlyStruct<T>
// CHECK: br bb1
// CHECK: bb1:
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: inject_enum_addr %0
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK: dealloc_stack [[SELF_BOX]]
-// CHECK-NEXT: return
+// CHECK: return
init?(failBeforeInitialization: ()) {
return nil
}
@@ -227,11 +228,11 @@
// CHECK: bb1:
// CHECK-NEXT: [[X_ADDR:%.*]] = struct_element_addr [[SELF_BOX]]
// CHECK-NEXT: destroy_addr [[X_ADDR]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: inject_enum_addr %0
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK: dealloc_stack [[SELF_BOX]]
-// CHECK-NEXT: return
+// CHECK: return
init?(failAfterPartialInitialization: ()) {
x = T()
return nil
@@ -257,11 +258,11 @@
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: destroy_addr [[SELF_BOX]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: inject_enum_addr %0
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK: dealloc_stack [[SELF_BOX]]
-// CHECK-NEXT: return
+// CHECK: return
init?(failAfterFullInitialization: ()) {
x = T()
y = T()
@@ -527,14 +528,15 @@
// CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]]
// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<ThrowStruct>, #Optional.some!enumelt.1, [[SELF_VALUE]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: br [[EPILOG_BB:bb[0-9]+]]([[NEW_SELF]] : $Optional<ThrowStruct>)
//
// CHECK: [[FAIL_EPILOG_BB]]:
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<ThrowStruct>, #Optional.none!enumelt
// CHECK-NEXT: br [[EPILOG_BB]]([[NEW_SELF]] : $Optional<ThrowStruct>)
//
// CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional<ThrowStruct>):
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[NEW_SELF]] : $Optional<ThrowStruct>
//
// CHECK: [[TRY_APPLY_FAIL_TRAMPOLINE_BB:bb[0-9]+]]:
@@ -746,10 +748,10 @@
// CHECK: bb1:
// CHECK-NEXT: [[METATYPE:%.*]] = value_metatype $@thick FailableBaseClass.Type, %0 : $FailableBaseClass
// CHECK-NEXT: dealloc_partial_ref %0 : $FailableBaseClass, [[METATYPE]] : $@thick FailableBaseClass.Type
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[RESULT:%.*]] = enum $Optional<FailableBaseClass>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[RESULT]]
convenience init?(failBeforeDelegation: ()) {
return nil
@@ -765,10 +767,10 @@
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: strong_release [[NEW_SELF]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[RESULT:%.*]] = enum $Optional<FailableBaseClass>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[RESULT]]
convenience init?(failAfterDelegation: ()) {
self.init(noFail: ())
@@ -792,14 +794,15 @@
// CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]]
// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableBaseClass>, #Optional.some!enumelt.1, [[SELF_VALUE]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: br [[EPILOG_BB:bb[0-9]+]]([[NEW_SELF]] : $Optional<FailableBaseClass>)
//
// CHECK: [[FAIL_TRAMPOLINE_BB]]:
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableBaseClass>, #Optional.none!enumelt
// CHECK-NEXT: br [[EPILOG_BB]]([[NEW_SELF]] : $Optional<FailableBaseClass>)
//
// CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional<FailableBaseClass>):
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[NEW_SELF]]
// Optional to optional
convenience init?(failDuringDelegation: ()) {
@@ -840,10 +843,10 @@
// CHECK: bb1:
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick FailableDerivedClass.Type
// CHECK-NEXT: dealloc_partial_ref %0 : $FailableDerivedClass, [[METATYPE]] : $@thick FailableDerivedClass.Type
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[RESULT:%.*]] = enum $Optional<FailableDerivedClass>, #Optional.none!enumelt
// CHECK-NEXT: br bb2
// CHECK: bb2:
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[RESULT]]
init?(derivedFailBeforeDelegation: ()) {
return nil
@@ -871,14 +874,15 @@
// CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_ref_cast [[BASE_SELF_VALUE]]
// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableDerivedClass>, #Optional.some!enumelt.1, [[SELF_VALUE]]
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: br [[EPILOG_BB:bb[0-9]+]]([[NEW_SELF]] : $Optional<FailableDerivedClass>)
//
// CHECK: [[FAIL_TRAMPOLINE_BB]]:
+// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional<FailableDerivedClass>, #Optional.none!enumelt
// CHECK-NEXT: br [[EPILOG_BB]]([[NEW_SELF]] : $Optional<FailableDerivedClass>)
//
// CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional<FailableDerivedClass>):
-// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: return [[NEW_SELF]] : $Optional<FailableDerivedClass>
init?(derivedFailDuringDelegation: ()) {
self.otherMember = Canary()
diff --git a/test/SILOptimizer/definite_init_protocol_init.swift b/test/SILOptimizer/definite_init_protocol_init.swift
index b109ff3..f24d78b 100644
--- a/test/SILOptimizer/definite_init_protocol_init.swift
+++ b/test/SILOptimizer/definite_init_protocol_init.swift
@@ -100,8 +100,8 @@
// CHECK-NEXT: copy_addr [take] [[SELF_BOX]] to [initialization] [[SELF]]
// CHECK-NEXT: dealloc_stack [[SELF_BOX]]
// CHECK-NEXT: copy_addr [take] [[SELF]] to [initialization] %0
-// CHECK-NEXT: [[RESULT:%.*]] = tuple ()
// CHECK-NEXT: dealloc_stack [[SELF]]
+// CHECK-NEXT: [[RESULT:%.*]] = tuple ()
// CHECK-NEXT: return [[RESULT]]
init(upper: Int) {
self.init(middle: upper)
diff --git a/test/SILOptimizer/sroa_unreferenced_members.swift b/test/SILOptimizer/sroa_unreferenced_members.swift
index cce1ea7..9c7775c 100644
--- a/test/SILOptimizer/sroa_unreferenced_members.swift
+++ b/test/SILOptimizer/sroa_unreferenced_members.swift
@@ -3,7 +3,7 @@
import gizmo
// CHECK: ModifyStruct
-// CHECK: %1 = alloc_stack $Drill
+// CHECK: = alloc_stack $Drill
// CHECK: ret
func ModifyStruct(inDrill : Drill) -> Int32 {
var D : Drill = inDrill