Skip to content

Commit 0e1037e

Browse files
authored
[SPIR-V] Strip convergence intrinsics before ISel (#75948)
The structurizer will require the frontend to emit convergence intrinsics. Once uses to restructurize the control-flow, those intrinsics shall be removed, as they cannot be converted to SPIR-V. This commit adds a new pass to the SPIR-V backend which strips those intrinsics. Those 2 new steps are not limited to Vulkan as OpenCL could also benefit from not crashing if a convertent operation is in the IR (even though the frontend doesn't generate such intrinsics). Signed-off-by: Nathan Gauër <[email protected]>
1 parent 019ffbf commit 0e1037e

File tree

5 files changed

+104
-0
lines changed

5 files changed

+104
-0
lines changed

llvm/lib/Target/SPIRV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_llvm_target(SPIRVCodeGen
2222
SPIRVGlobalRegistry.cpp
2323
SPIRVInstrInfo.cpp
2424
SPIRVInstructionSelector.cpp
25+
SPIRVStripConvergentIntrinsics.cpp
2526
SPIRVISelLowering.cpp
2627
SPIRVLegalizerInfo.cpp
2728
SPIRVMCInstLower.cpp

llvm/lib/Target/SPIRV/SPIRV.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class InstructionSelector;
2020
class RegisterBankInfo;
2121

2222
ModulePass *createSPIRVPrepareFunctionsPass(const SPIRVTargetMachine &TM);
23+
FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
2324
FunctionPass *createSPIRVRegularizerPass();
2425
FunctionPass *createSPIRVPreLegalizerPass();
2526
FunctionPass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===-- SPIRVStripConvergentIntrinsics.cpp ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This pass trims convergence intrinsics as those were only useful when
10+
// modifying the CFG during IR passes.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "SPIRV.h"
15+
#include "SPIRVSubtarget.h"
16+
#include "SPIRVTargetMachine.h"
17+
#include "SPIRVUtils.h"
18+
#include "llvm/CodeGen/IntrinsicLowering.h"
19+
#include "llvm/IR/IRBuilder.h"
20+
#include "llvm/IR/IntrinsicInst.h"
21+
#include "llvm/IR/Intrinsics.h"
22+
#include "llvm/IR/IntrinsicsSPIRV.h"
23+
#include "llvm/Transforms/Utils/Cloning.h"
24+
#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
25+
26+
using namespace llvm;
27+
28+
namespace llvm {
29+
void initializeSPIRVStripConvergentIntrinsicsPass(PassRegistry &);
30+
}
31+
32+
class SPIRVStripConvergentIntrinsics : public FunctionPass {
33+
public:
34+
static char ID;
35+
36+
SPIRVStripConvergentIntrinsics() : FunctionPass(ID) {
37+
initializeSPIRVStripConvergentIntrinsicsPass(
38+
*PassRegistry::getPassRegistry());
39+
};
40+
41+
virtual bool runOnFunction(Function &F) override {
42+
DenseSet<Instruction *> ToRemove;
43+
44+
for (BasicBlock &BB : F) {
45+
for (Instruction &I : BB) {
46+
if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
47+
if (II->getIntrinsicID() !=
48+
Intrinsic::experimental_convergence_entry &&
49+
II->getIntrinsicID() !=
50+
Intrinsic::experimental_convergence_loop &&
51+
II->getIntrinsicID() !=
52+
Intrinsic::experimental_convergence_anchor) {
53+
continue;
54+
}
55+
56+
II->replaceAllUsesWith(UndefValue::get(II->getType()));
57+
ToRemove.insert(II);
58+
} else if (auto *CI = dyn_cast<CallInst>(&I)) {
59+
auto OB = CI->getOperandBundle(LLVMContext::OB_convergencectrl);
60+
if (!OB.has_value())
61+
continue;
62+
63+
auto *NewCall = CallBase::removeOperandBundle(
64+
CI, LLVMContext::OB_convergencectrl, CI);
65+
NewCall->copyMetadata(*CI);
66+
CI->replaceAllUsesWith(NewCall);
67+
ToRemove.insert(CI);
68+
}
69+
}
70+
}
71+
72+
// All usages must be removed before their definition is removed.
73+
for (Instruction *I : ToRemove)
74+
I->eraseFromParent();
75+
76+
return ToRemove.size() != 0;
77+
}
78+
};
79+
80+
char SPIRVStripConvergentIntrinsics::ID = 0;
81+
INITIALIZE_PASS(SPIRVStripConvergentIntrinsics, "strip-convergent-intrinsics",
82+
"SPIRV strip convergent intrinsics", false, false)
83+
84+
FunctionPass *llvm::createSPIRVStripConvergenceIntrinsicsPass() {
85+
return new SPIRVStripConvergentIntrinsics();
86+
}

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ void SPIRVPassConfig::addIRPasses() {
168168
TargetPassConfig::addIRPasses();
169169
addPass(createSPIRVRegularizerPass());
170170
addPass(createSPIRVPrepareFunctionsPass(TM));
171+
addPass(createSPIRVStripConvergenceIntrinsicsPass());
171172
}
172173

173174
void SPIRVPassConfig::addISelPrepare() {

llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
; CHECK-DAG: %[[#bool:]] = OpTypeBool
44
; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
55
; CHECK-DAG: %[[#uint_0:]] = OpConstant %[[#uint]] 0
6+
; CHECK-DAG: OpName %[[#main:]] "main"
67

78
define void @main() #1 {
89
%1 = icmp ne i32 0, 0
10+
%t1 = call token @llvm.experimental.convergence.entry()
911
br i1 %1, label %l1, label %l2
1012

13+
; CHECK: %[[#main]] = OpFunction
1114
; CHECK: %[[#cond:]] = OpINotEqual %[[#bool]] %[[#uint_0]] %[[#uint_0]]
1215
; CHECK: OpBranchConditional %[[#cond]] %[[#l1_pre:]] %[[#l2_pre:]]
1316

@@ -18,6 +21,7 @@ define void @main() #1 {
1821
; CHECK-NEXT: OpBranch %[[#l1_header:]]
1922

2023
l1:
24+
%tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t1) ]
2125
br i1 %1, label %l1_body, label %l1_end
2226
; CHECK-DAG: %[[#l1_header]] = OpLabel
2327
; CHECK-NEXT: OpBranchConditional %[[#cond]] %[[#l1_body:]] %[[#l1_end:]]
@@ -33,11 +37,14 @@ l1_continue:
3337
; CHECK-NEXT: OpBranch %[[#l1_header]]
3438

3539
l1_end:
40+
%call = call spir_func i32 @_Z3absi(i32 0) [ "convergencectrl"(token %tl1) ]
3641
br label %end
3742
; CHECK-DAG: %[[#l1_end]] = OpLabel
43+
; CHECK-DAG: %[[#]] = OpFunctionCall
3844
; CHECK-NEXT: OpBranch %[[#end:]]
3945

4046
l2:
47+
%tl2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t1) ]
4148
br i1 %1, label %l2_body, label %l2_end
4249
; CHECK-DAG: %[[#l2_header]] = OpLabel
4350
; CHECK-NEXT: OpBranchConditional %[[#cond]] %[[#l2_body:]] %[[#l2_end:]]
@@ -64,3 +71,11 @@ end:
6471
}
6572

6673
attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" convergent }
74+
75+
declare token @llvm.experimental.convergence.entry()
76+
declare token @llvm.experimental.convergence.control()
77+
declare token @llvm.experimental.convergence.loop()
78+
79+
; This intrinsic is not convergent. This is only because the backend doesn't
80+
; support convergent operations yet.
81+
declare spir_func i32 @_Z3absi(i32) convergent

0 commit comments

Comments
 (0)