From 34f0093ca5175ceebb0d992d697a43a15b779c35 Mon Sep 17 00:00:00 2001 From: Andrew Savonichev Date: Thu, 8 Jun 2023 14:47:56 +0900 Subject: [PATCH 1/6] [DebugInfo] Merge fragments from SIL with fragments created in IRGen SIL variables can be split by SILSROA into separate allocations, each having op_fragment expressions in debug_value (VarInfo.DIExpr). These allocations can be further split by IRGen (multiple values in Storage argument). These "nested" fragments refer to the same DI variable, so it is important to merge them for the LLVM IR DI expression. The compiler used to ignore fragment expressions from SIL when IRGen fragments were also present. This led to incorrect DI info generation, and for some cases even triggered assertions in LLVM X86 CodeGen: DwarfExpression.cpp:679: void llvm::DwarfExpression::addFragmentOffset(const llvm::DIExpression *): Assertion `FragmentOffset >= OffsetInBits && "overlapping or duplicate fragments"' failed. The patch fixes issue #64642. The LIT test is a reduced reproducer from that issue. --- lib/IRGen/IRGenDebugInfo.cpp | 104 ++++++++++++++++++-------- test/IRGen/debug_fragment_merge.swift | 40 ++++++++++ 2 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 test/IRGen/debug_fragment_merge.swift diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index d4ce0fbe47bce..10fdd073c5afb 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -215,10 +215,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { /// Return false if we fail to create the right DW_OP_LLVM_fragment operand. bool handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp, - SmallVectorImpl &Operands); + std::pair &Fragment); /// Return false if we fail to create the desired !DIExpression. bool buildDebugInfoExpression(const SILDebugVariable &VarInfo, - SmallVectorImpl &Operands); + SmallVectorImpl &Operands, + std::pair &Fragment); /// Emit a dbg.declare at the current insertion point in Builder. void emitVariableDeclaration(IRBuilder &Builder, @@ -2522,7 +2523,8 @@ void IRGenDebugInfoImpl::emitOutlinedFunction(IRBuilder &Builder, } bool IRGenDebugInfoImpl::handleFragmentDIExpr( - const SILDIExprOperand &CurDIExprOp, SmallVectorImpl &Operands) { + const SILDIExprOperand &CurDIExprOp, + std::pair &Fragment) { assert(CurDIExprOp.getOperator() == SILDIExprOperator::Fragment); // Expecting a VarDecl that points to a field in an struct auto DIExprArgs = CurDIExprOp.args(); @@ -2554,23 +2556,31 @@ bool IRGenDebugInfoImpl::handleFragmentDIExpr( uint64_t OffsetInBits = Offset->getUniqueInteger().getLimitedValue() * SizeOfByte; - // Translate to LLVM dbg intrinsic operands - Operands.push_back(llvm::dwarf::DW_OP_LLVM_fragment); - Operands.push_back(OffsetInBits); - Operands.push_back(SizeInBits); + // Translate to DW_OP_LLVM_fragment operands + Fragment = std::make_pair(OffsetInBits, SizeInBits); return true; } bool IRGenDebugInfoImpl::buildDebugInfoExpression( - const SILDebugVariable &VarInfo, SmallVectorImpl &Operands) { + const SILDebugVariable &VarInfo, SmallVectorImpl &Operands, + std::pair &Fragment) { assert(VarInfo.DIExpr && "SIL debug info expression not found"); +#ifndef NDEBUG + bool HasFragment = VarInfo.DIExpr.hasFragment(); +#endif + const auto &DIExpr = VarInfo.DIExpr; for (const SILDIExprOperand &ExprOperand : DIExpr.operands()) { switch (ExprOperand.getOperator()) { case SILDIExprOperator::Fragment: - if (!handleFragmentDIExpr(ExprOperand, Operands)) + assert(HasFragment && "Fragment must be the last part of a DIExpr"); +#ifndef NDEBUG + // Trigger the assert above if we have more than one fragment expression. + HasFragment = false; +#endif + if (!handleFragmentDIExpr(ExprOperand, Fragment)) return false; break; case SILDIExprOperator::Dereference: @@ -2677,24 +2687,60 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( LocalVarCache.insert({Key, llvm::TrackingMDNodeRef(Var)}); } - auto appendDIExpression = - [&VarInfo, this](llvm::DIExpression *DIExpr) -> llvm::DIExpression * { - if (VarInfo.DIExpr) { - llvm::SmallVector Operands; - if (!buildDebugInfoExpression(VarInfo, Operands)) - return nullptr; - if (Operands.size()) - return llvm::DIExpression::append(DIExpr, Operands); - } - return DIExpr; - }; - // Running variables for the current/previous piece. bool IsPiece = Storage.size() > 1; uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth(); unsigned AlignInBits = SizeOfByte; unsigned OffsetInBits = 0; unsigned SizeInBits = 0; + std::pair Fragment(0, 0); + + auto appendDIExpression = + [&VarInfo, this]( + llvm::DIExpression *DIExpr, + std::pair PieceFragment) -> llvm::DIExpression * { + unsigned PieceFragmentOffset = PieceFragment.first; + unsigned PieceFragmentSize = PieceFragment.second; + + if (!VarInfo.DIExpr) { + if (!PieceFragmentSize) + return DIExpr; + + return llvm::DIExpression::createFragmentExpression( + DIExpr, PieceFragmentOffset, PieceFragmentSize) + .getValueOr(nullptr); + } + + llvm::SmallVector Operands; + std::pair VarFragment(0, 0); + if (!buildDebugInfoExpression(VarInfo, Operands, VarFragment)) + return nullptr; + + if (!Operands.empty()) + DIExpr = llvm::DIExpression::append(DIExpr, Operands); + + unsigned VarFragmentOffset = VarFragment.first; + unsigned VarFragmentSize = VarFragment.second; + + // Add the fragment of the SIL variable. + if (VarFragmentSize) + DIExpr = llvm::DIExpression::createFragmentExpression( + DIExpr, VarFragmentOffset, VarFragmentSize) + .getValueOr(nullptr); + + if (!DIExpr) + return nullptr; + + // When the fragment of the SIL variable is further split into other + // fragments (PieceFragment), merge them into one DW_OP_LLVM_Fragment + // expression. + if (PieceFragmentSize) + return llvm::DIExpression::createFragmentExpression( + DIExpr, PieceFragmentOffset, PieceFragmentSize) + .getValueOr(nullptr); + + return DIExpr; + }; for (llvm::Value *Piece : Storage) { SmallVector Operands; @@ -2723,16 +2769,12 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( } #endif - // Add the piece DWARF expression. - Operands.push_back(llvm::dwarf::DW_OP_LLVM_fragment); - Operands.push_back(OffsetInBits); - Operands.push_back(SizeInBits); + // Add the piece DW_OP_LLVM_fragment operands + Fragment.first = OffsetInBits; + Fragment.second = SizeInBits; } llvm::DIExpression *DIExpr = DBuilder.createExpression(Operands); - // DW_OP_LLVM_fragment must be the last part of an DIExpr - // so we can't append more if IsPiece is true. - if (!IsPiece) - DIExpr = appendDIExpression(DIExpr); + DIExpr = appendDIExpression(DIExpr, Fragment); if (DIExpr) emitDbgIntrinsic( Builder, Piece, Var, DIExpr, DInstLine, DInstLoc.column, Scope, DS, @@ -2742,7 +2784,9 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( // Emit locationless intrinsic for variables that were optimized away. if (Storage.empty()) { - if (auto *DIExpr = appendDIExpression(DBuilder.createExpression())) + std::pair NoFragment(0, 0); + if (auto *DIExpr = + appendDIExpression(DBuilder.createExpression(), NoFragment)) emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var, DIExpr, DInstLine, DInstLoc.column, Scope, DS, Indirection == CoroDirectValue || diff --git a/test/IRGen/debug_fragment_merge.swift b/test/IRGen/debug_fragment_merge.swift new file mode 100644 index 0000000000000..53311981d353c --- /dev/null +++ b/test/IRGen/debug_fragment_merge.swift @@ -0,0 +1,40 @@ +// RUN: %target-swift-frontend -primary-file %s -emit-sil -O -g | %FileCheck %s --check-prefix CHECK-SIL +// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s + +protocol External { + func use(str: String); + func decode(_: T.Type) -> T +} + +struct Data { + var a: String + var b: String +} + +func test(cond: Int, external: External) async { + // CHECK-DAG: ![[VAR:[0-9]+]] = !DILocalVariable(name: "data", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: [[# @LINE + 1]], type: !{{[0-9]+}}) + let data: Data + switch cond { + // CHECK-DAG: ![[LOC1:[0-9]+]] = !DILocation(line: [[# @LINE + 1]], column: 12, scope: !{{.*}}) + case 42: data = external.decode(Data.self) + // CHECK-DAG: ![[LOC2:[0-9]+]] = !DILocation(line: [[# @LINE + 1]], column: 12, scope: !{{.*}}) + default: data = external.decode(Data.self) + } + external.use(str: data.a) + external.use(str: data.b) +} + +// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.a +// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.b +// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.a +// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.b + +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 192, 64){{.*}} !dbg ![[LOC1]] +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64){{.*}} !dbg ![[LOC1]] +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64){{.*}} !dbg ![[LOC1]] +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64){{.*}} !dbg ![[LOC1]] +// +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 192, 64){{.*}} !dbg ![[LOC2]] +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64){{.*}} !dbg ![[LOC2]] +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64){{.*}} !dbg ![[LOC2]] +// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64){{.*}} !dbg ![[LOC2]] From ab1ce9e4d8f6247f82dd967af0b5516ce3abe380 Mon Sep 17 00:00:00 2001 From: Andrew Savonichev Date: Tue, 13 Jun 2023 18:31:32 +0900 Subject: [PATCH 2/6] Require "concurrency" for the LIT test with "async" --- test/IRGen/debug_fragment_merge.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/IRGen/debug_fragment_merge.swift b/test/IRGen/debug_fragment_merge.swift index 53311981d353c..708778b3f10db 100644 --- a/test/IRGen/debug_fragment_merge.swift +++ b/test/IRGen/debug_fragment_merge.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -primary-file %s -emit-sil -O -g | %FileCheck %s --check-prefix CHECK-SIL // RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s +// REQUIRES: concurrency protocol External { func use(str: String); From d5b56d71531e51b712a3e87d88a1f36209b58807 Mon Sep 17 00:00:00 2001 From: Andrew Savonichev Date: Thu, 15 Jun 2023 01:42:02 +0900 Subject: [PATCH 3/6] Revert "Require "concurrency" for the LIT test with "async"" This reverts commit ab1ce9e4d8f6247f82dd967af0b5516ce3abe380. Does not help to fix CI failures. --- test/IRGen/debug_fragment_merge.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/test/IRGen/debug_fragment_merge.swift b/test/IRGen/debug_fragment_merge.swift index 708778b3f10db..53311981d353c 100644 --- a/test/IRGen/debug_fragment_merge.swift +++ b/test/IRGen/debug_fragment_merge.swift @@ -1,6 +1,5 @@ // RUN: %target-swift-frontend -primary-file %s -emit-sil -O -g | %FileCheck %s --check-prefix CHECK-SIL // RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s -// REQUIRES: concurrency protocol External { func use(str: String); From 853ab3166c4e48ab28c3384af9ca25f1a9eae9f0 Mon Sep 17 00:00:00 2001 From: Andrew Savonichev Date: Thu, 15 Jun 2023 01:43:20 +0900 Subject: [PATCH 4/6] Add -disable-availability-checking to avoid errors about async --- test/IRGen/debug_fragment_merge.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/IRGen/debug_fragment_merge.swift b/test/IRGen/debug_fragment_merge.swift index 53311981d353c..ae373691ac62f 100644 --- a/test/IRGen/debug_fragment_merge.swift +++ b/test/IRGen/debug_fragment_merge.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -primary-file %s -emit-sil -O -g | %FileCheck %s --check-prefix CHECK-SIL -// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s +// RUN: %target-swift-frontend -disable-availability-checking -primary-file %s -emit-sil -O -g | %FileCheck %s --check-prefix CHECK-SIL +// RUN: %target-swift-frontend -disable-availability-checking -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s protocol External { func use(str: String); From e57e1d0a6c8c0c7f3251df0f2453d1b836347dbc Mon Sep 17 00:00:00 2001 From: Andrew Savonichev Date: Mon, 19 Jun 2023 21:27:13 +0900 Subject: [PATCH 5/6] Use llvm::DIExpression::FragmentInfo --- lib/IRGen/IRGenDebugInfo.cpp | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 10fdd073c5afb..0491f15ff50be 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -215,11 +215,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { /// Return false if we fail to create the right DW_OP_LLVM_fragment operand. bool handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp, - std::pair &Fragment); + llvm::DIExpression::FragmentInfo &Fragment); /// Return false if we fail to create the desired !DIExpression. bool buildDebugInfoExpression(const SILDebugVariable &VarInfo, SmallVectorImpl &Operands, - std::pair &Fragment); + llvm::DIExpression::FragmentInfo &Fragment); /// Emit a dbg.declare at the current insertion point in Builder. void emitVariableDeclaration(IRBuilder &Builder, @@ -2524,7 +2524,7 @@ void IRGenDebugInfoImpl::emitOutlinedFunction(IRBuilder &Builder, bool IRGenDebugInfoImpl::handleFragmentDIExpr( const SILDIExprOperand &CurDIExprOp, - std::pair &Fragment) { + llvm::DIExpression::FragmentInfo &Fragment) { assert(CurDIExprOp.getOperator() == SILDIExprOperator::Fragment); // Expecting a VarDecl that points to a field in an struct auto DIExprArgs = CurDIExprOp.args(); @@ -2557,14 +2557,14 @@ bool IRGenDebugInfoImpl::handleFragmentDIExpr( Offset->getUniqueInteger().getLimitedValue() * SizeOfByte; // Translate to DW_OP_LLVM_fragment operands - Fragment = std::make_pair(OffsetInBits, SizeInBits); + Fragment = {SizeInBits, OffsetInBits}; return true; } bool IRGenDebugInfoImpl::buildDebugInfoExpression( const SILDebugVariable &VarInfo, SmallVectorImpl &Operands, - std::pair &Fragment) { + llvm::DIExpression::FragmentInfo &Fragment) { assert(VarInfo.DIExpr && "SIL debug info expression not found"); #ifndef NDEBUG @@ -2693,39 +2693,33 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( unsigned AlignInBits = SizeOfByte; unsigned OffsetInBits = 0; unsigned SizeInBits = 0; - std::pair Fragment(0, 0); + llvm::DIExpression::FragmentInfo Fragment = {0, 0}; auto appendDIExpression = - [&VarInfo, this]( - llvm::DIExpression *DIExpr, - std::pair PieceFragment) -> llvm::DIExpression * { - unsigned PieceFragmentOffset = PieceFragment.first; - unsigned PieceFragmentSize = PieceFragment.second; - + [&VarInfo, this](llvm::DIExpression *DIExpr, + llvm::DIExpression::FragmentInfo PieceFragment) + -> llvm::DIExpression * { if (!VarInfo.DIExpr) { - if (!PieceFragmentSize) + if (!PieceFragment.SizeInBits) return DIExpr; return llvm::DIExpression::createFragmentExpression( - DIExpr, PieceFragmentOffset, PieceFragmentSize) + DIExpr, PieceFragment.OffsetInBits, PieceFragment.SizeInBits) .getValueOr(nullptr); } llvm::SmallVector Operands; - std::pair VarFragment(0, 0); + llvm::DIExpression::FragmentInfo VarFragment = {0, 0}; if (!buildDebugInfoExpression(VarInfo, Operands, VarFragment)) return nullptr; if (!Operands.empty()) DIExpr = llvm::DIExpression::append(DIExpr, Operands); - unsigned VarFragmentOffset = VarFragment.first; - unsigned VarFragmentSize = VarFragment.second; - // Add the fragment of the SIL variable. - if (VarFragmentSize) + if (VarFragment.SizeInBits) DIExpr = llvm::DIExpression::createFragmentExpression( - DIExpr, VarFragmentOffset, VarFragmentSize) + DIExpr, VarFragment.OffsetInBits, VarFragment.SizeInBits) .getValueOr(nullptr); if (!DIExpr) @@ -2734,9 +2728,9 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( // When the fragment of the SIL variable is further split into other // fragments (PieceFragment), merge them into one DW_OP_LLVM_Fragment // expression. - if (PieceFragmentSize) + if (PieceFragment.SizeInBits) return llvm::DIExpression::createFragmentExpression( - DIExpr, PieceFragmentOffset, PieceFragmentSize) + DIExpr, PieceFragment.OffsetInBits, PieceFragment.SizeInBits) .getValueOr(nullptr); return DIExpr; @@ -2770,8 +2764,8 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( #endif // Add the piece DW_OP_LLVM_fragment operands - Fragment.first = OffsetInBits; - Fragment.second = SizeInBits; + Fragment.OffsetInBits = OffsetInBits; + Fragment.SizeInBits = SizeInBits; } llvm::DIExpression *DIExpr = DBuilder.createExpression(Operands); DIExpr = appendDIExpression(DIExpr, Fragment); @@ -2784,7 +2778,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( // Emit locationless intrinsic for variables that were optimized away. if (Storage.empty()) { - std::pair NoFragment(0, 0); + llvm::DIExpression::FragmentInfo NoFragment = {0, 0}; if (auto *DIExpr = appendDIExpression(DBuilder.createExpression(), NoFragment)) emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var, From 56e082a9146d2d0ae291e1da97869bf00022faec Mon Sep 17 00:00:00 2001 From: Andrew Savonichev Date: Tue, 20 Jun 2023 01:39:08 +0900 Subject: [PATCH 6/6] Add a SIL LIT test --- test/IRGen/debug_fragment_merge.sil | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 test/IRGen/debug_fragment_merge.sil diff --git a/test/IRGen/debug_fragment_merge.sil b/test/IRGen/debug_fragment_merge.sil new file mode 100644 index 0000000000000..e794007d99bb8 --- /dev/null +++ b/test/IRGen/debug_fragment_merge.sil @@ -0,0 +1,65 @@ +// RUN: %target-swift-frontend -disable-availability-checking -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s + +// CHECK-DAG: llvm.dbg.value{{.*}} metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 192, 64){{.*}} !dbg ![[LOC1:[0-9]+]] +// CHECK-DAG: llvm.dbg.value{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64){{.*}} !dbg ![[LOC1]] +// CHECK-DAG: llvm.dbg.value{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64){{.*}} !dbg ![[LOC1]] +// CHECK-DAG: llvm.dbg.value{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64){{.*}} !dbg ![[LOC1]] + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +protocol External { + func use(str: String) + func decode(_: T.Type) -> T +} + +struct Data { + @_hasStorage var a: String { get set } + @_hasStorage var b: String { get set } + init(a: String, b: String) +} + +sil_scope 9 { loc "debug_fragment_merge.swift":14:6 parent @$s20debug_fragment_merge4test4cond8externalySi_AA8External_ptYaF : $@convention(thin) (Int, @in_guaranteed any External) -> () } +sil_scope 10 { loc "debug_fragment_merge.swift":16:7 parent 9 } +sil_scope 11 { loc "debug_fragment_merge.swift":17:3 parent 10 } +sil_scope 12 { loc "debug_fragment_merge.swift":19:3 parent 11 } +sil_scope 13 { loc "debug_fragment_merge.swift":19:12 parent 12 } +sil_scope 14 { loc "debug_fragment_merge.swift":21:3 parent 11 } +sil_scope 15 { loc "debug_fragment_merge.swift":21:12 parent 14 } + +sil @$side_effect : $@convention(thin) (@owned String) -> () +sil @$side_effect_data : $@convention(thin) (@owned Data) -> () +sil @$cond : $@convention(thin) () -> (Builtin.Int1) + +// test(cond:external:), loc "debug_fragment_merge.swift":14:6, scope 9 +sil hidden @$s20debug_fragment_merge4test4cond8externalySi_AA8External_ptYaF : $@convention(thin) (Int, @in_guaranteed any External) -> () { +[%1: read v**, write v**, copy v**, destroy v**] +[global: read,write,copy,destroy,allocate,deinit_barrier] +bb0(%0 : $Int, %1 : $*any External): + br bb1 // cond_br %cond, bb1, bb2 + +bb1: // Preds: bb0 + %10 = open_existential_addr immutable_access %1 : $*any External to $*@opened("EDD72648-0EA0-11EE-9925-E91C6F300971", any External) Self, loc "debug_fragment_merge.swift":19:28, scope 12 // users: %14, %14, %13 + %11 = alloc_stack $Data, loc * "debug_fragment_merge.swift":19:28, scope 13 // users: %17, %15, %21, %14 + %12 = metatype $@thick Data.Type, loc "debug_fragment_merge.swift":19:35, scope 13 // user: %14 + %13 = witness_method $@opened("EDD72648-0EA0-11EE-9925-E91C6F300971", any External) Self, #External.decode : (Self) -> (T.Type) -> T, %10 : $*@opened("EDD72648-0EA0-11EE-9925-E91C6F300971", any External) Self : $@convention(witness_method: External) <τ_0_0 where τ_0_0 : External><τ_1_0> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out τ_1_0, loc "debug_fragment_merge.swift":19:28, scope 13 // type-defs: %10; user: %14 + %14 = apply %13<@opened("EDD72648-0EA0-11EE-9925-E91C6F300971", any External) Self, Data>(%11, %12, %10) : $@convention(witness_method: External) <τ_0_0 where τ_0_0 : External><τ_1_0> (@thick τ_1_0.Type, @in_guaranteed τ_0_0) -> @out τ_1_0, loc "debug_fragment_merge.swift":19:28, scope 13 // type-defs: %10 + %15 = struct_element_addr %11 : $*Data, #Data.a, loc "debug_fragment_merge.swift":19:28, scope 13 // user: %16 + %16 = load %15 : $*String, loc "debug_fragment_merge.swift":19:28, scope 13 // users: %19, %22 + %17 = struct_element_addr %11 : $*Data, #Data.b, loc "debug_fragment_merge.swift":19:28, scope 13 // user: %18 + %18 = load %17 : $*String, loc "debug_fragment_merge.swift":19:28, scope 13 // users: %22, %20 + debug_value %16 : $String, let, (name "data", loc "debug_fragment_merge.swift":16:7, scope 10), type $*Data, expr op_fragment:#Data.a, loc "debug_fragment_merge.swift":19:17, scope 12 // id: %19 + debug_value %18 : $String, let, (name "data", loc "debug_fragment_merge.swift":16:7, scope 10), type $*Data, expr op_fragment:#Data.b, loc "debug_fragment_merge.swift":19:17, scope 12 // id: %20 + dealloc_stack %11 : $*Data, loc "debug_fragment_merge.swift":19:44, scope 13 // id: %21 + br bb3(%18 : $String, %16 : $String), loc * "debug_fragment_merge.swift":19:44, scope 13 // id: %22 + +bb3(%36 : $String, %37 : $String): // Preds: bb2 bb1 + %side_effect_ref = function_ref @$side_effect : $@convention(thin) (@owned String) -> () + apply %side_effect_ref(%36) : $@convention(thin) (@owned String) -> () + apply %side_effect_ref(%37) : $@convention(thin) (@owned String) -> () + %44 = tuple () + return %44 : $() +}