Skip to content

Commit 115648d

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
1 parent b24e290 commit 115648d

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
@@ -6900,6 +6900,66 @@ As such, this function attribute is currently only supported on X86 targets.
69006900
}];
69016901
}
69026902

6903+
def TransparentSteppingDocs : Documentation {
6904+
let Category = DocCatFunction;
6905+
let Content = [{
6906+
The ``transparent_stepping`` attribute is intended as a hint for debuggers that this
6907+
function itself is not interesting, but it calls a function that might be. So, when
6908+
stepping in arrives at a function with this attribute, debuggers should transparently
6909+
step-in through it into the functions called by the annotated function (but not by
6910+
subsequent calls made by those functions), stopping at the first one its normal rules
6911+
for whether to stop says to stop at - or stepping out again if none qualify. Also, when
6912+
stepping out arrives at a function with this attribute, the debugger should continue
6913+
stepping out to its caller.
6914+
6915+
For example:
6916+
6917+
.. code-block:: c
6918+
6919+
int bar(void) {
6920+
return 42;
6921+
}
6922+
6923+
__attribute__((transparent_stepping))
6924+
int foo(void) {
6925+
return bar();
6926+
}
6927+
6928+
int caller(void) {
6929+
return foo();
6930+
}
6931+
6932+
Stepping into ``foo`` should step directly into ``bar`` instead, and stepping out of ``bar``
6933+
should stop in ``caller``.
6934+
6935+
Functions with the ``transparent_stepping`` attribute can be chained together:
6936+
6937+
.. code-block:: c
6938+
6939+
int baz(void) {
6940+
return 42;
6941+
}
6942+
6943+
__attribute__((transparent_stepping))
6944+
int bar(void) {
6945+
return baz();
6946+
}
6947+
6948+
__attribute__((transparent_stepping))
6949+
int foo(void) {
6950+
return bar();
6951+
}
6952+
6953+
int caller(void) {
6954+
return foo();
6955+
}
6956+
6957+
In this example, stepping into ``foo`` should step directly into ``baz``, and stepping out of
6958+
``baz`` should stop in ``caller``.
6959+
}];
6960+
}
6961+
6962+
69036963
def ReadOnlyPlacementDocs : Documentation {
69046964
let Category = DocCatType;
69056965
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
@@ -69,6 +69,12 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
6969
return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
7070
}
7171

72+
static bool getIsTransparentStepping(const Decl *D) {
73+
if (!D)
74+
return false;
75+
return D->hasAttr<TransparentSteppingAttr>();
76+
}
77+
7278
CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
7379
: CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
7480
DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -1987,6 +1993,8 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
19871993
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
19881994
if (CGM.getLangOpts().Optimize)
19891995
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
1996+
if (getIsTransparentStepping(Method))
1997+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
19901998

19911999
// In this debug mode, emit type info for a class when its constructor type
19922000
// info is emitted.
@@ -3928,6 +3936,8 @@ llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
39283936
if (Stub) {
39293937
Flags |= getCallSiteRelatedAttrs();
39303938
SPFlags |= llvm::DISubprogram::SPFlagDefinition;
3939+
if (getIsTransparentStepping(FD))
3940+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
39313941
return DBuilder.createFunction(
39323942
DContext, Name, LinkageName, Unit, Line,
39333943
getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags,
@@ -4077,6 +4087,8 @@ llvm::DISubprogram *CGDebugInfo::getObjCMethodDeclaration(
40774087
if (It == TypeCache.end())
40784088
return nullptr;
40794089
auto *InterfaceType = cast<llvm::DICompositeType>(It->second);
4090+
if (getIsTransparentStepping(D))
4091+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
40804092
llvm::DISubprogram *FD = DBuilder.createFunction(
40814093
InterfaceType, getObjCMethodName(OMD), StringRef(),
40824094
InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags);
@@ -4244,6 +4256,8 @@ void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc,
42444256
SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit;
42454257
if (CGM.getLangOpts().Optimize)
42464258
SPFlags |= llvm::DISubprogram::SPFlagOptimized;
4259+
if (getIsTransparentStepping(D))
4260+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
42474261

42484262
llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs();
42494263
llvm::DISubprogram::DISPFlags SPFlagsForDef =
@@ -4330,6 +4344,9 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
43304344

43314345
llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
43324346
llvm::DISubroutineType *STy = getOrCreateFunctionType(D, FnType, Unit);
4347+
if (getIsTransparentStepping(D))
4348+
SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping;
4349+
43334350
llvm::DISubprogram *SP = DBuilder.createFunction(
43344351
FDContext, Name, LinkageName, Unit, LineNo, STy, ScopeLine, Flags,
43354352
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
@@ -6773,6 +6773,12 @@ static void handleSwiftAsyncName(Sema &S, Decl *D, const ParsedAttr &AL) {
67736773
D->addAttr(::new (S.Context) SwiftAsyncNameAttr(S.Context, AL, Name));
67746774
}
67756775

6776+
static void handleTransparentStepping(Sema &S, Decl *D,
6777+
const ParsedAttr &AL) {
6778+
D->addAttr(::new (S.Context)
6779+
TransparentSteppingAttr(S.Context, AL));
6780+
}
6781+
67766782
static void handleSwiftNewType(Sema &S, Decl *D, const ParsedAttr &AL) {
67776783
// Make sure that there is an identifier as the annotation's single argument.
67786784
if (!AL.checkExactlyNumArgs(S, 1))
@@ -9085,6 +9091,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
90859091
case ParsedAttr::AT_NoDebug:
90869092
handleNoDebugAttr(S, D, AL);
90879093
break;
9094+
case ParsedAttr::AT_TransparentStepping:
9095+
handleTransparentStepping(S, D, AL);
9096+
break;
90889097
case ParsedAttr::AT_CmseNSEntry:
90899098
handleCmseNSEntryAttr(S, D, AL);
90909099
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
@@ -186,6 +186,7 @@
186186
// CHECK-NEXT: TargetClones (SubjectMatchRule_function)
187187
// CHECK-NEXT: TargetVersion (SubjectMatchRule_function)
188188
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
189+
// CHECK-NEXT: TransparentStepping (SubjectMatchRule_function)
189190
// CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
190191
// CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local)
191192
// CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function)
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)