Skip to content

[move-function] Break blocks right after the non-undef debug info associated with move only values. #41515

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions include/swift/SILOptimizer/Utils/CFGOptUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,22 @@ SILBasicBlock *splitBasicBlockAndBranch(SILBuilder &builder,
DominanceInfo *domInfo,
SILLoopInfo *loopInfo);

/// A version of splitBasicBlockAndBranch that takes a SILBuilderContext instead
/// of a SILBuilder. We generally are trying to eliminate APIs that take in
/// SILBuilder directly since that can cause weird downstream mistakes around
/// debug info scopes. So this provides a better choice for engineers.
///
/// TODO: Migrate all callers of splitBasicBlockAndBranch to use this entry
/// point.
inline SILBasicBlock *splitBasicBlockAndBranch(SILBuilderContext &builderCtx,
SILInstruction *splitBeforeInst,
DominanceInfo *domInfo,
SILLoopInfo *loopInfo) {
// Make sure we have the right debug scope from split before inst.
SILBuilderWithScope builder(splitBeforeInst, builderCtx);
return splitBasicBlockAndBranch(builder, splitBeforeInst, domInfo, loopInfo);
}

/// Return true if the function has a critical edge, false otherwise.
bool hasCriticalEdges(SILFunction &f, bool onlyNonCondBr);

Expand Down
428 changes: 254 additions & 174 deletions lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp

Large diffs are not rendered by default.

63 changes: 60 additions & 3 deletions lib/SILOptimizer/Mandatory/MoveKillsCopyableValuesChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SILOptimizer/Analysis/ClosureScope.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
#include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h"

using namespace swift;
Expand Down Expand Up @@ -192,8 +196,19 @@ namespace {
struct MoveKillsCopyableValuesChecker {
SILFunction *fn;
CheckerLivenessInfo livenessInfo;
DominanceInfo *dominanceToUpdate;
SILLoopInfo *loopInfoToUpdate;

MoveKillsCopyableValuesChecker(SILFunction *fn)
: fn(fn), livenessInfo(), dominanceToUpdate(nullptr),
loopInfoToUpdate(nullptr) {}

void setDominanceToUpdate(DominanceInfo *newDFI) {
dominanceToUpdate = newDFI;
}

void setLoopInfoToUpdate(SILLoopInfo *newLFI) { loopInfoToUpdate = newLFI; }

MoveKillsCopyableValuesChecker(SILFunction *fn) : fn(fn) {}
bool check();

void emitDiagnosticForMove(SILValue borrowedValue,
Expand Down Expand Up @@ -357,6 +372,16 @@ bool MoveKillsCopyableValuesChecker::check() {
auto valuesToProcess =
llvm::makeArrayRef(valuesToCheck.begin(), valuesToCheck.end());
auto &mod = fn->getModule();

// If we do not emit any diagnostics, we need to put in a break after each dbg
// info carrying inst for a lexical value that we find a move on. This ensures
// that we avoid a behavior today in SelectionDAG that causes dbg info addr to
// be always sunk to the end of a block.
//
// TODO: We should add llvm.dbg.addr support for fastisel and also teach
// CodeGen how to handle llvm.dbg.addr better.
SmallVector<SILInstruction *, 8> successMovesDbgInfoCarryingInsts;
bool emittedDiagnostic = false;
while (!valuesToProcess.empty()) {
auto lexicalValue = valuesToProcess.front();
valuesToProcess = valuesToProcess.drop_front(1);
Expand Down Expand Up @@ -392,9 +417,11 @@ bool MoveKillsCopyableValuesChecker::check() {
if (livenessInfo.liveness.isWithinBoundary(mvi)) {
LLVM_DEBUG(llvm::dbgs() << " WithinBoundary: Yes!\n");
emitDiagnosticForMove(lexicalValue, varName, mvi);
emittedDiagnostic = true;
} else {
LLVM_DEBUG(llvm::dbgs() << " WithinBoundary: No!\n");
if (auto varInfo = dbgVarInfo.getVarInfo()) {
successMovesDbgInfoCarryingInsts.push_back(*dbgVarInfo);
auto *next = mvi->getNextInstruction();
SILBuilderWithScope builder(next);
// Use an autogenerated location to ensure that if we are next to a
Expand All @@ -416,6 +443,24 @@ bool MoveKillsCopyableValuesChecker::check() {
}
}

// If we emitted any diagnostics, we are going to fail and thus don't need to
// use any compile time to break blocks since a user will never debug such
// programs.
if (emittedDiagnostic)
return false;

// Ok! Now break before the instruction after our debug info generating inst
// so that the SelectionDAG behavior mentioned above on the declaration of
// successMovesDbgInfoCarryingInst.
if (!successMovesDbgInfoCarryingInsts.empty()) {
SILBuilderContext ctx(mod);
do {
auto *next = successMovesDbgInfoCarryingInsts.pop_back_val();
splitBasicBlockAndBranch(ctx, next->getNextInstruction(),
dominanceToUpdate, loopInfoToUpdate);
} while (!successMovesDbgInfoCarryingInsts.empty());
}

return false;
}

Expand Down Expand Up @@ -450,8 +495,20 @@ class MoveKillsCopyableValuesCheckerPass : public SILFunctionTransform {

MoveKillsCopyableValuesChecker checker(getFunction());

if (MoveKillsCopyableValuesChecker(getFunction()).check()) {
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
// If we already had dominance or loop info generated, update them when
// splitting blocks.
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
if (dominanceAnalysis->hasFunctionInfo(fn))
checker.setDominanceToUpdate(dominanceAnalysis->get(fn));
auto *loopAnalysis = getAnalysis<SILLoopAnalysis>();
if (loopAnalysis->hasFunctionInfo(fn))
checker.setLoopInfoToUpdate(loopAnalysis->get(fn));

if (checker.check()) {
AnalysisPreserver preserveDominance(dominanceAnalysis);
AnalysisPreserver preserveLoop(loopAnalysis);
invalidateAnalysis(
SILAnalysis::InvalidationKind::BranchesAndInstructions);
}

// Now search through our function one last time and any move_value
Expand Down
151 changes: 151 additions & 0 deletions test/DebugInfo/move_function_dbginfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public protocol P {
func doSomething()
}

public var trueValue: Bool { true }
public var falseValue: Bool { false }

///////////
// Tests //
///////////
Expand All @@ -40,12 +43,14 @@ public protocol P {
//
// We should have a llvm.dbg.addr for k since we moved it.
// CHECK: call void @llvm.dbg.addr(metadata {{.*}}** %k.debug, metadata ![[K_COPYABLE_VALUE_METADATA:[0-9]*]], metadata !DIExpression()), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br
//
// Our undef should be an llvm.dbg.value. Counter-intuitively this works for
// both llvm.dbg.addr /and/ llvm.dbg.value. Importantly though its metadata
// should be for k since that is the variable that we are telling the debugger
// is no longer defined.
// CHECK: call void @llvm.dbg.value(metadata %T21move_function_dbginfo5KlassC* undef, metadata ![[K_COPYABLE_VALUE_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NOT: br label
//
// CHECK: ret void
// CHECK-NEXT: }
Expand Down Expand Up @@ -83,9 +88,11 @@ public func copyableValueTest() {
// CHECK-LABEL: define swiftcc void @"$s21move_function_dbginfo15copyableVarTestyyF"()
// CHECK: call void @llvm.dbg.declare(metadata %T21move_function_dbginfo5KlassC** %m.debug,
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo5KlassC** %k, metadata ![[K_COPYABLE_VAR_METADATA:[0-9]+]], metadata !DIExpression()), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br
// CHECK: call void @llvm.dbg.value(metadata %T21move_function_dbginfo5KlassC** undef, metadata ![[K_COPYABLE_VAR_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// TODO: Should this be a deref like the original?
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo5KlassC** %k, metadata ![[K_COPYABLE_VAR_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NEXT: br
// CHECK: ret void
// CHECK-NEXT: }
//
Expand Down Expand Up @@ -127,6 +134,7 @@ public func copyableVarTest() {
// CHECK: @llvm.dbg.declare(metadata %swift.opaque** %x.debug,
// CHECK: @llvm.dbg.declare(metadata i8** %m.debug,
// CHECK: @llvm.dbg.addr(metadata i8** %k.debug, metadata ![[K_ADDR_LET_METADATA:[0-9]+]], metadata !DIExpression(DW_OP_deref)), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br
// CHECK: @llvm.dbg.value(metadata %swift.opaque* undef, metadata ![[K_ADDR_LET_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK: ret void
// CHECK-NEXT: }
Expand Down Expand Up @@ -177,8 +185,10 @@ public func addressOnlyValueTest<T : P>(_ x: T) {
// CHECK: @llvm.dbg.declare(metadata %swift.opaque** %x.debug,
// CHECK: @llvm.dbg.declare(metadata i8** %m.debug,
// CHECK: @llvm.dbg.addr(metadata i8** %k.debug, metadata ![[K_ADDRONLY_VAR_METADATA:[0-9]+]], metadata !DIExpression(DW_OP_deref)), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br
// CHECK: @llvm.dbg.value(metadata %swift.opaque* undef, metadata ![[K_ADDRONLY_VAR_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK: @llvm.dbg.addr(metadata i8** %k.debug, metadata ![[K_ADDRONLY_VAR_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NEXT: br
// CHECK: ret void
// CHECK-NEXT: }
//
Expand Down Expand Up @@ -226,6 +236,143 @@ public func addressOnlyVarTest<T : P>(_ x: T) {
k.doSomething()
}

///////////////////////
// Conditional Tests //
///////////////////////

// CHECK-LABEL: define swiftcc void @"$s21move_function_dbginfo037copyableVarTestCCFlowReinitOutOfBlockF0yyF"(
// CHECK: call void @llvm.dbg.declare(metadata %T21move_function_dbginfo5KlassC** %m.debug,
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo5KlassC** %k, metadata ![[K_COPYABLE_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA:[0-9]+]], metadata !DIExpression()), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br label %[[BB_NEXT:[0-9]+]],
//
// CHECK: [[BB_NEXT]]:
// CHECK: br i1 %{{[0-9]+}}, label %[[LHS:[0-9]+]], label %[[RHS:[0-9]+]],
//
// CHECK: [[LHS]]:
// CHECK: call void @llvm.dbg.value(metadata %T21move_function_dbginfo5KlassC** undef, metadata ![[K_COPYABLE_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK: br label %[[CONT_BB:[0-9]+]],
//
// CHECK: [[RHS]]:
// CHECK: br label %[[CONT_BB]],
//
// CHECK: [[CONT_BB]]:
// TODO: Should this be a deref like the original?
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo5KlassC** %k, metadata ![[K_COPYABLE_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NEXT: br
// CHECK: ret void
// CHECK-NEXT: }
public func copyableVarTestCCFlowReinitOutOfBlockTest() {
var k = Klass()
k.doSomething()
if trueValue {
let m = _move(k)
m.doSomething()
}
k = Klass()
k.doSomething()
}

// CHECK-LABEL: define swiftcc void @"$s21move_function_dbginfo034copyableVarTestCCFlowReinitInBlockF0yyF"(
// CHECK: entry:
// CHECK: call void @llvm.dbg.declare(metadata %T21move_function_dbginfo5KlassC** %m.debug,
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo5KlassC** %k, metadata ![[K_COPYABLE_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA:[0-9]+]], metadata !DIExpression()), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br label %[[BB_NEXT:[0-9]+]],
//
// CHECK: [[BB_NEXT]]:
// CHECK: br i1 %{{[0-9]+}}, label %[[LHS:[0-9]+]], label %[[RHS:[0-9]+]],
//
// CHECK: [[LHS]]:
// CHECK: call void @llvm.dbg.value(metadata %T21move_function_dbginfo5KlassC** undef, metadata ![[K_COPYABLE_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// TODO: Should this be a deref like the original?
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo5KlassC** %k, metadata ![[K_COPYABLE_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NEXT: br label %[[BB_NEXT_2:[0-9]+]],
//
// CHECK: [[BB_NEXT_2]]:
// CHECK: br label %[[CONT_BB:[0-9]+]],
//
// CHECK: [[RHS]]:
// CHECK: br label %[[CONT_BB]],
//
// CHECK: [[CONT_BB]]:
// CHECK: ret void
// CHECK-NEXT: }
public func copyableVarTestCCFlowReinitInBlockTest() {
var k = Klass()
k.doSomething()
if trueValue {
let m = _move(k)
m.doSomething()
k = Klass()
}
k.doSomething()
}

// CHECK-LABEL: define swiftcc void @"$s21move_function_dbginfo040addressOnlyVarTestCCFlowReinitOutOfBlockG0yyxmAA1PRzlF"(
// CHECK: entry:
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo1PP* %k, metadata ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA:[0-9]+]], metadata !DIExpression()), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br label %[[BB_NEXT:[0-9]+]],
//
// CHECK: [[BB_NEXT]]:
// CHECK: br i1 %{{[0-9]+}}, label %[[LHS:[0-9]+]], label %[[RHS:[0-9]+]],
//
// CHECK: [[LHS]]:
// CHECK: call void @llvm.dbg.value(metadata %T21move_function_dbginfo1PP* undef, metadata ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK: br label %[[CONT_BB:[0-9]+]],
//
// CHECK: [[RHS]]:
// CHECK: br label %[[CONT_BB]],
//
// CHECK: [[CONT_BB]]:
// TODO: Should this be a deref like the original?
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo1PP* %k, metadata ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NEXT: br
// CHECK: ret void
// CHECK-NEXT: }
public func addressOnlyVarTestCCFlowReinitOutOfBlockTest<T : P>(_ x: T.Type) {
var k = T.value
k.doSomething()
if trueValue {
let m = _move(k)
m.doSomething()
}
k = T.value
k.doSomething()
}

// CHECK-LABEL: define swiftcc void @"$s21move_function_dbginfo037addressOnlyVarTestCCFlowReinitInBlockG0yyxmAA1PRzlF"(
// CHECK: entry:
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo1PP* %k, metadata ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA:[0-9]+]], metadata !DIExpression()), !dbg ![[ADDR_LOC:[0-9]*]]
// CHECK-NEXT: br label %[[BB_NEXT:[0-9]+]],
//
// CHECK: [[BB_NEXT]]:
// CHECK: br i1 %{{[0-9]+}}, label %[[LHS:[0-9]+]], label %[[RHS:[0-9]+]],
//
// CHECK: [[LHS]]:
// CHECK: call void @llvm.dbg.value(metadata %T21move_function_dbginfo1PP* undef, metadata ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// TODO: Should this be a deref like the original?
// CHECK: call void @llvm.dbg.addr(metadata %T21move_function_dbginfo1PP* %k, metadata ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA]], metadata !DIExpression()), !dbg ![[ADDR_LOC]]
// CHECK-NEXT: br label %[[BB_NEXT_2:[0-9]+]],
//
// CHECK: [[BB_NEXT_2]]:
// CHECK: br label %[[CONT_BB:[0-9]+]],
//
// CHECK: [[RHS]]:
// CHECK: br label %[[CONT_BB]],
//
// CHECK: [[CONT_BB]]:
// CHECK: ret void
// CHECK-NEXT: }
public func addressOnlyVarTestCCFlowReinitInBlockTest<T : P>(_ x: T.Type) {
var k = T.value
k.doSomething()
if trueValue {
let m = _move(k)
m.doSomething()
k = T.value
}
k.doSomething()
}

//////////////////////////
// Late Metadata Checks //
//////////////////////////
Expand All @@ -234,3 +381,7 @@ public func addressOnlyVarTest<T : P>(_ x: T) {
// CHECK-DAG: ![[K_COPYABLE_VAR_METADATA]] = !DILocalVariable(name: "k",
// CHECK-DAG: ![[K_ADDR_LET_METADATA]] = !DILocalVariable(name: "k",
// CHECK-DAG: ![[K_ADDRONLY_VAR_METADATA]] = !DILocalVariable(name: "k",
// CHECK-DAG: ![[K_COPYABLE_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA]] = !DILocalVariable(name: "k",
// CHECK-DAG: ![[K_COPYABLE_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA]] = !DILocalVariable(name: "k",
// CHECK-DAG: ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_OUT_BLOCK_METADATA]] = !DILocalVariable(name: "k",
// CHECK-DAG: ![[K_ADDRESSONLY_VAR_CCFLOW_REINIT_IN_BLOCK_METADATA]] = !DILocalVariable(name: "k",
9 changes: 7 additions & 2 deletions test/SILGen/moveonly_builtin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ class Klass {}

// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyAA5KlassCADnF : $@convention(thin) (@owned Klass) -> @owned Klass {
// CHECK-SIL: bb0([[ARG:%.*]] :
// CHECK-SIL-NEXT: debug_value
// CHECK-SIL-NEXT: debug_value [moved]
// CHECK-SIL-NEXT: br bb1
//
// CHECK-SIL: bb1
// CHECK-SIL-NEXT: strong_retain
// CHECK-SIL-NEXT: move_value
// CHECK-SIL-NEXT: debug_value [moved] undef
Expand Down Expand Up @@ -62,7 +65,9 @@ func useMove(_ k: __owned Klass) -> Klass {

// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyxxnRlzClF : $@convention(thin) <T where T : AnyObject> (@owned T) -> @owned T {
// CHECK-SIL: bb0([[ARG:%.*]] :
// CHECK-SIL-NEXT: debug_value
// CHECK-SIL-NEXT: debug_value [moved]
// CHECK-SIL-NEXT: br bb1
// CHECK-SIL: bb1:
// CHECK-SIL-NEXT: strong_retain
// CHECK-SIL-NEXT: move_value
// CHECK-SIL-NEXT: debug_value [moved] undef
Expand Down
8 changes: 5 additions & 3 deletions test/SILOptimizer/move_function_kills_addresses_dbginfo.sil
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Builtin

// CHECK-LABEL: sil [ossa] @singleBlock : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: [[SRC_ADDR:%.*]] = alloc_stack [lexical] [moved] $Builtin.NativeObject, let, name "[[VAR_NAME:.*]]"
// CHECK-NEXT: br
// CHECK: [[DEST_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK: copy_addr [take] [[SRC_ADDR]] to [initialization] [[DEST_ADDR]]
// CHECK-NEXT: debug_value [moved] undef
Expand Down Expand Up @@ -38,13 +39,14 @@ bb0(%0 : @owned $Builtin.NativeObject):
// CHECK-LABEL: sil [ossa] @multipleBlock : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0(
// CHECK: [[SRC_ADDR:%.*]] = alloc_stack [lexical] [moved] $Builtin.NativeObject, let, name "[[VAR_NAME:.*]]"
// CHECK-NEXT: br
// CHECK: [[DEST_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK: cond_br undef, bb1, bb2
// CHECK: cond_br undef, [[LHS:bb[0-9]+]], [[RHS:bb[0-9]+]]
//
// CHECK: bb1:
// CHECK: [[LHS]]:
// CHECK: copy_addr [take] [[SRC_ADDR]] to [initialization] [[DEST_ADDR]]
// CHECK: debug_value [moved] undef : $*Builtin.NativeObject, let, name "[[VAR_NAME]]"
// CHECK: br bb3
// CHECK: br bb
//
// CHECK: } // end sil function 'multipleBlock'
sil [ossa] @multipleBlock : $@convention(thin) (@owned Builtin.NativeObject) -> () {
Expand Down
Loading