Skip to content

Commit 23e6e54

Browse files
Merge pull request swiftlang#71925 from Snowy1803/recursive-sroa
[DebugInfo] Add debug info support for recursive SIL SROA
2 parents d420fe6 + 49ee148 commit 23e6e54

File tree

11 files changed

+154
-42
lines changed

11 files changed

+154
-42
lines changed

include/swift/SIL/SILDebugVariable.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ struct SILDebugVariable {
7676
static std::optional<SILDebugVariable>
7777
createFromAllocation(const AllocationInst *AI);
7878

79+
/// Return the underlying variable declaration that this denotes,
80+
/// or nullptr if we don't have one.
81+
VarDecl *getDecl() const;
82+
7983
// We're not comparing DIExpr here because strictly speaking,
8084
// DIExpr is not part of the debug variable. We simply piggyback
8185
// it in this class so that's it's easier to carry DIExpr around.

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2961,21 +2961,19 @@ bool IRGenDebugInfoImpl::buildDebugInfoExpression(
29612961
llvm::DIExpression::FragmentInfo &Fragment) {
29622962
assert(VarInfo.DIExpr && "SIL debug info expression not found");
29632963

2964-
#ifndef NDEBUG
2965-
bool HasFragment = VarInfo.DIExpr.hasFragment();
2966-
#endif
2967-
29682964
const auto &DIExpr = VarInfo.DIExpr;
29692965
for (const SILDIExprOperand &ExprOperand : DIExpr.operands()) {
2966+
llvm::DIExpression::FragmentInfo SubFragment = {0, 0};
29702967
switch (ExprOperand.getOperator()) {
29712968
case SILDIExprOperator::Fragment:
2972-
assert(HasFragment && "Fragment must be the last part of a DIExpr");
2973-
#ifndef NDEBUG
2974-
// Trigger the assert above if we have more than one fragment expression.
2975-
HasFragment = false;
2976-
#endif
2977-
if (!handleFragmentDIExpr(ExprOperand, Fragment))
2969+
if (!handleFragmentDIExpr(ExprOperand, SubFragment))
29782970
return false;
2971+
assert(!Fragment.SizeInBits
2972+
|| (SubFragment.OffsetInBits + SubFragment.SizeInBits
2973+
<= Fragment.SizeInBits)
2974+
&& "Invalid nested fragments");
2975+
Fragment.OffsetInBits += SubFragment.OffsetInBits;
2976+
Fragment.SizeInBits = SubFragment.SizeInBits;
29792977
break;
29802978
case SILDIExprOperator::Dereference:
29812979
Operands.push_back(llvm::dwarf::DW_OP_deref);

lib/IRGen/IRGenSIL.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5463,9 +5463,16 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
54635463
assert(isa<SILBoxType>(RealTy));
54645464
}
54655465

5466+
VarDecl *VD = i->getDecl();
5467+
if (!VD) {
5468+
// The source location of a DebugValueInst inserted by the SIL optimizer is
5469+
// not necessarily the VarDecl, as it can be the source location of the
5470+
// update point this DebugValueInst represents.
5471+
VD = VarInfo->getDecl();
5472+
}
54665473
// Figure out the debug variable type
5467-
if (VarDecl *Decl = i->getDecl()) {
5468-
DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealTy, getTypeInfo(SILTy),
5474+
if (VD) {
5475+
DbgTy = DebugTypeInfo::getLocalVariable(VD, RealTy, getTypeInfo(SILTy),
54695476
IGM, IsFragmentType);
54705477
} else if (!SILTy.hasArchetype() && !VarInfo->Name.empty()) {
54715478
// Handle the cases that read from a SIL file

lib/SIL/IR/SILInstructions.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,6 @@ SILDebugVariable::createFromAllocation(const AllocationInst *AI) {
208208
if (!VarInfo)
209209
return {};
210210

211-
// TODO: Support variables with expressions.
212-
if (VarInfo->DIExpr)
213-
return {};
214-
215211
// Coalesce the debug loc attached on AI into VarInfo
216212
SILType Type = AI->getType();
217213
SILLocation InstLoc = AI->getLoc();
@@ -495,6 +491,12 @@ VarDecl *DebugValueInst::getDecl() const {
495491
return getLoc().getAsASTNode<VarDecl>();
496492
}
497493

494+
VarDecl *SILDebugVariable::getDecl() const {
495+
if (!Loc)
496+
return nullptr;
497+
return Loc->getAsASTNode<VarDecl>();
498+
}
499+
498500
AllocExistentialBoxInst *AllocExistentialBoxInst::create(
499501
SILDebugLocation Loc, SILType ExistentialType, CanType ConcreteType,
500502
ArrayRef<ProtocolConformanceRef> Conformances,

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
14691469

14701470
// Check debug info expression
14711471
if (const auto &DIExpr = varInfo->DIExpr) {
1472+
bool HasFragment = false;
14721473
for (auto It = DIExpr.element_begin(), ItEnd = DIExpr.element_end();
14731474
It != ItEnd;) {
14741475
require(It->getKind() == SILDIExprElement::OperatorKind,
@@ -1483,8 +1484,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
14831484
"di-expression operand kind mismatch");
14841485

14851486
if (Op == SILDIExprOperator::Fragment)
1486-
require(It == ItEnd, "op_fragment directive needs to be at the end "
1487-
"of a di-expression");
1487+
HasFragment = true;
1488+
else
1489+
require(!HasFragment, "no directive allowed after op_fragment"
1490+
" in a di-expression");
14881491
}
14891492
}
14901493
}

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,12 +1854,6 @@ void swift::salvageDebugInfo(SILInstruction *I) {
18541854
auto VarInfo = DbgInst->getVarInfo();
18551855
if (!VarInfo)
18561856
continue;
1857-
if (VarInfo->DIExpr.hasFragment())
1858-
// Since we can't merge two different op_fragment
1859-
// now, we're simply bailing out if there is an
1860-
// existing op_fragment in DIExpression.
1861-
// TODO: Try to merge two op_fragment expressions here.
1862-
continue;
18631857
for (VarDecl *FD : FieldDecls) {
18641858
SILDebugVariable NewVarInfo = *VarInfo;
18651859
auto FieldVal = STI->getFieldValue(FD);
@@ -1933,10 +1927,7 @@ void swift::createDebugFragments(SILValue oldValue, Projection proj,
19331927
if (!debugVal)
19341928
continue;
19351929

1936-
// Can't create a fragment of a fragment.
19371930
auto varInfo = debugVal->getVarInfo();
1938-
if (!varInfo || varInfo->DIExpr.hasFragment())
1939-
continue;
19401931

19411932
SILType baseType = oldValue->getType();
19421933

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// RUN: %target-sil-opt %s -performance-constant-propagation | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
8+
struct Wrapper {
9+
let bytes: UInt64
10+
private func pop(numBits: UInt64) -> Wrapper
11+
public static func maker(bits: UInt64) -> Wrapper
12+
}
13+
14+
sil_scope 3 { loc "e.swift":6:16 parent @pop : $@convention(method) (UInt64, Wrapper) -> Wrapper }
15+
sil_scope 4 { loc "e.swift":8:12 parent 3 }
16+
17+
// Wrapper.pop(numBits:), scope 3
18+
sil private @pop : $@convention(method) (UInt64, Wrapper) -> Wrapper {
19+
[global: ]
20+
bb0(%0 : $UInt64, %1 : $Wrapper):
21+
debug_value %0 : $UInt64, let, name "numBits", argno 1, scope 3
22+
debug_value %1 : $Wrapper, let, name "self", argno 2, implicit, scope 3
23+
%4 = struct_extract %1 : $Wrapper, #Wrapper.bytes, scope 3
24+
%5 = struct_extract %4 : $UInt64, #UInt64._value, scope 3
25+
%6 = struct_extract %0 : $UInt64, #UInt64._value, scope 3
26+
%7 = integer_literal $Builtin.Int1, -1, scope 3
27+
%8 = builtin "usub_with_overflow_Int64"(%5 : $Builtin.Int64, %6 : $Builtin.Int64, %7 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1), scope 3
28+
%9 = tuple_extract %8 : $(Builtin.Int64, Builtin.Int1), 0, scope 3
29+
%10 = tuple_extract %8 : $(Builtin.Int64, Builtin.Int1), 1, scope 3
30+
cond_fail %10 : $Builtin.Int1, "arithmetic overflow", scope 3
31+
%12 = struct $UInt64 (%9 : $Builtin.Int64), scope 3
32+
%13 = struct $Wrapper (%12 : $UInt64), loc * "e.swift":3:8
33+
return %13 : $Wrapper, scope 3
34+
} // end sil function 'pop'
35+
36+
sil_scope 7 { loc "e.swift":12:22 parent @maker : $@convention(method) (UInt64, @thin Wrapper.Type) -> Wrapper }
37+
sil_scope 8 { loc "e.swift":17:12 parent 7 }
38+
sil_scope 10 { loc "e.swift":17:39 parent 7 }
39+
sil_scope 11 { loc "e.swift":6:16 parent @pop : $@convention(method) (UInt64, Wrapper) -> Wrapper inlined_at 10 }
40+
sil_scope 12 { loc "e.swift":8:12 parent 11 inlined_at 10 }
41+
42+
// static Wrapper.maker(bits:), scope 7
43+
// CHECK-LABEL: sil {{.*}} @maker
44+
sil hidden @maker : $@convention(method) (UInt64, @thin Wrapper.Type) -> Wrapper {
45+
[global: ]
46+
bb0(%0 : $UInt64, %1 : $@thin Wrapper.Type):
47+
// CHECK: debug_value %0 : $UInt64, let, name "bits", argno 1
48+
debug_value %0 : $UInt64, let, name "bits", argno 1, scope 7
49+
// CHECK: debug_value %1 : $@thin Wrapper.Type, let, name "self", argno 2, implicit
50+
debug_value %1 : $@thin Wrapper.Type, let, name "self", argno 2, implicit, scope 7
51+
%4 = integer_literal $Builtin.Int64, 3735928559, scope 7
52+
%5 = struct $UInt64 (%4 : $Builtin.Int64), scope 7
53+
%6 = struct $Wrapper (%5 : $UInt64), loc * "e.swift":3:8
54+
// CHECK-DAG: debug_value %0 : $UInt64, let, name "numBits", argno 1
55+
debug_value %0 : $UInt64, let, name "numBits", argno 1, scope 11
56+
// CHECK-DAG: debug_value %{{.*}} : $Builtin.Int64, let, name "self", {{.*}}, implicit, type $Wrapper, expr op_fragment:#Wrapper.bytes:op_fragment:#UInt64._value
57+
debug_value %6 : $Wrapper, let, name "self", argno 2, implicit, scope 11
58+
%9 = struct_extract %6 : $Wrapper, #Wrapper.bytes, scope 11
59+
%10 = struct_extract %9 : $UInt64, #UInt64._value, scope 11
60+
%11 = struct_extract %0 : $UInt64, #UInt64._value, scope 11
61+
%12 = integer_literal $Builtin.Int1, -1, scope 11
62+
%13 = builtin "usub_with_overflow_Int64"(%10 : $Builtin.Int64, %11 : $Builtin.Int64, %12 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1), scope 11
63+
%14 = tuple_extract %13 : $(Builtin.Int64, Builtin.Int1), 0, scope 11
64+
%15 = tuple_extract %13 : $(Builtin.Int64, Builtin.Int1), 1, scope 11
65+
cond_fail %15 : $Builtin.Int1, "arithmetic overflow", scope 11
66+
%17 = struct $UInt64 (%14 : $Builtin.Int64), scope 11
67+
%18 = struct $Wrapper (%17 : $UInt64), loc * "e.swift":3:8
68+
return %18 : $Wrapper, scope 7
69+
} // end sil function 'maker'
70+
71+
72+
// Generated and adapted from this Swift source code:
73+
// ```swift
74+
// struct Wrapper {
75+
// let bytes: UInt64
76+
//
77+
// private func pop(numBits: UInt64) -> Wrapper {
78+
// // `self` in here is a nested fragment. It should be salvaged, when inlined in `maker(bits:)`
79+
// return Self(bytes: bytes - numBits)
80+
// }
81+
//
82+
// public static func maker(bits: UInt64) -> Wrapper {
83+
// // This function is static, but `pop`'s self should be inlined in here
84+
// return Wrapper(bytes: 0xDEADBEEF).pop(numBits: bits)
85+
// }
86+
// }
87+
// ```

test/DebugInfo/sil_based_dbg.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,12 @@ struct TheStruct {
2929
}
3030
// CHECK_DBG_SCOPE-LABEL: sil {{.*}}test_debug_scope
3131
public func test_debug_scope(val : Int) -> Int {
32-
// TODO: Repeated SROA of the same variable is currently disabled.
33-
// CHECK_DBG_SCOPE: alloc_stack $Builtin.Int{{[0-9]+}},
34-
// CHECK_DBG_SCOPE-NOT: var,
35-
// DISABLED_DBG_SCOPE-SAME: var, (name "the_struct",
36-
// DISABLED_DBG_SCOPE-SAME: loc
32+
// CHECK_DBG_SCOPE: alloc_stack $Builtin.Int{{[0-9]+}},
33+
// CHECK_DBG_SCOPE-SAME: var, (name "the_struct",
34+
// CHECK_DBG_SCOPE-SAME: loc
35+
// CHECK_DBG_SCOPE-SAME: expr op_fragment:#TheStruct.the_member:op_fragment:#Int._value
3736
// The auxiliary debug scope should be removed
38-
// DISABLED_DBG_SCOPE-NOT: scope {{[0-9]+}})
37+
// CHECK_DBG_SCOPE-NOT: scope {{[0-9]+}})
3938
var the_struct = TheStruct(the_member: 0)
4039
the_struct.the_member = val + 13
4140
return the_struct.the_member

test/DebugInfo/srosroa_mem2reg.sil

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ bb0(%0 : $Int64, %1 : $Int64):
3131
debug_value %0 : $Int64, let, name "in_x", argno 1, loc "sroa.swift":7:10, scope 2
3232
debug_value %1 : $Int64, let, name "in_y", argno 2, loc "sroa.swift":7:21, scope 2
3333
%4 = alloc_stack $MyStruct, var, name "my_struct", type $*MyContainer, expr op_fragment:#MyContainer.z, loc "sroa.swift":8:9, scope 2
34-
// Recursive SROA of already SROA-ed aggregates is not yet supported.
3534
// CHECK-SROA: alloc_stack $Int64
36-
// CHECK-SROA-NOT: var,
35+
// CHECK-SROA-SAME: var,
3736
// CHECK-SROA: alloc_stack $Int64
38-
// CHECK-SROA-NOT: var,
37+
// CHECK-SROA-SAME: var,
3938
// CHECK-SROA: metatype
4039
%5 = metatype $@thin MyStruct.Type, loc "sroa.swift":8:21, scope 2
4140
%6 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":8:33, scope 2
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not --crash %target-swift-frontend %s -sil-verify-all -g -emit-sil
1+
// RUN: %target-swift-frontend %s -sil-verify-all -g -emit-sil | %FileCheck %s
22
import Builtin
33
import Swift
44

@@ -11,11 +11,12 @@ sil_scope 1 { loc "file.swift":7:6 parent @test_fragment : $@convention(thin) ()
1111

1212
sil hidden @test_fragment : $@convention(thin) () -> () {
1313
bb0:
14-
%2 = alloc_stack $MyStruct, var, name "my_struct", loc "file.swift":8:9, scope 1
15-
%3 = struct_element_addr %2 : $*MyStruct, #MyStruct.x, loc "file.swift":9:17, scope 1
16-
// every op_fragment should be the last di-expression operand
17-
debug_value %3 : $*Builtin.Int64, var, name "my_struct", expr op_deref:op_fragment:#MyStruct.y:op_fragment:#MyStruct.x
18-
dealloc_stack %2 : $*MyStruct
14+
%0 = alloc_stack $MyStruct, var, name "my_struct", loc "file.swift":8:9, scope 1
15+
%1 = struct_element_addr %0 : $*MyStruct, #MyStruct.x, loc "file.swift":9:17, scope 1
16+
// nested op_fragments at the end are allowed
17+
// CHECK: debug_value %1 : $*Builtin.Int64, var, name "my_struct", expr op_deref:op_fragment:#MyStruct.y:op_fragment:#MyStruct.x
18+
debug_value %1 : $*Builtin.Int64, var, name "my_struct", expr op_deref:op_fragment:#MyStruct.y:op_fragment:#MyStruct.x
19+
dealloc_stack %0 : $*MyStruct
1920
%r = tuple()
2021
return %r : $()
2122
}

0 commit comments

Comments
 (0)