[llvm] [SPIR-V] Strip convergence intrinsics before ISel (PR #75948)

Nathan Gauër via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 2 02:57:28 PST 2024


https://github.com/Keenuts updated https://github.com/llvm/llvm-project/pull/75948

>From 8bdf4bf33f3349d333ccf62e6d18925018a81b7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Mon, 18 Dec 2023 19:51:14 +0100
Subject: [PATCH 1/6] [SPIR-V] Add pre-headers to loops.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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 <brioche at google.com>
---
 llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp   | 35 ++++++++++
 llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp  |  6 ++
 llvm/lib/Target/SPIRV/SPIRVUtils.cpp          |  4 +-
 llvm/lib/Target/SPIRV/SPIRVUtils.h            |  2 +-
 .../CodeGen/SPIRV/scfg-add-pre-headers.ll     | 67 +++++++++++++++++++
 5 files changed, 111 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index f4076be2a7b778..275c24e22c3025 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -587,6 +587,40 @@ static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
   }
 }
 
+static bool isImplicitFallthrough(MachineBasicBlock &MBB) {
+  if (MBB.empty())
+    return true;
+
+  // Branching spirv intrinsics are not detected by this generic method.
+  // Thus, we can only trust negative result.
+  if (!MBB.canFallThrough())
+    return false;
+
+  // Otherwise, we must manually check if we have a spirv intrinsic which
+  // prevent an implicit fallthrough.
+  for (MachineBasicBlock::reverse_iterator It = MBB.rbegin(), E = MBB.rend();
+       It != E; ++It) {
+    if (isSpvIntrinsic(*It, Intrinsic::spv_switch))
+      return false;
+  }
+  return true;
+}
+
+static void removeImplicitFallthroughs(MachineFunction &MF,
+                                       MachineIRBuilder MIB) {
+  // It is valid for MachineBasicBlocks to not finish with a branch instruction.
+  // In such cases, they will simply fallthrough their immediate successor.
+  for (MachineBasicBlock &MBB : MF) {
+    if (!isImplicitFallthrough(MBB))
+      continue;
+
+    assert(std::distance(MBB.successors().begin(), MBB.successors().end()) ==
+           1);
+    MIB.setInsertPt(MBB, MBB.end());
+    MIB.buildBr(**MBB.successors().begin());
+  }
+}
+
 bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
   // Initialize the type registry.
   const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
@@ -599,6 +633,7 @@ bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
   generateAssignInstrs(MF, GR, MIB);
   processSwitches(MF, GR, MIB);
   processInstrsWithTypeFolding(MF, GR, MIB);
+  removeImplicitFallthroughs(MF, MIB);
 
   return true;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 1503f263e42c0d..517174b356f059 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -29,6 +29,7 @@
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Pass.h"
 #include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/Utils.h"
 #include <optional>
 
 using namespace llvm;
@@ -151,6 +152,11 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
 }
 
 void SPIRVPassConfig::addIRPasses() {
+  // Once legalized, we need to structurize the CFG to follow the spec.
+  // This is done through the following 8 steps.
+  // TODO(#75801): add the remaining steps.
+  addPass(createLoopSimplifyPass());
+
   TargetPassConfig::addIRPasses();
   addPass(createSPIRVRegularizerPass());
   addPass(createSPIRVPrepareFunctionsPass(TM));
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index 1c0e8d84e2fd10..d4f7d8e89af5e4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -228,8 +228,8 @@ uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) {
   return MI->getOperand(1).getCImm()->getValue().getZExtValue();
 }
 
-bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) {
-  if (auto *GI = dyn_cast<GIntrinsic>(&MI))
+bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID) {
+  if (const auto *GI = dyn_cast<GIntrinsic>(&MI))
     return GI->is(IntrinsicID);
   return false;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index 30fae6c7de479f..60742e2f272808 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -79,7 +79,7 @@ MachineInstr *getDefInstrMaybeConstant(Register &ConstReg,
 uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI);
 
 // Check if MI is a SPIR-V specific intrinsic call.
-bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID);
+bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID);
 
 // Get type of i-th operand of the metadata node.
 Type *getMDOperandAsType(const MDNode *N, unsigned I);
diff --git a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
new file mode 100644
index 00000000000000..eccf3301002ae7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
@@ -0,0 +1,67 @@
+; RUN: llc -mtriple=spirv-unknown-unknown -O0 %s -o - | FileCheck %s
+
+; CHECK-DAG:  [[bool:%[0-9]+]] = OpTypeBool
+; CHECK-DAG:  [[uint:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG:  [[uint_0:%[0-9]+]] = OpConstant [[uint]] 0
+
+define i32 @main(i32 noundef %0) #1 {
+  %2 = icmp ne i32 %0, 0
+  br i1 %2, label %l1, label %l2
+
+; CHECK: [[param_0:%[0-9]+]] = OpFunctionParameter [[uint]]
+; CHECK:    [[cond:%[0-9]+]] = OpINotEqual [[bool]] [[param_0]] [[uint_0]]
+; CHECK:                       OpBranchConditional [[cond]] [[l1_pre:%[0-9]+]] [[l2_pre:%[0-9]+]]
+
+; CHECK-DAG:   [[l2_pre]] = OpLabel
+; CHECK-NEXT:               OpBranch [[l2_header:%[0-9]+]]
+
+; CHECK-DAG:   [[l1_pre]] = OpLabel
+; CHECK-NEXT:               OpBranch [[l1_header:%[0-9]+]]
+
+l1:
+  br i1 %2, label %l1_body, label %l1_end
+; CHECK-DAG:    [[l1_header]] = OpLabel
+; CHECK-NEXT:                   OpBranchConditional [[cond]] [[l1_body:%[0-9]+]] [[l1_end:%[0-9]+]]
+
+l1_body:
+  br label %l1_continue
+; CHECK-DAG:   [[l1_body]] = OpLabel
+; CHECK-NEXT:                OpBranch [[l1_continue:%[0-9]+]]
+
+l1_continue:
+  br label %l1
+; CHECK-DAG:   [[l1_continue]] = OpLabel
+; CHECK-NEXT:                    OpBranch [[l1_header]]
+
+l1_end:
+  br label %end
+; CHECK-DAG:   [[l1_end]] = OpLabel
+; CHECK-NEXT:                OpBranch [[end:%[0-9]+]]
+
+l2:
+  br i1 %2, label %l2_body, label %l2_end
+; CHECK-DAG:    [[l2_header]] = OpLabel
+; CHECK-NEXT:                   OpBranchConditional [[cond]] [[l2_body:%[0-9]+]] [[l2_end:%[0-9]+]]
+
+l2_body:
+  br label %l2_continue
+; CHECK-DAG:   [[l2_body]] = OpLabel
+; CHECK-NEXT:                OpBranch [[l2_continue:%[0-9]+]]
+
+l2_continue:
+  br label %l2
+; CHECK-DAG:   [[l2_continue]] = OpLabel
+; CHECK-NEXT:                    OpBranch [[l2_header]]
+
+l2_end:
+  br label %end
+; CHECK-DAG:   [[l2_end]] = OpLabel
+; CHECK-NEXT:                OpBranch [[end:%[0-9]+]]
+
+end:
+  ret i32 1
+; CHECK-DAG:       [[end]] = OpLabel
+; CHECK-NEXT:                OpReturn
+}
+
+attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" convergent }

>From 28be748baa885a5c2b2af11debfc0d91e9966f36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 19 Dec 2023 13:51:41 +0100
Subject: [PATCH 2/6] fixup! [SPIR-V] Add pre-headers to loops.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

only allow pass for Vulkan

Signed-off-by: Nathan Gauër <brioche at google.com>
---
 llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 517174b356f059..52b174f4e0c294 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -152,10 +152,12 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
 }
 
 void SPIRVPassConfig::addIRPasses() {
-  // Once legalized, we need to structurize the CFG to follow the spec.
-  // This is done through the following 8 steps.
-  // TODO(#75801): add the remaining steps.
-  addPass(createLoopSimplifyPass());
+  if (TM.getSubtargetImpl()->isVulkanEnv()) {
+    // Once legalized, we need to structurize the CFG to follow the spec.
+    // This is done through the following 8 steps.
+    // TODO(#75801): add the remaining steps.
+    addPass(createLoopSimplifyPass());
+  }
 
   TargetPassConfig::addIRPasses();
   addPass(createSPIRVRegularizerPass());

>From ec501822239e9b1d3acd51b050be9408fdedeb7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 2 Jan 2024 11:40:34 +0100
Subject: [PATCH 3/6] fixup! fixup! [SPIR-V] Add pre-headers to loops.

fix test format
---
 .../CodeGen/SPIRV/scfg-add-pre-headers.ll     | 56 +++++++++----------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
index eccf3301002ae7..73efde8d17bb9c 100644
--- a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
+++ b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
@@ -1,67 +1,67 @@
 ; RUN: llc -mtriple=spirv-unknown-unknown -O0 %s -o - | FileCheck %s
 
-; CHECK-DAG:  [[bool:%[0-9]+]] = OpTypeBool
-; CHECK-DAG:  [[uint:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-DAG:  [[uint_0:%[0-9]+]] = OpConstant [[uint]] 0
+; CHECK-DAG:    [[#bool:]] = OpTypeBool
+; CHECK-DAG:    [[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG:  [[#uint_0:]] = OpConstant [[#uint]] 0
 
 define i32 @main(i32 noundef %0) #1 {
   %2 = icmp ne i32 %0, 0
   br i1 %2, label %l1, label %l2
 
-; CHECK: [[param_0:%[0-9]+]] = OpFunctionParameter [[uint]]
-; CHECK:    [[cond:%[0-9]+]] = OpINotEqual [[bool]] [[param_0]] [[uint_0]]
-; CHECK:                       OpBranchConditional [[cond]] [[l1_pre:%[0-9]+]] [[l2_pre:%[0-9]+]]
+; CHECK:     [[#param_0:]] = OpFunctionParameter [[#uint]]
+; CHECK:        [[#cond:]] = OpINotEqual [[#bool]] [[#param_0]] [[#uint_0]]
+; CHECK:                     OpBranchConditional [[#cond]] [[#l1_pre:]] [[#l2_pre:]]
 
-; CHECK-DAG:   [[l2_pre]] = OpLabel
-; CHECK-NEXT:               OpBranch [[l2_header:%[0-9]+]]
+; CHECK-DAG:   [[#l2_pre]] = OpLabel
+; CHECK-NEXT:                OpBranch [[#l2_header:]]
 
-; CHECK-DAG:   [[l1_pre]] = OpLabel
-; CHECK-NEXT:               OpBranch [[l1_header:%[0-9]+]]
+; CHECK-DAG:   [[#l1_pre]] = OpLabel
+; CHECK-NEXT:                OpBranch [[#l1_header:]]
 
 l1:
   br i1 %2, label %l1_body, label %l1_end
-; CHECK-DAG:    [[l1_header]] = OpLabel
-; CHECK-NEXT:                   OpBranchConditional [[cond]] [[l1_body:%[0-9]+]] [[l1_end:%[0-9]+]]
+; CHECK-DAG:    [[#l1_header]] = OpLabel
+; CHECK-NEXT:                    OpBranchConditional [[#cond]] [[#l1_body:]] [[#l1_end:]]
 
 l1_body:
   br label %l1_continue
-; CHECK-DAG:   [[l1_body]] = OpLabel
-; CHECK-NEXT:                OpBranch [[l1_continue:%[0-9]+]]
+; CHECK-DAG:   [[#l1_body]] = OpLabel
+; CHECK-NEXT:                 OpBranch [[#l1_continue:]]
 
 l1_continue:
   br label %l1
-; CHECK-DAG:   [[l1_continue]] = OpLabel
-; CHECK-NEXT:                    OpBranch [[l1_header]]
+; CHECK-DAG:   [[#l1_continue]] = OpLabel
+; CHECK-NEXT:                     OpBranch [[#l1_header]]
 
 l1_end:
   br label %end
-; CHECK-DAG:   [[l1_end]] = OpLabel
-; CHECK-NEXT:                OpBranch [[end:%[0-9]+]]
+; CHECK-DAG:   [[#l1_end]] = OpLabel
+; CHECK-NEXT:                OpBranch [[#end:]]
 
 l2:
   br i1 %2, label %l2_body, label %l2_end
-; CHECK-DAG:    [[l2_header]] = OpLabel
-; CHECK-NEXT:                   OpBranchConditional [[cond]] [[l2_body:%[0-9]+]] [[l2_end:%[0-9]+]]
+; CHECK-DAG:    [[#l2_header]] = OpLabel
+; CHECK-NEXT:                    OpBranchConditional [[#cond]] [[#l2_body:]] [[#l2_end:]]
 
 l2_body:
   br label %l2_continue
-; CHECK-DAG:   [[l2_body]] = OpLabel
-; CHECK-NEXT:                OpBranch [[l2_continue:%[0-9]+]]
+; CHECK-DAG:   [[#l2_body]] = OpLabel
+; CHECK-NEXT:                 OpBranch [[#l2_continue:]]
 
 l2_continue:
   br label %l2
-; CHECK-DAG:   [[l2_continue]] = OpLabel
-; CHECK-NEXT:                    OpBranch [[l2_header]]
+; CHECK-DAG:   [[#l2_continue]] = OpLabel
+; CHECK-NEXT:                     OpBranch [[#l2_header]]
 
 l2_end:
   br label %end
-; CHECK-DAG:   [[l2_end]] = OpLabel
-; CHECK-NEXT:                OpBranch [[end:%[0-9]+]]
+; CHECK-DAG:   [[#l2_end]] = OpLabel
+; CHECK-NEXT:                OpBranch [[#end:]]
 
 end:
   ret i32 1
-; CHECK-DAG:       [[end]] = OpLabel
-; CHECK-NEXT:                OpReturn
+; CHECK-DAG:       [[#end]] = OpLabel
+; CHECK-NEXT:                 OpReturn
 }
 
 attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" convergent }

>From b1f3b2e003bd04d63ad78e1d3a882c42786b9e7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 2 Jan 2024 11:51:41 +0100
Subject: [PATCH 4/6] fixup! [SPIR-V] Add pre-headers to loops.

fix test format
---
 .../CodeGen/SPIRV/scfg-add-pre-headers.ll     | 54 +++++++++----------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
index 73efde8d17bb9c..46f6e03303bcb4 100644
--- a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
+++ b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
@@ -1,66 +1,66 @@
 ; RUN: llc -mtriple=spirv-unknown-unknown -O0 %s -o - | FileCheck %s
 
-; CHECK-DAG:    [[#bool:]] = OpTypeBool
-; CHECK-DAG:    [[#uint:]] = OpTypeInt 32 0
-; CHECK-DAG:  [[#uint_0:]] = OpConstant [[#uint]] 0
+; CHECK-DAG:    %[[#bool:]] = OpTypeBool
+; CHECK-DAG:    %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG:  %[[#uint_0:]] = OpConstant %[[#uint]] 0
 
 define i32 @main(i32 noundef %0) #1 {
   %2 = icmp ne i32 %0, 0
   br i1 %2, label %l1, label %l2
 
-; CHECK:     [[#param_0:]] = OpFunctionParameter [[#uint]]
-; CHECK:        [[#cond:]] = OpINotEqual [[#bool]] [[#param_0]] [[#uint_0]]
-; CHECK:                     OpBranchConditional [[#cond]] [[#l1_pre:]] [[#l2_pre:]]
+; CHECK:     %[[#param_0:]] = OpFunctionParameter %[[#uint]]
+; CHECK:        %[[#cond:]] = OpINotEqual %[[#bool]] %[[#param_0]] %[[#uint_0]]
+; CHECK:                     OpBranchConditional %[[#cond]] %[[#l1_pre:]] %[[#l2_pre:]]
 
-; CHECK-DAG:   [[#l2_pre]] = OpLabel
-; CHECK-NEXT:                OpBranch [[#l2_header:]]
+; CHECK-DAG:   %[[#l2_pre]] = OpLabel
+; CHECK-NEXT:                OpBranch %[[#l2_header:]]
 
-; CHECK-DAG:   [[#l1_pre]] = OpLabel
-; CHECK-NEXT:                OpBranch [[#l1_header:]]
+; CHECK-DAG:   %[[#l1_pre]] = OpLabel
+; CHECK-NEXT:                OpBranch %[[#l1_header:]]
 
 l1:
   br i1 %2, label %l1_body, label %l1_end
-; CHECK-DAG:    [[#l1_header]] = OpLabel
-; CHECK-NEXT:                    OpBranchConditional [[#cond]] [[#l1_body:]] [[#l1_end:]]
+; CHECK-DAG:    %[[#l1_header]] = OpLabel
+; CHECK-NEXT:                    OpBranchConditional %[[#cond]] %[[#l1_body:]] %[[#l1_end:]]
 
 l1_body:
   br label %l1_continue
-; CHECK-DAG:   [[#l1_body]] = OpLabel
-; CHECK-NEXT:                 OpBranch [[#l1_continue:]]
+; CHECK-DAG:   %[[#l1_body]] = OpLabel
+; CHECK-NEXT:                 OpBranch %[[#l1_continue:]]
 
 l1_continue:
   br label %l1
-; CHECK-DAG:   [[#l1_continue]] = OpLabel
-; CHECK-NEXT:                     OpBranch [[#l1_header]]
+; CHECK-DAG:   %[[#l1_continue]] = OpLabel
+; CHECK-NEXT:                     OpBranch %[[#l1_header]]
 
 l1_end:
   br label %end
-; CHECK-DAG:   [[#l1_end]] = OpLabel
-; CHECK-NEXT:                OpBranch [[#end:]]
+; CHECK-DAG:   %[[#l1_end]] = OpLabel
+; CHECK-NEXT:                OpBranch %[[#end:]]
 
 l2:
   br i1 %2, label %l2_body, label %l2_end
-; CHECK-DAG:    [[#l2_header]] = OpLabel
-; CHECK-NEXT:                    OpBranchConditional [[#cond]] [[#l2_body:]] [[#l2_end:]]
+; CHECK-DAG:    %[[#l2_header]] = OpLabel
+; CHECK-NEXT:                    OpBranchConditional %[[#cond]] %[[#l2_body:]] %[[#l2_end:]]
 
 l2_body:
   br label %l2_continue
-; CHECK-DAG:   [[#l2_body]] = OpLabel
-; CHECK-NEXT:                 OpBranch [[#l2_continue:]]
+; CHECK-DAG:   %[[#l2_body]] = OpLabel
+; CHECK-NEXT:                 OpBranch %[[#l2_continue:]]
 
 l2_continue:
   br label %l2
-; CHECK-DAG:   [[#l2_continue]] = OpLabel
-; CHECK-NEXT:                     OpBranch [[#l2_header]]
+; CHECK-DAG:   %[[#l2_continue]] = OpLabel
+; CHECK-NEXT:                     OpBranch %[[#l2_header]]
 
 l2_end:
   br label %end
-; CHECK-DAG:   [[#l2_end]] = OpLabel
-; CHECK-NEXT:                OpBranch [[#end:]]
+; CHECK-DAG:   %[[#l2_end]] = OpLabel
+; CHECK-NEXT:                OpBranch %[[#end:]]
 
 end:
   ret i32 1
-; CHECK-DAG:       [[#end]] = OpLabel
+; CHECK-DAG:       %[[#end]] = OpLabel
 ; CHECK-NEXT:                 OpReturn
 }
 

>From 0da63f4665c3ed22cf571931e1381e56726f214f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 2 Jan 2024 11:54:51 +0100
Subject: [PATCH 5/6] fixup! [SPIR-V] Add pre-headers to loops.

fix CHECK indent
---
 .../CodeGen/SPIRV/scfg-add-pre-headers.ll     | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
index 46f6e03303bcb4..4d2c8f01a97ed9 100644
--- a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
+++ b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
@@ -10,58 +10,58 @@ define i32 @main(i32 noundef %0) #1 {
 
 ; CHECK:     %[[#param_0:]] = OpFunctionParameter %[[#uint]]
 ; CHECK:        %[[#cond:]] = OpINotEqual %[[#bool]] %[[#param_0]] %[[#uint_0]]
-; CHECK:                     OpBranchConditional %[[#cond]] %[[#l1_pre:]] %[[#l2_pre:]]
+; CHECK:                      OpBranchConditional %[[#cond]] %[[#l1_pre:]] %[[#l2_pre:]]
 
 ; CHECK-DAG:   %[[#l2_pre]] = OpLabel
-; CHECK-NEXT:                OpBranch %[[#l2_header:]]
+; CHECK-NEXT:                 OpBranch %[[#l2_header:]]
 
 ; CHECK-DAG:   %[[#l1_pre]] = OpLabel
-; CHECK-NEXT:                OpBranch %[[#l1_header:]]
+; CHECK-NEXT:                 OpBranch %[[#l1_header:]]
 
 l1:
   br i1 %2, label %l1_body, label %l1_end
 ; CHECK-DAG:    %[[#l1_header]] = OpLabel
-; CHECK-NEXT:                    OpBranchConditional %[[#cond]] %[[#l1_body:]] %[[#l1_end:]]
+; CHECK-NEXT:                     OpBranchConditional %[[#cond]] %[[#l1_body:]] %[[#l1_end:]]
 
 l1_body:
   br label %l1_continue
 ; CHECK-DAG:   %[[#l1_body]] = OpLabel
-; CHECK-NEXT:                 OpBranch %[[#l1_continue:]]
+; CHECK-NEXT:                  OpBranch %[[#l1_continue:]]
 
 l1_continue:
   br label %l1
 ; CHECK-DAG:   %[[#l1_continue]] = OpLabel
-; CHECK-NEXT:                     OpBranch %[[#l1_header]]
+; CHECK-NEXT:                      OpBranch %[[#l1_header]]
 
 l1_end:
   br label %end
 ; CHECK-DAG:   %[[#l1_end]] = OpLabel
-; CHECK-NEXT:                OpBranch %[[#end:]]
+; CHECK-NEXT:                 OpBranch %[[#end:]]
 
 l2:
   br i1 %2, label %l2_body, label %l2_end
 ; CHECK-DAG:    %[[#l2_header]] = OpLabel
-; CHECK-NEXT:                    OpBranchConditional %[[#cond]] %[[#l2_body:]] %[[#l2_end:]]
+; CHECK-NEXT:                     OpBranchConditional %[[#cond]] %[[#l2_body:]] %[[#l2_end:]]
 
 l2_body:
   br label %l2_continue
 ; CHECK-DAG:   %[[#l2_body]] = OpLabel
-; CHECK-NEXT:                 OpBranch %[[#l2_continue:]]
+; CHECK-NEXT:                  OpBranch %[[#l2_continue:]]
 
 l2_continue:
   br label %l2
 ; CHECK-DAG:   %[[#l2_continue]] = OpLabel
-; CHECK-NEXT:                     OpBranch %[[#l2_header]]
+; CHECK-NEXT:                      OpBranch %[[#l2_header]]
 
 l2_end:
   br label %end
 ; CHECK-DAG:   %[[#l2_end]] = OpLabel
-; CHECK-NEXT:                OpBranch %[[#end:]]
+; CHECK-NEXT:                 OpBranch %[[#end:]]
 
 end:
   ret i32 1
 ; CHECK-DAG:       %[[#end]] = OpLabel
-; CHECK-NEXT:                 OpReturn
+; CHECK-NEXT:                  OpReturn
 }
 
 attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" convergent }

>From 987be14b68e70ba448142d35e2b24888314a97d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 19 Dec 2023 15:57:05 +0100
Subject: [PATCH 6/6] [SPIR-V] Strip convergence intrinsics before ISel
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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 2 new functions in the PrepareFunction pass,
one to remove convergencectrl bundle uses, and one to remove
the convergence 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 <brioche at google.com>
---
 .../Target/SPIRV/SPIRVPrepareFunctions.cpp    | 57 ++++++++++++++++++-
 .../CodeGen/SPIRV/scfg-add-pre-headers.ll     | 15 +++++
 2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index c376497469ce33..5550800b722484 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -12,6 +12,8 @@
 // restore correct SPIR-V types of function arguments and return values.
 // This pass also substitutes some llvm intrinsic calls with calls to newly
 // generated functions (as the Khronos LLVM/SPIR-V Translator does).
+// This pass also trims convergence intrinsics as those were only useful when
+// modifying the CFG during IR passes.
 //
 // NOTE: this pass is a module-level one due to the necessity to modify
 // GVs/functions.
@@ -41,6 +43,8 @@ namespace {
 class SPIRVPrepareFunctions : public ModulePass {
   const SPIRVTargetMachine &TM;
   bool substituteIntrinsicCalls(Function *F);
+  bool stripConvergenceOperands(Function *F);
+  bool stripConvergenceIntrinsics(Function *F);
   Function *removeAggregateTypesFromSignature(Function *F);
 
 public:
@@ -312,6 +316,54 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
   return Changed;
 }
 
+bool SPIRVPrepareFunctions::stripConvergenceOperands(Function *F) {
+  DenseSet<Instruction *> to_remove;
+  for (BasicBlock &BB : *F) {
+    for (Instruction &I : BB) {
+      auto *CI = dyn_cast<CallInst>(&I);
+      if (CI == nullptr)
+        continue;
+
+      auto OB = CI->getOperandBundle(LLVMContext::OB_convergencectrl);
+      if (!OB.has_value())
+        continue;
+
+      auto *NewCall = CallBase::removeOperandBundle(
+          CI, LLVMContext::OB_convergencectrl, CI);
+      NewCall->copyMetadata(*CI);
+      CI->replaceAllUsesWith(NewCall);
+      to_remove.insert(CI);
+    }
+  }
+
+  for (Instruction *I : to_remove)
+    I->eraseFromParent();
+  return to_remove.size() != 0;
+}
+
+bool SPIRVPrepareFunctions::stripConvergenceIntrinsics(Function *F) {
+  DenseSet<Instruction *> to_remove;
+  for (BasicBlock &BB : *F) {
+    for (Instruction &I : BB) {
+      auto *II = dyn_cast<IntrinsicInst>(&I);
+      if (II == nullptr)
+        continue;
+
+      if (II->getIntrinsicID() != Intrinsic::experimental_convergence_entry &&
+          II->getIntrinsicID() != Intrinsic::experimental_convergence_loop &&
+          II->getIntrinsicID() != Intrinsic::experimental_convergence_anchor) {
+        continue;
+      }
+
+      to_remove.insert(II);
+    }
+  }
+
+  for (Instruction *I : to_remove)
+    I->eraseFromParent();
+  return to_remove.size() != 0;
+}
+
 // Returns F if aggregate argument/return types are not present or cloned F
 // function with the types replaced by i32 types. The change in types is
 // noted in 'spv.cloned_funcs' metadata for later restoration.
@@ -380,8 +432,11 @@ SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) {
 
 bool SPIRVPrepareFunctions::runOnModule(Module &M) {
   bool Changed = false;
-  for (Function &F : M)
+  for (Function &F : M) {
     Changed |= substituteIntrinsicCalls(&F);
+    Changed |= stripConvergenceOperands(&F);
+    Changed |= stripConvergenceIntrinsics(&F);
+  }
 
   std::vector<Function *> FuncsWorklist;
   for (auto &F : M)
diff --git a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
index 4d2c8f01a97ed9..cd40a30ec8b08f 100644
--- a/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
+++ b/llvm/test/CodeGen/SPIRV/scfg-add-pre-headers.ll
@@ -3,11 +3,14 @@
 ; CHECK-DAG:    %[[#bool:]] = OpTypeBool
 ; CHECK-DAG:    %[[#uint:]] = OpTypeInt 32 0
 ; CHECK-DAG:  %[[#uint_0:]] = OpConstant %[[#uint]] 0
+; CHECK-DAG:                  OpName %[[#main:]] "main"
 
 define i32 @main(i32 noundef %0) #1 {
+  %t1 = call token @llvm.experimental.convergence.entry()
   %2 = icmp ne i32 %0, 0
   br i1 %2, label %l1, label %l2
 
+; CHECK:         %[[#main]] = OpFunction
 ; CHECK:     %[[#param_0:]] = OpFunctionParameter %[[#uint]]
 ; CHECK:        %[[#cond:]] = OpINotEqual %[[#bool]] %[[#param_0]] %[[#uint_0]]
 ; CHECK:                      OpBranchConditional %[[#cond]] %[[#l1_pre:]] %[[#l2_pre:]]
@@ -19,6 +22,7 @@ define i32 @main(i32 noundef %0) #1 {
 ; CHECK-NEXT:                 OpBranch %[[#l1_header:]]
 
 l1:
+  %tl1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t1) ]
   br i1 %2, label %l1_body, label %l1_end
 ; CHECK-DAG:    %[[#l1_header]] = OpLabel
 ; CHECK-NEXT:                     OpBranchConditional %[[#cond]] %[[#l1_body:]] %[[#l1_end:]]
@@ -34,11 +38,14 @@ l1_continue:
 ; CHECK-NEXT:                      OpBranch %[[#l1_header]]
 
 l1_end:
+  %call = call spir_func i32 @_Z3absi(i32 %0) [ "convergencectrl"(token %tl1) ]
   br label %end
 ; CHECK-DAG:   %[[#l1_end]] = OpLabel
+; CHECK-DAG:         %[[#]] = OpFunctionCall
 ; CHECK-NEXT:                 OpBranch %[[#end:]]
 
 l2:
+  %tl2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t1) ]
   br i1 %2, label %l2_body, label %l2_end
 ; CHECK-DAG:    %[[#l2_header]] = OpLabel
 ; CHECK-NEXT:                     OpBranchConditional %[[#cond]] %[[#l2_body:]] %[[#l2_end:]]
@@ -65,3 +72,11 @@ end:
 }
 
 attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" convergent }
+
+declare token @llvm.experimental.convergence.entry()
+declare token @llvm.experimental.convergence.control()
+declare token @llvm.experimental.convergence.loop()
+
+; This intrinsic is not convergent. This is only because the backend doesn't
+; support convergent operations yet.
+declare spir_func i32 @_Z3absi(i32) convergent



More information about the llvm-commits mailing list