Skip to content

Commit 35e346f

Browse files
committed
Add support for op_tuple_fragment in SIL DIExpression and SIL SROA
rdar://124034536
1 parent 7685371 commit 35e346f

File tree

9 files changed

+199
-13
lines changed

9 files changed

+199
-13
lines changed

docs/SIL.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4325,6 +4325,7 @@ from that of source variable.
43254325
debug-info-expr ::= di-expr-operand (':' di-expr-operand)*
43264326
di-expr-operand ::= di-expr-operator (':' sil-operand)*
43274327
di-expr-operator ::= 'op_fragment'
4328+
di-expr-operator ::= 'op_tuple_fragment'
43284329
di-expr-operator ::= 'op_deref'
43294330

43304331
SIL debug info expression (SIL DIExpression) is a powerful method to connect SSA
@@ -4372,6 +4373,10 @@ a field declaration -- which references the desired sub-field in source variable
43724373
In the snippet above, source variable "the_struct" has an aggregate type ``$MyStruct`` and we use a SIL DIExpression with ``op_fragment`` operator to associate ``%1`` to the ``y`` member variable (via the ``#MyStruct.y`` directive) inside "the_struct".
43734374
Note that the extra source location directive follows right after ``name "the_struct"`` indicate that "the_struct" was originally declared in line 8, but not until line 9 -- the current ``debug_value`` instruction's source location -- does member ``y`` got updated with SSA value ``%1``.
43744375

4376+
For tuples, it works similarly, except we use ``op_tuple_fragment``, which takes two arguments: the tuple type and the index. If our struct was instead a tuple, we would have:
4377+
4378+
debug_value %1 : $Int, var, (name "the_tuple", loc "file.swift":8:7), type $(x: Int, y: Int), expr op_tuple_fragment:$(x: Int, y: Int):1, loc "file.swift":9:4
4379+
43754380
It is worth noting that a SIL DIExpression is similar to
43764381
`!DIExpression <https://www.llvm.org/docs/LangRef.html#diexpression>`_ in LLVM debug
43774382
info metadata. While LLVM represents ``!DIExpression`` are a list of 64-bit integers,

include/swift/SIL/SILDebugInfoExpression.h

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ enum class SILDIExprOperator : unsigned {
4040
/// associated source variable. This operator takes a single
4141
/// VarDecl operand pointing to the field declaration.
4242
/// Note that this directive can only appear at the end of an
43-
/// expression.
43+
/// expression, along with `TupleFragment`.
4444
Fragment,
4545
/// Perform arithmetic addition on the top two elements of the
4646
/// expression stack and push the result back to the stack.
@@ -51,7 +51,13 @@ enum class SILDIExprOperator : unsigned {
5151
/// Push an unsigned integer constant onto the stack.
5252
ConstUInt,
5353
/// Push a signed integer constant onto the stack.
54-
ConstSInt
54+
ConstSInt,
55+
/// Specifies that the SSA value is an element of the
56+
/// associated tuple. This operator takes a TupleType
57+
/// operand pointing to the tuple type, and the index of the element.
58+
/// Note that this directive can only appear at the end of an
59+
/// expression, along with `Fragment`.
60+
TupleFragment
5561
};
5662

5763
/// Represents a single component in a debug info expression.
@@ -64,7 +70,9 @@ struct SILDIExprElement {
6470
DeclKind,
6571
/// An integer constant value. Note that
6672
/// we don't specify its signedness here.
67-
ConstIntKind
73+
ConstIntKind,
74+
/// An operand that has `Type` type.
75+
TypeKind
6876
};
6977

7078
private:
@@ -74,6 +82,7 @@ struct SILDIExprElement {
7482
SILDIExprOperator Operator;
7583
Decl *Declaration;
7684
uint64_t ConstantInt;
85+
Type TypePtr;
7786
};
7887

7988
explicit SILDIExprElement(Kind OpK) : OpKind(OpK) {}
@@ -94,6 +103,8 @@ struct SILDIExprElement {
94103
return {};
95104
}
96105

106+
Type getAsType() const { return OpKind == TypeKind ? TypePtr : nullptr; }
107+
97108
static SILDIExprElement createOperator(SILDIExprOperator Op) {
98109
SILDIExprElement DIOp(OperatorKind);
99110
DIOp.Operator = Op;
@@ -111,6 +122,12 @@ struct SILDIExprElement {
111122
DIOp.ConstantInt = V;
112123
return DIOp;
113124
}
125+
126+
static SILDIExprElement createType(Type T) {
127+
SILDIExprElement DIOp(TypeKind);
128+
DIOp.TypePtr = T;
129+
return DIOp;
130+
}
114131
};
115132

116133
/// Returns the hashcode for the di expr element.
@@ -267,20 +284,27 @@ class SILDebugInfoExpression {
267284
/// Return true if this expression is not empty
268285
inline operator bool() const { return Elements.size(); }
269286

270-
/// Create a op_fragment expression
287+
/// Create a `op_fragment` expression
271288
static SILDebugInfoExpression createFragment(VarDecl *Field);
272289

290+
/// Create a `op_tuple_fragment` expression
291+
static
292+
SILDebugInfoExpression createTupleFragment(TupleType *TypePtr, unsigned Idx);
293+
273294
/// Return true if this DIExpression starts with op_deref
274295
bool startsWithDeref() const {
275296
return Elements.size() &&
276297
Elements[0].getAsOperator() == SILDIExprOperator::Dereference;
277298
}
278299

279-
/// Return true if this DIExpression has op_fragment (at the end)
300+
/// Return true if this DIExpression has a fragment (at the end)
280301
bool hasFragment() const {
281-
return Elements.size() >= 2 &&
302+
return (Elements.size() >= 2 &&
282303
Elements[Elements.size() - 2].getAsOperator() ==
283-
SILDIExprOperator::Fragment;
304+
SILDIExprOperator::Fragment) ||
305+
(Elements.size() >= 3 &&
306+
Elements[Elements.size() - 3].getAsOperator() ==
307+
SILDIExprOperator::TupleFragment);
284308
}
285309
};
286310

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "GenEnum.h"
1919
#include "GenOpaque.h"
2020
#include "GenStruct.h"
21+
#include "GenTuple.h"
2122
#include "GenType.h"
2223
#include "IRBuilder.h"
2324
#include "swift/AST/ASTDemangler.h"
@@ -232,6 +233,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
232233
/// Return false if we fail to create the right DW_OP_LLVM_fragment operand.
233234
bool handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp,
234235
llvm::DIExpression::FragmentInfo &Fragment);
236+
/// Return false if we fail to create the right DW_OP_LLVM_fragment operand.
237+
bool handleTupleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp,
238+
llvm::DIExpression::FragmentInfo &Fragment);
235239
/// Return false if we fail to create the desired !DIExpression.
236240
bool buildDebugInfoExpression(const SILDebugVariable &VarInfo,
237241
SmallVectorImpl<uint64_t> &Operands,
@@ -2928,10 +2932,12 @@ void IRGenDebugInfoImpl::emitOutlinedFunction(IRBuilder &Builder,
29282932
bool IRGenDebugInfoImpl::handleFragmentDIExpr(
29292933
const SILDIExprOperand &CurDIExprOp,
29302934
llvm::DIExpression::FragmentInfo &Fragment) {
2935+
if (CurDIExprOp.getOperator() == SILDIExprOperator::TupleFragment)
2936+
return handleTupleFragmentDIExpr(CurDIExprOp, Fragment);
29312937
assert(CurDIExprOp.getOperator() == SILDIExprOperator::Fragment);
29322938
// Expecting a VarDecl that points to a field in an struct
29332939
auto DIExprArgs = CurDIExprOp.args();
2934-
auto *VD = dyn_cast_or_null<VarDecl>(DIExprArgs.size()?
2940+
auto *VD = dyn_cast_or_null<VarDecl>(DIExprArgs.size() ?
29352941
DIExprArgs[0].getAsDecl() : nullptr);
29362942
assert(VD && "Expecting a VarDecl as the operand for "
29372943
"DIExprOperator::Fragment");
@@ -2965,6 +2971,39 @@ bool IRGenDebugInfoImpl::handleFragmentDIExpr(
29652971
return true;
29662972
}
29672973

2974+
bool IRGenDebugInfoImpl::handleTupleFragmentDIExpr(
2975+
const SILDIExprOperand &CurDIExprOp,
2976+
llvm::DIExpression::FragmentInfo &Fragment) {
2977+
assert(CurDIExprOp.getOperator() == SILDIExprOperator::TupleFragment);
2978+
// Expecting a TupleType followed by an index
2979+
auto DIExprArgs = CurDIExprOp.args();
2980+
assert(DIExprArgs.size() >= 2 && "Expecting two arguments for "
2981+
"DIExprOperator::TupleFragment");
2982+
auto *TT = dyn_cast<TupleType>(DIExprArgs[0].getAsType().getPointer());
2983+
assert(TT && "Expecting a TupleType as the first operand for "
2984+
"DIExprOperator::TupleFragment");
2985+
auto Idx = DIExprArgs[1].getAsConstInt();
2986+
assert(Idx && "Expecting an index as the second operand for "
2987+
"DIExprOperator::TupleFragment");
2988+
// Translate the based type
2989+
SILType ParentSILType = IGM.getLoweredType(TT);
2990+
// Retrieve the offset & size of the field
2991+
auto Offset = getFixedTupleElementOffset(IGM, ParentSILType, *Idx);
2992+
auto ElementType = TT->getElement(*Idx).getType()->getCanonicalType();
2993+
llvm::Type *FieldTy = IGM.getStorageTypeForLowered(ElementType);
2994+
// Doesn't support non-fixed type right now
2995+
if (!Offset || !FieldTy)
2996+
return false;
2997+
2998+
uint64_t SizeInBits = IGM.DataLayout.getTypeSizeInBits(FieldTy);
2999+
uint64_t OffsetInBits = Offset->getValueInBits();
3000+
3001+
// Translate to DW_OP_LLVM_fragment operands
3002+
Fragment = {SizeInBits, OffsetInBits};
3003+
3004+
return true;
3005+
}
3006+
29683007
bool IRGenDebugInfoImpl::buildDebugInfoExpression(
29693008
const SILDebugVariable &VarInfo, SmallVectorImpl<uint64_t> &Operands,
29703009
llvm::DIExpression::FragmentInfo &Fragment) {
@@ -2975,6 +3014,7 @@ bool IRGenDebugInfoImpl::buildDebugInfoExpression(
29753014
llvm::DIExpression::FragmentInfo SubFragment = {0, 0};
29763015
switch (ExprOperand.getOperator()) {
29773016
case SILDIExprOperator::Fragment:
3017+
case SILDIExprOperator::TupleFragment:
29783018
if (!handleFragmentDIExpr(ExprOperand, SubFragment))
29793019
return false;
29803020
assert(!Fragment.SizeInBits

lib/SIL/IR/SILDebugInfoExpression.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ const SILDIExprInfo *SILDIExprInfo::get(SILDIExprOperator Op) {
3737
static const std::unordered_map<SILDIExprOperator, SILDIExprInfo> Infos = {
3838
{SILDIExprOperator::Fragment,
3939
{"op_fragment", {SILDIExprElement::DeclKind}}},
40+
{SILDIExprOperator::TupleFragment,
41+
{"op_tuple_fragment",
42+
{SILDIExprElement::TypeKind, SILDIExprElement::ConstIntKind}}},
4043
{SILDIExprOperator::Dereference, {"op_deref", {}}},
4144
{SILDIExprOperator::Plus, {"op_plus", {}}},
4245
{SILDIExprOperator::Minus, {"op_minus", {}}},
@@ -71,3 +74,13 @@ SILDebugInfoExpression SILDebugInfoExpression::createFragment(VarDecl *Field) {
7174
{SILDIExprElement::createOperator(SILDIExprOperator::Fragment),
7275
SILDIExprElement::createDecl(Field)});
7376
}
77+
78+
SILDebugInfoExpression
79+
SILDebugInfoExpression::createTupleFragment(TupleType *TypePtr, unsigned Idx) {
80+
assert(TypePtr);
81+
return SILDebugInfoExpression(
82+
{SILDIExprElement::createOperator(SILDIExprOperator::TupleFragment),
83+
SILDIExprElement::createType(TypePtr),
84+
SILDIExprElement::createConstInt(Idx)});
85+
}
86+

lib/SIL/IR/SILPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,11 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
13881388
*this << V;
13891389
break;
13901390
}
1391+
case SILDIExprElement::TypeKind: {
1392+
const Type TypePtr = Arg.getAsType();
1393+
*this << "$";
1394+
TypePtr->print(PrintState.OS, PrintState.ASTOptions);
1395+
}
13911396
}
13921397
}
13931398
}

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1507,7 +1507,8 @@ bool SILParser::parseSILDebugInfoExpression(SILDebugInfoExpression &DIExpr) {
15071507
SILDIExprOperator::Plus,
15081508
SILDIExprOperator::Minus,
15091509
SILDIExprOperator::ConstUInt,
1510-
SILDIExprOperator::ConstSInt
1510+
SILDIExprOperator::ConstSInt,
1511+
SILDIExprOperator::TupleFragment
15111512
};
15121513

15131514
do {
@@ -1557,6 +1558,18 @@ bool SILParser::parseSILDebugInfoExpression(SILDebugInfoExpression &DIExpr) {
15571558
DIExpr.push_back(NewOperand);
15581559
break;
15591560
}
1561+
case SILDIExprElement::TypeKind: {
1562+
SILType Result;
1563+
if (parseSILType(Result)) {
1564+
P.diagnose(P.Tok.getLoc(), diag::sil_dbg_expr_expect_operand_kind,
1565+
OpText, "type");
1566+
return true;
1567+
}
1568+
auto NewOperand = SILDIExprElement::createType(
1569+
Result.getASTType().getPointer());
1570+
DIExpr.push_back(NewOperand);
1571+
break;
1572+
}
15601573
default:
15611574
P.diagnose(P.Tok.getLoc(), diag::sil_dbg_unknown_expr_part,
15621575
"operand kind");

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1487,7 +1487,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
14871487
require(It != ItEnd && (It++)->getKind() == OpK,
14881488
"di-expression operand kind mismatch");
14891489

1490-
if (Op == SILDIExprOperator::Fragment)
1490+
if (Op == SILDIExprOperator::Fragment ||
1491+
Op == SILDIExprOperator::TupleFragment)
14911492
HasFragment = true;
14921493
else
14931494
require(!HasFragment, "no directive allowed after op_fragment"

lib/SILOptimizer/Transforms/SILSROA.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,19 @@ createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
196196
SILBuilderWithScope B(AI);
197197
SILType Type = AI->getType().getObjectType();
198198

199-
// Intentionally dropping the debug info.
199+
// Intentionally dropping the debug location.
200200
SILLocation Loc = RegularLocation::getAutoGeneratedLocation();
201201
if (TT) {
202-
// TODO: Add op_fragment support for tuple type
203202
for (unsigned EltNo : indices(TT->getElementTypes())) {
203+
std::optional<SILDebugVariable> NewDebugVarInfo =
204+
SILDebugVariable::createFromAllocation(AI);
205+
if (NewDebugVarInfo)
206+
NewDebugVarInfo->DIExpr.append(
207+
SILDebugInfoExpression::createTupleFragment(TT, EltNo));
204208
SILType EltTy = Type.getTupleElementType(EltNo);
205209
NewAllocations.push_back(B.createAllocStack(
206-
Loc, EltTy, {}, AI->hasDynamicLifetime(), AI->isLexical()));
210+
Loc, EltTy, NewDebugVarInfo, AI->hasDynamicLifetime(),
211+
AI->isLexical()));
207212
}
208213
} else {
209214
assert(SD && "SD should not be null since either it or TT must be set at "

test/DebugInfo/sroa_mem2reg_tuple.sil

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
2+
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa -mem2reg %s -o %t.sil
3+
// RUN: %FileCheck --check-prefix=CHECK-MEM2REG %s --input-file=%t.sil
4+
// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -emit-ir %t.sil | %FileCheck --check-prefix=CHECK-IR %s
5+
// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -c %t.sil -o %t.o
6+
// RUN: %llvm-dwarfdump --debug-info %t.o | %FileCheck --check-prefix=CHECK-DWARF %s
7+
sil_stage canonical
8+
9+
import Builtin
10+
import Swift
11+
12+
sil_scope 2 { loc "sroa.swift":1:6 parent @foo : $@convention(thin) (Int, Int) -> Int }
13+
sil_scope 3 { loc "sroa.swift":2:7 parent 2 }
14+
sil_scope 4 { loc "sroa.swift":2:16 parent 2 }
15+
16+
// foo(in_x:in_y:), loc "sroa.swift":1:6, scope 2
17+
sil hidden @foo : $@convention(thin) (Int, Int) -> Int {
18+
bb0(%0 : $Int, %1 : $Int):
19+
debug_value %0 : $Int, let, name "in_x", argno 1, loc "sroa.swift":1:10, scope 2
20+
debug_value %1 : $Int, let, name "in_y", argno 2, loc "sroa.swift":1:21, scope 2
21+
22+
%4 = alloc_stack $(x: Int, y: Int), var, name "my_tup", loc "sroa.swift":2:7, scope 3
23+
// Make sure SROA propagate the debug info to the splitted alloc_stack instructions
24+
// CHECK-SROA: alloc_stack $Builtin.Int64, var
25+
// CHECK-SROA-SAME: (name "my_tup", loc "sroa.swift":2:7
26+
// CHECK-SROA-SAME: type $*(x: Int, y: Int), expr op_tuple_fragment:$(x: Int, y: Int):0:op_fragment:#Int._value
27+
// CHECK-SROA-SAME: loc * "<compiler-generated>":0:0
28+
// CHECK-SROA: alloc_stack $Builtin.Int64, var
29+
// CHECK-SROA-SAME: (name "my_tup", loc "sroa.swift":2:7
30+
// CHECK-SROA-SAME: type $*(x: Int, y: Int), expr op_tuple_fragment:$(x: Int, y: Int):1:op_fragment:#Int._value
31+
// CHECK-SROA-SAME: loc * "<compiler-generated>":0:0
32+
33+
// Make sure the struct fields' SSA values are properly connected to the source variables via op_fragment
34+
%5 = tuple_element_addr %4 : $*(x: Int, y: Int), 0, loc "sroa.swift":2:16, scope 4
35+
%6 = tuple_element_addr %4 : $*(x: Int, y: Int), 1, loc "sroa.swift":2:16, scope 4
36+
%7 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":2:20, scope 4
37+
%8 = struct $Int (%7 : $Builtin.Int64), loc "sroa.swift":2:20, scope 4
38+
// CHECK-MEM2REG: %[[FIELD_X:[0-9]+]] = struct_extract %[[INT_X:[0-9]+]] : $Int, #Int._value, loc "sroa.swift":2:20
39+
// CHECK-MEM2REG: debug_value %[[FIELD_X]] : $Builtin.Int64, var, (name "my_tup", loc "sroa.swift":2:7, scope 3), type $*(x: Int, y: Int), expr op_tuple_fragment:$(x: Int, y: Int):0:op_fragment:#Int._value
40+
store %8 to %5 : $*Int, loc "sroa.swift":2:20, scope 4
41+
%10 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":2:26, scope 4
42+
%11 = struct $Int (%10 : $Builtin.Int64), loc "sroa.swift":2:26, scope 4
43+
// CHECK-MEM2REG: %[[FIELD_Y:[0-9]+]] = struct_extract %[[INT_Y:[0-9]+]] : $Int, #Int._value, loc "sroa.swift":2:26
44+
// CHECK-MEM2REG: debug_value %[[FIELD_Y]] : $Builtin.Int64, var, (name "my_tup", loc "sroa.swift":2:7, scope 3), type $*(x: Int, y: Int), expr op_tuple_fragment:$(x: Int, y: Int):1:op_fragment:#Int._value
45+
store %11 to %6 : $*Int, loc "sroa.swift":2:26, scope 4
46+
47+
%14 = tuple_element_addr %4 : $*(x: Int, y: Int), 0, loc "sroa.swift":3:12, scope 3
48+
store %0 to %14 : $*Int, loc "sroa.swift":3:12, scope 3
49+
%18 = tuple_element_addr %4 : $*(x: Int, y: Int), 1, loc "sroa.swift":4:12, scope 3
50+
store %1 to %18 : $*Int, loc "sroa.swift":4:12, scope 3
51+
52+
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %0
53+
// CHECK-IR-SAME: metadata ![[MY_TUP_MD:[0-9]+]]
54+
// CHECK-IR-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 64)
55+
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %1
56+
// CHECK-IR-SAME: metadata ![[MY_TUP_MD]]
57+
// CHECK-IR-SAME: !DIExpression(DW_OP_LLVM_fragment, 64, 64)
58+
59+
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %0, metadata ![[ARG1_MD:[0-9]+]]
60+
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %1, metadata ![[ARG2_MD:[0-9]+]]
61+
62+
dealloc_stack %4 : $*(x: Int, y: Int), loc "sroa.swift":2:7, scope 3
63+
return %0 : $Int, loc "sroa.swift":5:3, scope 3
64+
} // end sil function 'foo'
65+
66+
67+
// CHECK-IR: ![[ARG1_MD]] = !DILocalVariable(name: "in_x", arg: 1
68+
// CHECK-IR-SAME: line: 1
69+
// CHECK-IR: ![[ARG2_MD]] = !DILocalVariable(name: "in_y", arg: 2
70+
// CHECK-IR-SAME: line: 1
71+
72+
// CHECK-IR: ![[MY_TUP_MD]] = !DILocalVariable(name: "my_tup"
73+
// CHECK-IR-SAME: line: 2
74+
75+
// CHECK-DWARF-LABEL: DW_AT_name ("foo")
76+
// CHECK-DWARF: DW_TAG_variable
77+
// CHECK-DWARF: DW_AT_name ("my_tup")
78+
// Shouldn't be marked artificial
79+
// CHECK-DWARF-NOT: DW_AT_artificial (true)
80+
// CHECK-DWARF: DW_TAG_{{.*}}

0 commit comments

Comments
 (0)