Skip to content

Commit a883738

Browse files
committed
[KeyInstr][Clang] Ret atom
This patch is part of a stack that teaches Clang to generate Key Instructions metadata for C and C++. The Key Instructions project is introduced, including a "quick summary" section at the top which adds context for this PR, here: https://discourse.llvm.org/t/rfc-improving-is-stmt-placement-for-better-interactive-debugging/82668 The feature is only functional in LLVM if LLVM is built with CMake flag LLVM_EXPERIMENTAL_KEY_INSTRUCTIONs. Eventually that flag will be removed. The Clang-side work is demoed here: #130943
1 parent 8e50e88 commit a883738

File tree

5 files changed

+144
-5
lines changed

5 files changed

+144
-5
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3946,7 +3946,8 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
39463946

39473947
// Functions with no result always return void.
39483948
if (!ReturnValue.isValid()) {
3949-
Builder.CreateRetVoid();
3949+
auto *I = Builder.CreateRetVoid();
3950+
addRetToOverrideOrNewSourceAtom(I, nullptr);
39503951
return;
39513952
}
39523953

@@ -4126,6 +4127,9 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
41264127

41274128
if (RetDbgLoc)
41284129
Ret->setDebugLoc(std::move(RetDbgLoc));
4130+
4131+
llvm::Value *Backup = RV ? Ret->getOperand(0) : nullptr;
4132+
addRetToOverrideOrNewSourceAtom(cast<llvm::ReturnInst>(Ret), Backup);
41294133
}
41304134

41314135
void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) {

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,7 @@ static bool isSwiftAsyncCallee(const CallExpr *CE) {
15891589
/// if the function returns void, or may be missing one if the function returns
15901590
/// non-void. Fun stuff :).
15911591
void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
1592+
ApplyAtomGroup Grp(getDebugInfo());
15921593
if (requiresReturnValueCheck()) {
15931594
llvm::Constant *SLoc = EmitCheckSourceLocation(S.getBeginLoc());
15941595
auto *SLocPtr =
@@ -1598,6 +1599,7 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
15981599
CGM.getSanitizerMetadata()->disableSanitizerForGlobal(SLocPtr);
15991600
assert(ReturnLocation.isValid() && "No valid return location");
16001601
Builder.CreateStore(SLocPtr, ReturnLocation);
1602+
//*OCH?*//
16011603
}
16021604

16031605
// Returning from an outlined SEH helper is UB, and we already warn on it.
@@ -1664,16 +1666,19 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
16641666
// If this function returns a reference, take the address of the expression
16651667
// rather than the value.
16661668
RValue Result = EmitReferenceBindingToExpr(RV);
1667-
Builder.CreateStore(Result.getScalarVal(), ReturnValue);
1669+
auto *I = Builder.CreateStore(Result.getScalarVal(), ReturnValue);
1670+
addInstToCurrentSourceAtom(I, I->getValueOperand());
16681671
} else {
16691672
switch (getEvaluationKind(RV->getType())) {
16701673
case TEK_Scalar: {
16711674
llvm::Value *Ret = EmitScalarExpr(RV);
1672-
if (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect)
1675+
if (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect) {
16731676
EmitStoreOfScalar(Ret, MakeAddrLValue(ReturnValue, RV->getType()),
16741677
/*isInit*/ true);
1675-
else
1676-
Builder.CreateStore(Ret, ReturnValue);
1678+
} else {
1679+
auto *I = Builder.CreateStore(Ret, ReturnValue);
1680+
addInstToCurrentSourceAtom(I, I->getValueOperand());
1681+
}
16771682
break;
16781683
}
16791684
case TEK_Complex:

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,15 @@ llvm::DebugLoc CodeGenFunction::EmitReturnBlock() {
342342
// later by the actual 'ret' instruction.
343343
llvm::DebugLoc Loc = BI->getDebugLoc();
344344
Builder.SetInsertPoint(BI->getParent());
345+
346+
// Key Instructions: If there's only one `ret` then we want to put the
347+
// instruction in the same source atom group as the store to the ret-value
348+
// alloca and unconditional `br` to the return block that we're about to
349+
// delete. It all comes from the same source (`return (value)`).
350+
if (auto *DI = getDebugInfo(); DI && BI->getDebugLoc())
351+
DI->setRetInstSourceAtomOverride(
352+
BI->getDebugLoc().get()->getAtomGroup());
353+
345354
BI->eraseFromParent();
346355
delete ReturnBlock.getBlock();
347356
ReturnBlock = JumpDest();
@@ -1550,6 +1559,12 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
15501559
Bypasses.Init(CGM, Body);
15511560
}
15521561

1562+
// Finalize function debug info on exit.
1563+
auto Cleanup = llvm::make_scope_exit([this] {
1564+
if (CGDebugInfo *DI = getDebugInfo())
1565+
DI->completeFunction();
1566+
});
1567+
15531568
// Emit the standard function prologue.
15541569
StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin());
15551570

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang -gkey-instructions -gno-column-info -x c++ %s -gmlt -S -emit-llvm -o - \
2+
// RUN: | FileCheck %s --implicit-check-not atomGroup --implicit-check-not atomRank
3+
4+
// RUN: %clang -gkey-instructions -gno-column-info -x c %s -gmlt -S -emit-llvm -o - \
5+
// RUN: | FileCheck %s --implicit-check-not atomGroup --implicit-check-not atomRank
6+
7+
typedef struct {
8+
struct{} a;
9+
double b;
10+
} s1;
11+
12+
s1 f(int z, ...) {
13+
__builtin_va_list list;
14+
__builtin_va_start(list, z);
15+
// CHECK: vaarg.end:
16+
// CHECK-NEXT: %vaarg.addr = phi ptr
17+
// CHECK-NEXT: call void @llvm.memcpy{{.*}}, !dbg [[G1R1:!.*]]
18+
// CHECK-NEXT: {{.*}} = getelementptr{{.*}}
19+
// CHECK-NEXT: [[LOAD:%.*]] = load double{{.*}}, !dbg [[G1R2:!.*]]
20+
// CHECK-NEXT: ret double [[LOAD]], !dbg [[G1R1]]
21+
return __builtin_va_arg(list, s1);
22+
}
23+
24+
// CHECK: [[G1R1]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 1)
25+
// CHECK: [[G1R2]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 2)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %clang -gkey-instructions -gno-column-info -x c++ %s -gmlt -S -emit-llvm -o - \
2+
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-CXX
3+
4+
// RUN: %clang -gkey-instructions -gno-column-info -x c %s -gmlt -S -emit-llvm -o - \
5+
// RUN: | FileCheck %s
6+
7+
// Check the stores to `retval` allocas and branches to `return` block are in
8+
// the same atom group. They are both rank 1, which could in theory introduce
9+
// an extra step in some optimized code. This low risk currently feels an
10+
// acceptable for keeping the code a bit simpler (as opposed to adding
11+
// scaffolding to make the store rank 2).
12+
13+
// Also check that in the case of a single return (no control flow) the
14+
// return instruction inherits the atom group of the branch to the return
15+
// block when the blocks get folded togather.
16+
17+
#ifdef __cplusplus
18+
#define nomangle extern "C"
19+
#else
20+
#define nomangle
21+
#endif
22+
23+
int g;
24+
nomangle float a() {
25+
// CHECK: float @a()
26+
if (g)
27+
// CHECK: if.then:
28+
// CHECK-NEXT: %1 = load i32, ptr @g{{.*}}, !dbg [[G2R3:!.*]]
29+
// CHECK-NEXT: %conv = sitofp i32 %1 to float{{.*}}, !dbg [[G2R2:!.*]]
30+
// CHECK-NEXT: store float %conv, ptr %retval{{.*}}, !dbg [[G2R1:!.*]]
31+
// CHECK-NEXT: br label %return{{.*}}, !dbg [[G2R1]]
32+
return g;
33+
// CHECK: if.end:
34+
// CHECK-NEXT: store float 1.000000e+00, ptr %retval{{.*}}, !dbg [[G3R1:!.*]]
35+
// CHECK-NEXT: br label %return, !dbg [[G3R1]]
36+
37+
// CHECK: return:
38+
// CHECK-NEXT: %2 = load float, ptr %retval{{.*}}, !dbg [[G4R2:!.*]]
39+
// CHECK-NEXT: ret float %2{{.*}}, !dbg [[G4R1:!.*]]
40+
return 1;
41+
}
42+
43+
// CHECK: void @b()
44+
// CHECK: ret void{{.*}}, !dbg [[B_G1R1:!.*]]
45+
nomangle void b() { return; }
46+
47+
// CHECK: i32 @c()
48+
// CHECK: %add = add{{.*}}, !dbg [[C_G1R2:!.*]]
49+
// CHECK: ret i32 %add{{.*}}, !dbg [[C_G1R1:!.*]]
50+
nomangle int c() { return g + 1; }
51+
52+
// NOTE: (return) (g = 1) are two separate atoms.
53+
// CHECK: i32 @d()
54+
// CHECK: store{{.*}}, !dbg [[D_G2R1:!.*]]
55+
// CHECK: ret i32 1{{.*}}, !dbg [[D_G1R1:!.*]]
56+
nomangle int d() { return g = 1; }
57+
58+
// The implicit return here get the line number of the closing brace; make it
59+
// key to match existing behaviour.
60+
// CHECK: ret void, !dbg [[E_G1R1:!.*]]
61+
nomangle void e() {}
62+
63+
#ifdef __cplusplus
64+
// CHECK-CXX: ptr @_Z1fRi
65+
int &f(int &r) {
66+
// Include ctrl-flow to stop ret value store being elided.
67+
if (r)
68+
// CHECK-CXX: if.then:
69+
// CHECK-CXX-NEXT: %2 = load ptr, ptr %r.addr{{.*}}, !dbg [[F_G2R2:!.*]]
70+
// CHECK-CXX-NEXT: store ptr %2, ptr %retval{{.*}}, !dbg [[F_G2R1:!.*]]
71+
// CHECK-CXX-NEXT: br label %return, !dbg [[F_G2R1:!.*]]
72+
return r;
73+
return g;
74+
}
75+
#endif
76+
77+
// CHECK: [[G2R3]] = !DILocation({{.*}}, atomGroup: 2, atomRank: 3)
78+
// CHECK: [[G2R2]] = !DILocation({{.*}}, atomGroup: 2, atomRank: 2)
79+
// CHECK: [[G2R1]] = !DILocation({{.*}}, atomGroup: 2, atomRank: 1)
80+
// CHECK: [[G3R1]] = !DILocation({{.*}}, atomGroup: 3, atomRank: 1)
81+
// CHECK: [[G4R2]] = !DILocation({{.*}}, atomGroup: 4, atomRank: 2)
82+
// CHECK: [[G4R1]] = !DILocation({{.*}}, atomGroup: 4, atomRank: 1)
83+
// CHECK: [[B_G1R1]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 1)
84+
// CHECK: [[C_G1R2]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 2)
85+
// CHECK: [[C_G1R1]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 1)
86+
// CHECK: [[D_G2R1]] = !DILocation({{.*}}, atomGroup: 2, atomRank: 1)
87+
// CHECK: [[D_G1R1]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 1)
88+
// CHECK: [[E_G1R1]] = !DILocation({{.*}}, atomGroup: 1, atomRank: 1)
89+
// CHECK-CXX: [[F_G2R2]] = !DILocation({{.*}}, atomGroup: 2, atomRank: 2)
90+
// CHECK-CXX: [[F_G2R1]] = !DILocation({{.*}}, atomGroup: 2, atomRank: 1)

0 commit comments

Comments
 (0)