Skip to content

Commit 5a42c27

Browse files
authored
Merge pull request #38378 from mshockwave/dev-sil-diexpression
[DebugInfo] Adding DIExpression into SIL
2 parents f6f1c3e + b363bbd commit 5a42c27

17 files changed

+962
-122
lines changed

docs/SIL.rst

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,7 +3489,7 @@ debug_value
34893489

34903490
::
34913491

3492-
sil-instruction ::= debug_value '[poison]'? sil-operand (',' debug-var-attr)*
3492+
sil-instruction ::= debug_value '[poison]'? sil-operand (',' debug-var-attr)* advanced-debug-var-attr* (',' 'expr' debug-info-expr)?
34933493

34943494
debug_value %1 : $Int
34953495

@@ -3506,10 +3506,26 @@ The operand must have loadable type.
35063506
debug-var-attr ::= 'name' string-literal
35073507
debug-var-attr ::= 'argno' integer-literal
35083508

3509+
::
3510+
3511+
advanced-debug-var-attr ::= '(' 'name' string-literal (',' sil-instruction-source-info)? ')'
3512+
advanced-debug-var-attr ::= 'type' sil-type
3513+
3514+
::
3515+
3516+
debug-info-expr ::= di-expr-operand (':' di-expr-operand)*
3517+
di-expr-operand ::= di-expr-operator (':' sil-operand)*
3518+
di-expr-operator ::= 'op_fragment'
3519+
35093520
There are a number of attributes that provide details about the source
35103521
variable that is being described, including the name of the
35113522
variable. For function and closure arguments ``argno`` is the number
3512-
of the function argument starting with 1.
3523+
of the function argument starting with 1. The advanced debug variable
3524+
attributes represent source locations and type of the source variable
3525+
when it was originally declared. It is useful when we're indirectly
3526+
associating the SSA value with the source variable (via di-expression,
3527+
for example) in which case SSA value's type is different from that of
3528+
source variable.
35133529

35143530
If the '[poison]' flag is set, then all references within this debug
35153531
value will be overwritten with a sentinel at this point in the
@@ -3520,12 +3536,32 @@ generated until OSSA islowered. They are not expected to be serialized
35203536
within the module, and the pipeline is not expected to do any
35213537
significant code motion after lowering.
35223538

3539+
Debug info expression (di-expression) is a powerful method to connect SSA
3540+
value with the source variable in an indirect fashion. For example,
3541+
we can use the ``op_fragment`` operator to specify that the SSA value
3542+
is originated from a struct field inside the source variable (which has
3543+
an aggregate data type). Di-expression in SIL works similarly to LLVM's
3544+
``!DIExpression`` metadata. Where both of them adopt a stack based
3545+
execution model to evaluate the expression. The biggest difference between
3546+
them is that LLVM always represent ``!DIExpression`` elements as 64-bit
3547+
integers, while SIL's di-expression can have elements with various types,
3548+
like AST nodes or strings. Here is an example::
3549+
3550+
struct MyStruct {
3551+
var x: Int
3552+
var y: Int
3553+
}
3554+
...
3555+
debug_value %1 : $Int, var, (name "the_struct", loc "file.swift":8:7), type $MyStruct, expr op_fragment:#MyStruct.y, loc "file.swift":9:4
3556+
3557+
In the snippet above, source variable "the_struct" has an aggregate type ``$MyStruct`` and we use di-expression with ``op_fragment`` operator to associate ``%1`` to the ``y`` member variable inside "the_struct". Note that the extra source location directive follows rigt 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``.
3558+
35233559
debug_value_addr
35243560
````````````````
35253561

35263562
::
35273563

3528-
sil-instruction ::= debug_value_addr sil-operand (',' debug-var-attr)*
3564+
sil-instruction ::= debug_value_addr sil-operand (',' debug-var-attr)* advanced-debug-var-attr* (',' 'expr' debug-info-expr)?
35293565

35303566
debug_value_addr %7 : $*SomeProtocol
35313567

@@ -3534,6 +3570,7 @@ has changed value to the specified operand. The declaration in
35343570
question is identified by the SILLocation attached to the
35353571
debug_value_addr instruction.
35363572

3573+
Note that this instruction can be replaced by ``debug_value`` + di-expression operator that is equivalent to LLVM's ``DW_OP_deref``.
35373574

35383575
Accessing Memory
35393576
~~~~~~~~~~~~~~~~

include/swift/AST/DiagnosticsParse.def

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,15 +589,20 @@ ERROR(sil_missing_substitutions,none,
589589
"missing substitutions", ())
590590
ERROR(sil_too_many_substitutions,none,
591591
"too many substitutions", ())
592-
ERROR(sil_dbg_unknown_key,none,
593-
"unknown key '%0' in debug variable declaration", (StringRef))
594592
ERROR(sil_objc_with_tail_elements,none,
595593
"alloc_ref [objc] cannot have tail allocated elements", ())
596594
ERROR(sil_expected_access_kind,none,
597595
"%0 instruction must have explicit access kind", (StringRef))
598596
ERROR(sil_expected_access_enforcement,none,
599597
"%0 instruction must have explicit access enforcement", (StringRef))
600598

599+
ERROR(sil_dbg_unknown_key,none,
600+
"unknown key '%0' in debug variable declaration", (StringRef))
601+
ERROR(sil_dbg_unknown_expr_part,none,
602+
"unrecognized debug info expression %0", (StringRef))
603+
ERROR(sil_dbg_expr_expect_operand_kind,none,
604+
"operator %0 expects a %1 kind operand here", (StringRef, StringRef))
605+
601606
ERROR(sil_keypath_expected_component_kind,none,
602607
"expected keypath component kind", ())
603608
ERROR(sil_keypath_unknown_component_kind,none,
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//===--- SILDebugInfoExpression.h - DIExpression for SIL --------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// \file
14+
/// This file contains types that model debug info expressions in SIL. Including
15+
/// (debug info) operator and operand.
16+
///
17+
//===----------------------------------------------------------------------===//
18+
#ifndef SWIFT_SIL_DEBUGINFOEXPRESSION_H
19+
#define SWIFT_SIL_DEBUGINFOEXPRESSION_H
20+
#include "swift/AST/Decl.h"
21+
#include "llvm/ADT/ArrayRef.h"
22+
#include "llvm/ADT/Optional.h"
23+
#include "llvm/ADT/iterator_range.h"
24+
#include "llvm/Support/raw_ostream.h"
25+
26+
namespace swift {
27+
class TailAllocatedDebugVariable;
28+
29+
/// Operator in a debug info expression
30+
enum class SILDIExprOperator : unsigned {
31+
INVALID = 0,
32+
/// Dereferences the SSA value
33+
Dereference,
34+
/// Specifies that the SSA value is a fragment (sub-field) of the
35+
/// associated source variable. This operator takes a single
36+
/// VarDecl operand pointing to the field declaration.
37+
/// Note that this directive can only appear at the end of an
38+
/// expression.
39+
Fragment
40+
};
41+
42+
/// Represents a single component in a debug info expression.
43+
/// Including operator and operand.
44+
struct SILDIExprElement {
45+
enum Kind {
46+
/// A di-expression operator
47+
OperatorKind,
48+
/// An operand that has declaration type
49+
DeclKind
50+
};
51+
52+
private:
53+
Kind OpKind;
54+
55+
union {
56+
SILDIExprOperator Operator;
57+
Decl *Declaration;
58+
};
59+
60+
explicit SILDIExprElement(Kind OpK) : OpKind(OpK) {}
61+
62+
public:
63+
Kind getKind() const { return OpKind; }
64+
65+
SILDIExprOperator getAsOperator() const {
66+
return OpKind == OperatorKind ? Operator : SILDIExprOperator::INVALID;
67+
}
68+
69+
Decl *getAsDecl() const { return OpKind == DeclKind ? Declaration : nullptr; }
70+
71+
static SILDIExprElement createOperator(SILDIExprOperator Op) {
72+
SILDIExprElement DIOp(OperatorKind);
73+
DIOp.Operator = Op;
74+
return DIOp;
75+
}
76+
77+
static SILDIExprElement createDecl(Decl *D) {
78+
SILDIExprElement DIOp(DeclKind);
79+
DIOp.Declaration = D;
80+
return DIOp;
81+
}
82+
};
83+
84+
/// For a given SILDIExprOperator, provides information
85+
/// like its textual name and operand types.
86+
struct SILDIExprInfo {
87+
StringRef OpText;
88+
SmallVector<SILDIExprElement::Kind, 2> OperandKinds;
89+
90+
static const SILDIExprInfo *get(SILDIExprOperator Op);
91+
};
92+
93+
/// A DIExpr operand is consisting of a SILDIExprOperator and
94+
/// SILDIExprElement arguments following after.
95+
struct SILDIExprOperand : public llvm::ArrayRef<SILDIExprElement> {
96+
// Reuse all the ctors
97+
using llvm::ArrayRef<SILDIExprElement>::ArrayRef;
98+
99+
SILDIExprOperator getOperator() const {
100+
assert(size() && "empty DIExpr operand");
101+
const SILDIExprElement &First = front();
102+
return First.getAsOperator();
103+
}
104+
105+
size_t getNumArg() const {
106+
assert(size() && "empty DIExpr operand");
107+
return size() - 1;
108+
}
109+
110+
llvm::ArrayRef<SILDIExprElement> args() const {
111+
return drop_front();
112+
}
113+
};
114+
115+
/// Represents a debug info expression in SIL
116+
class SILDebugInfoExpression {
117+
friend class TailAllocatedDebugVariable;
118+
llvm::SmallVector<SILDIExprElement, 2> Elements;
119+
120+
public:
121+
SILDebugInfoExpression() = default;
122+
123+
explicit SILDebugInfoExpression(llvm::ArrayRef<SILDIExprElement> EL)
124+
: Elements(EL.begin(), EL.end()) {}
125+
126+
size_t getNumElements() const { return Elements.size(); }
127+
128+
using iterator = typename decltype(Elements)::iterator;
129+
using const_iterator = typename decltype(Elements)::const_iterator;
130+
131+
iterator element_begin() { return Elements.begin(); }
132+
iterator element_end() { return Elements.end(); }
133+
134+
const_iterator element_begin() const { return Elements.begin(); }
135+
const_iterator element_end() const { return Elements.end(); }
136+
137+
llvm::iterator_range<iterator> elements() {
138+
return llvm::make_range(element_begin(), element_end());
139+
}
140+
141+
llvm::iterator_range<const_iterator> elements() const {
142+
return llvm::make_range(element_begin(), element_end());
143+
}
144+
145+
const SILDIExprElement &getElement(size_t index) const {
146+
assert(index < Elements.size());
147+
return Elements[index];
148+
}
149+
150+
void push_back(const SILDIExprElement &Element) {
151+
Elements.push_back(Element);
152+
}
153+
154+
/// The iterator for SILDIExprOperand
155+
class op_iterator {
156+
friend class SILDebugInfoExpression;
157+
158+
SILDIExprOperand Current;
159+
llvm::ArrayRef<SILDIExprElement> Remain;
160+
161+
void increment();
162+
163+
explicit
164+
op_iterator(llvm::ArrayRef<SILDIExprElement> Remain): Remain(Remain) {
165+
increment();
166+
}
167+
168+
public:
169+
op_iterator() = default;
170+
op_iterator(const op_iterator &) = default;
171+
172+
const SILDIExprOperand &operator*() const { return Current; }
173+
const SILDIExprOperand *operator->() const { return &Current; }
174+
175+
// Pre increment
176+
op_iterator &operator++() {
177+
increment();
178+
return *this;
179+
}
180+
181+
// Post increment
182+
op_iterator operator++(int) {
183+
op_iterator This(*this);
184+
increment();
185+
return This;
186+
}
187+
188+
bool operator==(const op_iterator &Other) const {
189+
return (Current.empty() && Other.Current.empty()) ||
190+
(Current.data() == Other.Current.data() &&
191+
Current.size() == Other.Current.size());
192+
}
193+
bool operator!=(const op_iterator &Other) const {
194+
return !(Other == *this);
195+
}
196+
};
197+
198+
op_iterator operand_begin() const {
199+
return op_iterator(Elements);
200+
}
201+
op_iterator operand_end() const {
202+
return op_iterator(llvm::ArrayRef<SILDIExprElement>{});
203+
}
204+
205+
llvm::iterator_range<op_iterator> operands() const {
206+
return llvm::make_range(operand_begin(), operand_end());
207+
}
208+
209+
/// Return true if this expression is not empty
210+
inline operator bool() const { return Elements.size(); }
211+
};
212+
} // end namespace swift
213+
#endif

0 commit comments

Comments
 (0)