Skip to content

Commit a9ffc92

Browse files
authored
[SPIR-V] Add pre-headers to loops. (#75844)
This is the first of the 7 steps outlined in #75801. This PR explicitely calls the SimplifyLoops pass. Directly following this pass should follow the 6 others required to structurize the IR. Running this pass could generate empty basic-blocks, which are implicit fallthrough to the successor BB. There was a specific condition in the SPIR-V ISel which handled implicit fallthrough, but it couldn't work on empty basic-blocks. This commits removes the old logic, and adds this new logic, which checks all basic-blocks for implicit fallthroughs, including empty ones. --------- Signed-off-by: Nathan Gauër <[email protected]>
1 parent a831a21 commit a9ffc92

File tree

5 files changed

+118
-3
lines changed

5 files changed

+118
-3
lines changed

llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,40 @@ static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
607607
}
608608
}
609609

610+
static bool isImplicitFallthrough(MachineBasicBlock &MBB) {
611+
if (MBB.empty())
612+
return true;
613+
614+
// Branching SPIR-V intrinsics are not detected by this generic method.
615+
// Thus, we can only trust negative result.
616+
if (!MBB.canFallThrough())
617+
return false;
618+
619+
// Otherwise, we must manually check if we have a SPIR-V intrinsic which
620+
// prevent an implicit fallthrough.
621+
for (MachineBasicBlock::reverse_iterator It = MBB.rbegin(), E = MBB.rend();
622+
It != E; ++It) {
623+
if (isSpvIntrinsic(*It, Intrinsic::spv_switch))
624+
return false;
625+
}
626+
return true;
627+
}
628+
629+
static void removeImplicitFallthroughs(MachineFunction &MF,
630+
MachineIRBuilder MIB) {
631+
// It is valid for MachineBasicBlocks to not finish with a branch instruction.
632+
// In such cases, they will simply fallthrough their immediate successor.
633+
for (MachineBasicBlock &MBB : MF) {
634+
if (!isImplicitFallthrough(MBB))
635+
continue;
636+
637+
assert(std::distance(MBB.successors().begin(), MBB.successors().end()) ==
638+
1);
639+
MIB.setInsertPt(MBB, MBB.end());
640+
MIB.buildBr(**MBB.successors().begin());
641+
}
642+
}
643+
610644
bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
611645
// Initialize the type registry.
612646
const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
@@ -619,6 +653,7 @@ bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
619653
generateAssignInstrs(MF, GR, MIB);
620654
processSwitches(MF, GR, MIB);
621655
processInstrsWithTypeFolding(MF, GR, MIB);
656+
removeImplicitFallthroughs(MF, MIB);
622657

623658
return true;
624659
}

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/MC/TargetRegistry.h"
3030
#include "llvm/Pass.h"
3131
#include "llvm/Target/TargetOptions.h"
32+
#include "llvm/Transforms/Utils.h"
3233
#include <optional>
3334

3435
using namespace llvm;
@@ -151,6 +152,19 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
151152
}
152153

153154
void SPIRVPassConfig::addIRPasses() {
155+
if (TM.getSubtargetImpl()->isVulkanEnv()) {
156+
// Once legalized, we need to structurize the CFG to follow the spec.
157+
// This is done through the following 8 steps.
158+
// TODO(#75801): add the remaining steps.
159+
160+
// 1. Simplify loop for subsequent transformations. After this steps, loops
161+
// have the following properties:
162+
// - loops have a single entry edge (pre-header to loop header).
163+
// - all loop exits are dominated by the loop pre-header.
164+
// - loops have a single back-edge.
165+
addPass(createLoopSimplifyPass());
166+
}
167+
154168
TargetPassConfig::addIRPasses();
155169
addPass(createSPIRVRegularizerPass());
156170
addPass(createSPIRVPrepareFunctionsPass(TM));

llvm/lib/Target/SPIRV/SPIRVUtils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) {
228228
return MI->getOperand(1).getCImm()->getValue().getZExtValue();
229229
}
230230

231-
bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) {
232-
if (auto *GI = dyn_cast<GIntrinsic>(&MI))
231+
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID) {
232+
if (const auto *GI = dyn_cast<GIntrinsic>(&MI))
233233
return GI->is(IntrinsicID);
234234
return false;
235235
}

llvm/lib/Target/SPIRV/SPIRVUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ MachineInstr *getDefInstrMaybeConstant(Register &ConstReg,
7979
uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI);
8080

8181
// Check if MI is a SPIR-V specific intrinsic call.
82-
bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID);
82+
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID);
8383

8484
// Get type of i-th operand of the metadata node.
8585
Type *getMDOperandAsType(const MDNode *N, unsigned I);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; RUN: llc -mtriple=spirv-unknown-unknown -O0 %s -o - | FileCheck %s
2+
3+
; CHECK-DAG: %[[#bool:]] = OpTypeBool
4+
; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
5+
; CHECK-DAG: %[[#uint_0:]] = OpConstant %[[#uint]] 0
6+
7+
define void @main() #1 {
8+
%1 = icmp ne i32 0, 0
9+
br i1 %1, label %l1, label %l2
10+
11+
; CHECK: %[[#cond:]] = OpINotEqual %[[#bool]] %[[#uint_0]] %[[#uint_0]]
12+
; CHECK: OpBranchConditional %[[#cond]] %[[#l1_pre:]] %[[#l2_pre:]]
13+
14+
; CHECK-DAG: %[[#l2_pre]] = OpLabel
15+
; CHECK-NEXT: OpBranch %[[#l2_header:]]
16+
17+
; CHECK-DAG: %[[#l1_pre]] = OpLabel
18+
; CHECK-NEXT: OpBranch %[[#l1_header:]]
19+
20+
l1:
21+
br i1 %1, label %l1_body, label %l1_end
22+
; CHECK-DAG: %[[#l1_header]] = OpLabel
23+
; CHECK-NEXT: OpBranchConditional %[[#cond]] %[[#l1_body:]] %[[#l1_end:]]
24+
25+
l1_body:
26+
br label %l1_continue
27+
; CHECK-DAG: %[[#l1_body]] = OpLabel
28+
; CHECK-NEXT: OpBranch %[[#l1_continue:]]
29+
30+
l1_continue:
31+
br label %l1
32+
; CHECK-DAG: %[[#l1_continue]] = OpLabel
33+
; CHECK-NEXT: OpBranch %[[#l1_header]]
34+
35+
l1_end:
36+
br label %end
37+
; CHECK-DAG: %[[#l1_end]] = OpLabel
38+
; CHECK-NEXT: OpBranch %[[#end:]]
39+
40+
l2:
41+
br i1 %1, label %l2_body, label %l2_end
42+
; CHECK-DAG: %[[#l2_header]] = OpLabel
43+
; CHECK-NEXT: OpBranchConditional %[[#cond]] %[[#l2_body:]] %[[#l2_end:]]
44+
45+
l2_body:
46+
br label %l2_continue
47+
; CHECK-DAG: %[[#l2_body]] = OpLabel
48+
; CHECK-NEXT: OpBranch %[[#l2_continue:]]
49+
50+
l2_continue:
51+
br label %l2
52+
; CHECK-DAG: %[[#l2_continue]] = OpLabel
53+
; CHECK-NEXT: OpBranch %[[#l2_header]]
54+
55+
l2_end:
56+
br label %end
57+
; CHECK-DAG: %[[#l2_end]] = OpLabel
58+
; CHECK-NEXT: OpBranch %[[#end:]]
59+
60+
end:
61+
ret void
62+
; CHECK-DAG: %[[#end]] = OpLabel
63+
; CHECK-NEXT: OpReturn
64+
}
65+
66+
attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" convergent }

0 commit comments

Comments
 (0)