Skip to content

Commit 9f1a8d7

Browse files
committed
[clang] Add "transparent_stepping" attribute
The `transparent_stepping` attribute is intended as a hint for debuggers that this function itself is not interesting, but it calls a function that might be. So, when stepping in arrives at a function with this attribute, debuggers should transparently step-in through it into the functions called by the annotated function (but not by subsequent calls made by those functions), stopping at the first one its normal rules for whether to stop says to stop at - or stepping out again if none qualify. Also, when stepping out arrives at a function with this attribute, the debugger should continue stepping out to its caller. Differential Revision: https://reviews.llvm.org/D146595 (cherry picked from commit 115648d)
1 parent 7895f31 commit 9f1a8d7

File tree

15 files changed

+238
-7
lines changed

15 files changed

+238
-7
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,12 @@ def Artificial : InheritableAttr {
771771
let SimpleHandler = 1;
772772
}
773773

774+
def TransparentStepping: InheritableAttr {
775+
let Spellings = [Clang<"transparent_stepping">];
776+
let Subjects = SubjectList<[Function]>;
777+
let Documentation = [TransparentSteppingDocs];
778+
}
779+
774780
def XRayInstrument : InheritableAttr {
775781
let Spellings = [Clang<"xray_always_instrument">,
776782
Clang<"xray_never_instrument">];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6924,6 +6924,66 @@ As such, this function attribute is currently only supported on X86 targets.
69246924
}];
69256925
}
69266926

6927+
def TransparentSteppingDocs : Documentation {
6928+
let Category = DocCatFunction;
6929+
let Content = [{
6930+
The ``transparent_stepping`` attribute is intended as a hint for debuggers that this
6931+
function itself is not interesting, but it calls a function that might be. So, when
6932+
stepping in arrives at a function with this attribute, debuggers should transparently
6933+
step-in through it into the functions called by the annotated function (but not by
6934+
subsequent calls made by those functions), stopping at the first one its normal rules
6935+
for whether to stop says to stop at - or stepping out again if none qualify. Also, when
6936+
stepping out arrives at a function with this attribute, the debugger should continue
6937+
stepping out to its caller.
6938+
6939+
For example:
6940+
6941+
.. code-block:: c
6942+
6943+
int bar(void) {
6944+
return 42;
6945+
}
6946+
6947+
__attribute__((transparent_stepping))
6948+
int foo(void) {
6949+
return bar();
6950+
}
6951+
6952+
int caller(void) {
6953+
return foo();
6954+
}
6955+
6956+
Stepping into ``foo`` should step directly into ``bar`` instead, and stepping out of ``bar``
6957+
should stop in ``caller``.
6958+
6959+
Functions with the ``transparent_stepping`` attribute can be chained together:
6960+
6961+
.. code-block:: c
6962+
6963+
int baz(void) {
6964+
return 42;
6965+
}
6966+
6967+
__attribute__((transparent_stepping))
6968+
int bar(void) {
6969+
return baz();
6970+
}
6971+
6972+
__attribute__((transparent_stepping))
6973+
int foo(void) {
6974+
return bar();
6975+
}
6976+
6977+
int caller(void) {
6978+
return foo();
6979+
}
6980+
6981+
In this example, stepping into ``foo`` should step directly into ``baz``, and stepping out of
6982+
``baz`` should stop in ``caller``.
6983+
}];
6984+
}
6985+
6986+
69276987
def ReadOnlyPlacementDocs : Documentation {
69286988
let Category = DocCatType;
69296989
let Content = [{This attribute is attached to a structure, class or union declaration.

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
6767
return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
6868
}
6969

70+
static bool getIsTransparentStepping(const Decl *D) {
71+
if (!D)
72+
return false;
73+
return D->hasAttr<TransparentSteppingAttr>();
74+
}
75+
7076
CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
7177
: CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
7278
DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -1882,6 +1888,8 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
18821888
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
18831889
if (CGM.getLangOpts().Optimize)
18841890
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
1891+
if (getIsTransparentStepping(Method))
1892+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
18851893

18861894
// In this debug mode, emit type info for a class when its constructor type
18871895
// info is emitted.
@@ -3809,6 +3817,8 @@ llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
38093817
if (Stub) {
38103818
Flags |= getCallSiteRelatedAttrs();
38113819
SPFlags |= llvm::DISubprogram::SPFlagDefinition;
3820+
if (getIsTransparentStepping(FD))
3821+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
38123822
return DBuilder.createFunction(
38133823
DContext, Name, LinkageName, Unit, Line,
38143824
getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags,
@@ -3958,6 +3968,8 @@ llvm::DISubprogram *CGDebugInfo::getObjCMethodDeclaration(
39583968
if (It == TypeCache.end())
39593969
return nullptr;
39603970
auto *InterfaceType = cast<llvm::DICompositeType>(It->second);
3971+
if (getIsTransparentStepping(D))
3972+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
39613973
llvm::DISubprogram *FD = DBuilder.createFunction(
39623974
InterfaceType, getObjCMethodName(OMD), StringRef(),
39633975
InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags);
@@ -4124,6 +4136,8 @@ void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc,
41244136
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
41254137
if (CGM.getLangOpts().Optimize)
41264138
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
4139+
if (getIsTransparentStepping(D))
4140+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
41274141

41284142
llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs();
41294143
llvm::DISubprogram::DISPFlags SPFlagsForDef =
@@ -4210,6 +4224,9 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
42104224

42114225
llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
42124226
llvm::DISubroutineType *STy = getOrCreateFunctionType(D, FnType, Unit);
4227+
if (getIsTransparentStepping(D))
4228+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
4229+
42134230
llvm::DISubprogram *SP = DBuilder.createFunction(
42144231
FDContext, Name, LinkageName, Unit, LineNo, STy, ScopeLine, Flags,
42154232
SPFlags, TParamsArray.get(), nullptr, nullptr, Annotations);

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6687,6 +6687,12 @@ static void handleSwiftAsyncName(Sema &S, Decl *D, const ParsedAttr &AL) {
66876687
D->addAttr(::new (S.Context) SwiftAsyncNameAttr(S.Context, AL, Name));
66886688
}
66896689

6690+
static void handleTransparentStepping(Sema &S, Decl *D,
6691+
const ParsedAttr &AL) {
6692+
D->addAttr(::new (S.Context)
6693+
TransparentSteppingAttr(S.Context, AL));
6694+
}
6695+
66906696
static void handleSwiftNewType(Sema &S, Decl *D, const ParsedAttr &AL) {
66916697
// Make sure that there is an identifier as the annotation's single argument.
66926698
if (!AL.checkExactlyNumArgs(S, 1))
@@ -8948,6 +8954,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
89488954
case ParsedAttr::AT_NoDebug:
89498955
handleNoDebugAttr(S, D, AL);
89508956
break;
8957+
case ParsedAttr::AT_TransparentStepping:
8958+
handleTransparentStepping(S, D, AL);
8959+
break;
89518960
case ParsedAttr::AT_CmseNSEntry:
89528961
handleCmseNSEntryAttr(S, D, AL);
89538962
break;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
2+
3+
void bar(void) {}
4+
5+
struct A {
6+
[[clang::transparent_stepping()]]
7+
void foo(void) {
8+
bar();
9+
}
10+
};
11+
12+
int main() {
13+
A().foo();
14+
}
15+
16+
// CHECK: DISubprogram(name: "foo"{{.*}} DISPFlagIsTransparentStepping
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
2+
3+
void bar(void) {}
4+
5+
__attribute__((transparent_stepping))
6+
void foo(void) {
7+
bar();
8+
}
9+
10+
// CHECK: DISubprogram(name: "foo"{{.*}} DISPFlagIsTransparentStepping

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
// CHECK-NEXT: Target (SubjectMatchRule_function)
186186
// CHECK-NEXT: TargetClones (SubjectMatchRule_function)
187187
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
188+
// CHECK-NEXT: TransparentStepping (SubjectMatchRule_function)
188189
// CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
189190
// CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local)
190191
// CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %clang_cc1 %s -verify -fsyntax-only
2+
3+
__attribute__((transparent_stepping))
4+
void correct(void) {}
5+
6+
__attribute__((transparent_stepping(1))) // expected-error {{'transparent_stepping' attribute takes no arguments}}
7+
void wrong_arg(void) {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %clang_cc1 %s -verify -fsyntax-only
2+
3+
4+
struct S {
5+
[[clang::transparent_stepping]]
6+
void correct(void) {}
7+
8+
[[clang::transparent_stepping(1)]] // expected-error {{'transparent_stepping' attribute takes no arguments}}
9+
void one_arg(void) {}
10+
};
11+

llvm/include/llvm/IR/DebugInfoFlags.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ HANDLE_DISP_FLAG((1u << 8), MainSubprogram)
9191
// for defaulted functions
9292
HANDLE_DISP_FLAG((1u << 9), Deleted)
9393
HANDLE_DISP_FLAG((1u << 11), ObjCDirect)
94+
HANDLE_DISP_FLAG((1u << 12), IsTransparentStepping)
9495

9596
#ifdef DISP_FLAG_LARGEST_NEEDED
9697
// Intended to be used with ADT/BitmaskEnum.h.
9798
// NOTE: Always must be equal to largest flag, check this when adding new flags.
98-
HANDLE_DISP_FLAG((1 << 11), Largest)
99+
HANDLE_DISP_FLAG((1 << 12), Largest)
99100
#undef DISP_FLAG_LARGEST_NEEDED
100101
#endif
101102

0 commit comments

Comments
 (0)