Skip to content

Commit 094190c

Browse files
[llvm][CallBrPrepare] add llvm.callbr.landingpad intrinsic
Insert a new intrinsic call after splitting critical edges, and verify it. Later commits will update the SSA values to use this new value along indirect branches rather than the callbr's value, and have SelectionDAG consume this new value. Part 2b of https://discourse.llvm.org/t/rfc-syncing-asm-goto-with-outputs-with-gcc/65453/8. Reviewed By: efriedma, jyknight Differential Revision: https://reviews.llvm.org/D139883
1 parent 0a39af0 commit 094190c

File tree

6 files changed

+170
-7
lines changed

6 files changed

+170
-7
lines changed

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,11 @@ def int_call_preallocated_setup : DefaultAttrsIntrinsic<[llvm_token_ty], [llvm_i
620620
def int_call_preallocated_arg : DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_i32_ty]>;
621621
def int_call_preallocated_teardown : DefaultAttrsIntrinsic<[], [llvm_token_ty]>;
622622

623+
// This intrinsic is intentionally undocumented and users shouldn't call it;
624+
// it's produced then quickly consumed during codegen.
625+
def int_callbr_landingpad : Intrinsic<[llvm_any_ty], [LLVMMatchType<0>],
626+
[IntrNoMerge]>;
627+
623628
//===------------------- Standard C Library Intrinsics --------------------===//
624629
//
625630

llvm/lib/CodeGen/CallBrPrepare.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@
3232
//===----------------------------------------------------------------------===//
3333

3434
#include "llvm/ADT/ArrayRef.h"
35+
#include "llvm/ADT/SmallPtrSet.h"
3536
#include "llvm/ADT/SmallVector.h"
3637
#include "llvm/Analysis/CFG.h"
3738
#include "llvm/CodeGen/Passes.h"
3839
#include "llvm/IR/BasicBlock.h"
3940
#include "llvm/IR/Dominators.h"
4041
#include "llvm/IR/Function.h"
42+
#include "llvm/IR/IRBuilder.h"
4143
#include "llvm/IR/Instructions.h"
44+
#include "llvm/IR/IntrinsicInst.h"
45+
#include "llvm/IR/Intrinsics.h"
4246
#include "llvm/InitializePasses.h"
4347
#include "llvm/Pass.h"
4448
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
@@ -104,6 +108,23 @@ bool CallBrPrepare::SplitCriticalEdges(ArrayRef<CallBrInst *> CBRs,
104108
return Changed;
105109
}
106110

111+
static bool InsertIntrinsicCalls(ArrayRef<CallBrInst *> CBRs) {
112+
bool Changed = false;
113+
SmallPtrSet<const BasicBlock *, 4> Visited;
114+
IRBuilder<> Builder(CBRs[0]->getContext());
115+
for (CallBrInst *CBR : CBRs) {
116+
for (BasicBlock *IndDest : CBR->getIndirectDests()) {
117+
if (!Visited.insert(IndDest).second)
118+
continue;
119+
Builder.SetInsertPoint(&*IndDest->begin());
120+
Builder.CreateIntrinsic(CBR->getType(), Intrinsic::callbr_landingpad,
121+
{CBR});
122+
Changed = true;
123+
}
124+
}
125+
return Changed;
126+
}
127+
107128
bool CallBrPrepare::runOnFunction(Function &Fn) {
108129
bool Changed = false;
109130
SmallVector<CallBrInst *, 2> CBRs = FindCallBrs(Fn);
@@ -130,5 +151,8 @@ bool CallBrPrepare::runOnFunction(Function &Fn) {
130151
if (SplitCriticalEdges(CBRs, *DT))
131152
Changed = true;
132153

154+
if (InsertIntrinsicCalls(CBRs))
155+
Changed = true;
156+
133157
return Changed;
134158
}

llvm/lib/IR/Verifier.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5824,6 +5824,35 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
58245824
"isdata argument to llvm.aarch64.prefetch must be 0 or 1", Call);
58255825
break;
58265826
}
5827+
case Intrinsic::callbr_landingpad: {
5828+
const auto *CBR = dyn_cast<CallBrInst>(Call.getOperand(0));
5829+
Check(CBR, "intrinstic requires callbr operand", &Call);
5830+
if (!CBR)
5831+
break;
5832+
5833+
const BasicBlock *LandingPadBB = Call.getParent();
5834+
const BasicBlock *PredBB = LandingPadBB->getUniquePredecessor();
5835+
if (!PredBB) {
5836+
CheckFailed("Intrinsic in block must have 1 unique predecessor", &Call);
5837+
break;
5838+
}
5839+
if (!isa<CallBrInst>(PredBB->getTerminator())) {
5840+
CheckFailed("Intrinsic must have corresponding callbr in predecessor",
5841+
&Call);
5842+
break;
5843+
}
5844+
Check(llvm::any_of(CBR->getIndirectDests(),
5845+
[LandingPadBB](const BasicBlock *IndDest) {
5846+
return IndDest == LandingPadBB;
5847+
}),
5848+
"Intrinsic's corresponding callbr must have intrinsic's parent basic "
5849+
"block in indirect destination list",
5850+
&Call);
5851+
const Instruction &First = *LandingPadBB->begin();
5852+
Check(&First == &Call, "No other instructions may proceed intrinsic",
5853+
&Call);
5854+
break;
5855+
}
58275856
};
58285857

58295858
// Verify that there aren't any unmediated control transfers between funclets.

llvm/test/CodeGen/AArch64/callbr-prepare.ll

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ define i32 @test0() {
77
; CHECK-NEXT: [[OUT:%.*]] = callbr i32 asm "# $0", "=r,!i"()
88
; CHECK-NEXT: to label [[DIRECT:%.*]] [label %entry.indirect_crit_edge]
99
; CHECK: entry.indirect_crit_edge:
10+
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[OUT]])
1011
; CHECK-NEXT: br label [[INDIRECT:%.*]]
1112
; CHECK: direct:
1213
; CHECK-NEXT: [[OUT2:%.*]] = callbr i32 asm "# $0", "=r,!i"()
1314
; CHECK-NEXT: to label [[DIRECT2:%.*]] [label %direct.indirect_crit_edge]
1415
; CHECK: direct.indirect_crit_edge:
16+
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[OUT2]])
1517
; CHECK-NEXT: br label [[INDIRECT]]
1618
; CHECK: direct2:
1719
; CHECK-NEXT: ret i32 0
@@ -67,6 +69,7 @@ define i32 @dont_split1() {
6769
; CHECK: x:
6870
; CHECK-NEXT: ret i32 42
6971
; CHECK: y:
72+
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
7073
; CHECK-NEXT: ret i32 [[TMP0]]
7174
;
7275
entry:
@@ -138,12 +141,13 @@ define i32 @split_me0() {
138141
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
139142
; CHECK-NEXT: to label [[X:%.*]] [label %entry.y_crit_edge]
140143
; CHECK: entry.y_crit_edge:
144+
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
141145
; CHECK-NEXT: br label [[Y:%.*]]
142146
; CHECK: x:
143147
; CHECK-NEXT: br label [[Y]]
144148
; CHECK: y:
145-
; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[TMP0]], [[ENTRY_Y_CRIT_EDGE:%.*]] ], [ 42, [[X]] ]
146-
; CHECK-NEXT: ret i32 [[TMP1]]
149+
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP0]], [[ENTRY_Y_CRIT_EDGE:%.*]] ], [ 42, [[X]] ]
150+
; CHECK-NEXT: ret i32 [[TMP2]]
147151
;
148152
entry:
149153
%0 = callbr i32 asm "", "=r,!i"()
@@ -168,12 +172,13 @@ define i32 @split_me1(i1 %z) {
168172
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i,!i"()
169173
; CHECK-NEXT: to label [[X:%.*]] [label [[W_V_CRIT_EDGE:%.*]], label %w.v_crit_edge]
170174
; CHECK: w.v_crit_edge:
175+
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
171176
; CHECK-NEXT: br label [[V]]
172177
; CHECK: x:
173178
; CHECK-NEXT: ret i32 42
174179
; CHECK: v:
175-
; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[TMP0]], [[W_V_CRIT_EDGE]] ], [ undef, [[ENTRY:%.*]] ]
176-
; CHECK-NEXT: ret i32 [[TMP1]]
180+
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP0]], [[W_V_CRIT_EDGE]] ], [ undef, [[ENTRY:%.*]] ]
181+
; CHECK-NEXT: ret i32 [[TMP2]]
177182
;
178183
entry:
179184
br i1 %z, label %w, label %v
@@ -200,12 +205,13 @@ define i32 @split_me2(i1 %z) {
200205
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i,!i"()
201206
; CHECK-NEXT: to label [[X:%.*]] [label [[W_V_CRIT_EDGE:%.*]], label %w.v_crit_edge]
202207
; CHECK: w.v_crit_edge:
208+
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
203209
; CHECK-NEXT: br label [[V]]
204210
; CHECK: x:
205211
; CHECK-NEXT: ret i32 42
206212
; CHECK: v:
207-
; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[TMP0]], [[W_V_CRIT_EDGE]] ], [ 42, [[ENTRY:%.*]] ]
208-
; CHECK-NEXT: ret i32 [[TMP1]]
213+
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP0]], [[W_V_CRIT_EDGE]] ], [ 42, [[ENTRY:%.*]] ]
214+
; CHECK-NEXT: ret i32 [[TMP2]]
209215
;
210216
entry:
211217
br i1 %z, label %w, label %v

llvm/test/Transforms/SimplifyCFG/callbr-destinations.ll

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2-
; RUN: opt < %s -S -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 | FileCheck %s
2+
; RUN: opt < %s -S -passes="simplifycfg<sink-common-insts>" \
3+
; RUN: -simplifycfg-require-and-preserve-domtree=1 | FileCheck %s
34

45
define void @callbr_duplicate_dest() {
56
; CHECK-LABEL: @callbr_duplicate_dest(
@@ -57,3 +58,49 @@ bb2:
5758
bb3:
5859
ret void
5960
}
61+
62+
; Validate that callbr landingpad intrinsics do not get merged (via the
63+
; IntrNoMerge attribute).
64+
define i32 @callbr_landingpad_nomerge() {
65+
; CHECK-LABEL: @callbr_landingpad_nomerge(
66+
; CHECK-NEXT: entry:
67+
; CHECK-NEXT: [[OUT:%.*]] = callbr i32 asm "# $0", "=r,!i"()
68+
; CHECK-NEXT: to label [[DIRECT:%.*]] [label %entry.indirect_crit_edge]
69+
; CHECK: entry.indirect_crit_edge:
70+
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[OUT]])
71+
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
72+
; CHECK: direct:
73+
; CHECK-NEXT: [[OUT2:%.*]] = callbr i32 asm "# $0", "=r,!i"()
74+
; CHECK-NEXT: to label [[COMMON_RET]] [label %direct.indirect_crit_edge]
75+
; CHECK: direct.indirect_crit_edge:
76+
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[OUT2]])
77+
; CHECK-NEXT: br label [[COMMON_RET]]
78+
; CHECK: common.ret:
79+
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i32 [ 0, [[DIRECT]] ], [ [[TMP0]], [[ENTRY_INDIRECT_CRIT_EDGE:%.*]] ], [ [[TMP1]], [[DIRECT_INDIRECT_CRIT_EDGE:%.*]] ]
80+
; CHECK-NEXT: ret i32 [[COMMON_RET_OP]]
81+
;
82+
entry:
83+
%out = callbr i32 asm "# $0", "=r,!i"()
84+
to label %direct [label %entry.indirect_crit_edge]
85+
86+
entry.indirect_crit_edge:
87+
%0 = call i32 @llvm.callbr.landingpad.i32(i32 %out)
88+
br label %indirect
89+
90+
direct:
91+
%out2 = callbr i32 asm "# $0", "=r,!i"()
92+
to label %direct2 [label %direct.indirect_crit_edge]
93+
94+
direct.indirect_crit_edge:
95+
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %out2)
96+
br label %indirect
97+
98+
direct2:
99+
ret i32 0
100+
101+
indirect:
102+
%out3 = phi i32 [ %0, %entry.indirect_crit_edge ], [ %1, %direct.indirect_crit_edge ]
103+
ret i32 %out3
104+
}
105+
106+
declare i32 @llvm.callbr.landingpad.i32(i32)

llvm/test/Verifier/callbr.ll

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,55 @@ normal:
6868
abnormal:
6969
ret i32 %ret
7070
}
71+
72+
;; Tests of the callbr.landingpad intrinsic function.
73+
declare i32 @llvm.callbr.landingpad.i64(i64)
74+
define void @callbrpad_bad_type() {
75+
entry:
76+
; CHECK: Intrinsic has incorrect argument type!
77+
; CHECK-NEXT: ptr @llvm.callbr.landingpad.i64
78+
%foo = call i32 @llvm.callbr.landingpad.i64(i64 42)
79+
ret void
80+
}
81+
82+
declare i32 @llvm.callbr.landingpad.i32(i32)
83+
define i32 @callbrpad_multi_preds() {
84+
entry:
85+
%foo = callbr i32 asm "", "=r,!i"() to label %direct [label %indirect]
86+
direct:
87+
br label %indirect
88+
indirect:
89+
; CHECK-NEXT: Intrinsic in block must have 1 unique predecessor
90+
; CHECK-NEXT: %out = call i32 @llvm.callbr.landingpad.i32(i32 %foo)
91+
%out = call i32 @llvm.callbr.landingpad.i32(i32 %foo)
92+
ret i32 %out
93+
}
94+
95+
define void @callbrpad_wrong_callbr() {
96+
entry:
97+
%foo = callbr i32 asm "", "=r,!i"() to label %direct [label %indirect]
98+
direct:
99+
; CHECK-NEXT: Intrinsic's corresponding callbr must have intrinsic's parent basic block in indirect destination list
100+
; CHECK-NEXT: %x = call i32 @llvm.callbr.landingpad.i32(i32 %foo)
101+
%x = call i32 @llvm.callbr.landingpad.i32(i32 %foo)
102+
ret void
103+
indirect:
104+
ret void
105+
}
106+
107+
declare i32 @foo(i32)
108+
define i32 @test_callbr_landingpad_not_first_inst() {
109+
entry:
110+
%0 = callbr i32 asm "", "=r,!i"()
111+
to label %asm.fallthrough [label %landingpad]
112+
113+
asm.fallthrough:
114+
ret i32 42
115+
116+
landingpad:
117+
%foo = call i32 @foo(i32 42)
118+
; CHECK-NEXT: No other instructions may proceed intrinsic
119+
; CHECK-NEXT: %out = call i32 @llvm.callbr.landingpad.i32(i32 %0)
120+
%out = call i32 @llvm.callbr.landingpad.i32(i32 %0)
121+
ret i32 %out
122+
}

0 commit comments

Comments
 (0)