[llvm] [SPIR-V] Address the case when optimization uses GEP operator and GenCode creates G_PTR_ADD to convey the semantics (PR #107880)
Vyacheslav Levytskyy via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 9 10:27:59 PDT 2024
https://github.com/VyacheslavLevytskyy updated https://github.com/llvm/llvm-project/pull/107880
>From 7f4644ebd1edcdd8c88caa1e9e4a20179da9db93 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Mon, 9 Sep 2024 08:40:35 -0700
Subject: [PATCH 1/2] address the case when optimization uses GEP operator and
GenCode creates G_PTR_ADD
---
.../Target/SPIRV/SPIRVDuplicatesTracker.cpp | 14 +++++
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 58 +++++++++++++++++--
.../lib/Target/SPIRV/SPIRVSymbolicOperands.td | 1 +
.../CodeGen/SPIRV/opt-gepoperator-of-gvar.ll | 39 +++++++++++++
4 files changed, 107 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
index 7c32bb1968ef58..832ca0ba5a82d7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
@@ -13,6 +13,8 @@
#include "SPIRVDuplicatesTracker.h"
+#define DEBUG_TYPE "build-dep-graph"
+
using namespace llvm;
template <typename T>
@@ -63,6 +65,18 @@ void SPIRVGeneralDuplicatesTracker::buildDepsGraph(
if (MI->getOpcode() == SPIRV::OpConstantFunctionPointerINTEL && i == 2)
continue;
MachineOperand *RegOp = &VRegDef->getOperand(0);
+ LLVM_DEBUG({
+ if (Reg2Entry.count(RegOp) == 0 &&
+ (MI->getOpcode() != SPIRV::OpVariable || i != 3)) {
+ dbgs() << "Unexpected pattern while building a dependency "
+ "graph.\nInstruction: ";
+ MI->print(dbgs());
+ dbgs() << "Operand: ";
+ Op.print(dbgs());
+ dbgs() << "\nOperand definition: ";
+ VRegDef->print(dbgs());
+ }
+ });
assert((MI->getOpcode() == SPIRV::OpVariable && i == 3) ||
Reg2Entry.count(RegOp));
if (Reg2Entry.count(RegOp))
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index fed82b904af4f7..05f6ebaebf655c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -603,10 +603,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
case TargetOpcode::G_ADDRSPACE_CAST:
return selectAddrSpaceCast(ResVReg, ResType, I);
case TargetOpcode::G_PTR_ADD: {
- // Currently, we get G_PTR_ADD only as a result of translating
- // global variables, initialized with constant expressions like GV + Const
- // (see test opencl/basic/progvar_prog_scope_init.ll).
- // TODO: extend the handler once we have other cases.
+ // Currently, we get G_PTR_ADD only applied to global variables.
assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
Register GV = I.getOperand(1).getReg();
MachineRegisterInfo::def_instr_iterator II = MRI->def_instr_begin(GV);
@@ -615,8 +612,59 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
(*II).getOpcode() == TargetOpcode::COPY ||
(*II).getOpcode() == SPIRV::OpVariable) &&
isImm(I.getOperand(2), MRI));
- Register Idx = buildZerosVal(GR.getOrCreateSPIRVIntegerType(32, I, TII), I);
+ // It may be the initialization of a global variable.
+ bool IsGVInit = false;
+ for (MachineRegisterInfo::use_instr_iterator
+ UseIt = MRI->use_instr_begin(I.getOperand(0).getReg()),
+ UseEnd = MRI->use_instr_end();
+ UseIt != UseEnd; UseIt = std::next(UseIt)) {
+ if ((*UseIt).getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
+ (*UseIt).getOpcode() == SPIRV::OpVariable) {
+ IsGVInit = true;
+ break;
+ }
+ }
MachineBasicBlock &BB = *I.getParent();
+ if (!IsGVInit) {
+ SPIRVType *GVType = GR.getSPIRVTypeForVReg(GV);
+ SPIRVType *GVPointeeType = GR.getPointeeType(GVType);
+ SPIRVType *ResPointeeType = GR.getPointeeType(ResType);
+ if (GVPointeeType && ResPointeeType && GVPointeeType != ResPointeeType) {
+ // Build a new virtual register that is associated with the required
+ // data type.
+ Register NewVReg = MRI->createGenericVirtualRegister(MRI->getType(GV));
+ MRI->setRegClass(NewVReg, MRI->getRegClass(GV));
+ // Having a correctly typed base we are ready to build the actually
+ // required GEP. It may not be a constant though, because all Operands
+ // of OpSpecConstantOp is to originate from other const instructions,
+ // and only the AccessChain named opcodes accept a global OpVariable
+ // instruction. We can't use an AccessChain opcode because of the type
+ // mismatch between result and base types.
+ if (!GR.isBitcastCompatible(ResType, GVType))
+ report_fatal_error(
+ "incompatible result and operand types in a bitcast");
+ Register ResTypeReg = GR.getSPIRVTypeID(ResType);
+ MachineInstrBuilder MIB =
+ BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpBitcast))
+ .addDef(NewVReg)
+ .addUse(ResTypeReg)
+ .addUse(GV);
+ return MIB.constrainAllUses(TII, TRI, RBI) &&
+ BuildMI(BB, I, I.getDebugLoc(),
+ TII.get(STI.isVulkanEnv()
+ ? SPIRV::OpInBoundsAccessChain
+ : SPIRV::OpInBoundsPtrAccessChain))
+ .addDef(ResVReg)
+ .addUse(ResTypeReg)
+ .addUse(NewVReg)
+ .addUse(I.getOperand(2).getReg())
+ .constrainAllUses(TII, TRI, RBI);
+ }
+ }
+ // It's possible to translate G_PTR_ADD to OpSpecConstantOp: either to
+ // initialize a global variable with a constant expression (e.g., the test
+ // case opencl/basic/progvar_prog_scope_init.ll), or for another use case
+ Register Idx = buildZerosVal(GR.getOrCreateSPIRVIntegerType(32, I, TII), I);
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSpecConstantOp))
.addDef(ResVReg)
.addUse(GR.getSPIRVTypeID(ResType))
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 96601dd8796c63..23cd32eff45d5b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -1628,6 +1628,7 @@ multiclass OpcodeOperand<bits<32> value> {
defm : SymbolicOperandWithRequirements<OpcodeOperand, value, NAME, 0, 0, [], []>;
}
// TODO: implement other mnemonics.
+defm InBoundsAccessChain : OpcodeOperand<66>;
defm InBoundsPtrAccessChain : OpcodeOperand<70>;
defm PtrCastToGeneric : OpcodeOperand<121>;
defm Bitcast : OpcodeOperand<124>;
diff --git a/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll b/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll
new file mode 100644
index 00000000000000..be9cc351aa4831
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll
@@ -0,0 +1,39 @@
+; RUN: llc -O2 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O2 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; RUN: llc -O2 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O2 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#Char:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#PtrChar:]] = OpTypePointer CrossWorkgroup %[[#Char]]
+; CHECK-DAG: %[[#Int:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#PtrInt:]] = OpTypePointer CrossWorkgroup %[[#Int]]
+; CHECK-DAG: %[[#C648:]] = OpConstant %[[#]] 648
+; CHECK-DAG: %[[#Struct:]] = OpTypeStruct %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]]
+; CHECK-DAG: %[[#VarInit:]] = OpConstantNull %[[#Struct]]
+; CHECK-DAG: %[[#PtrStruct:]] = OpTypePointer CrossWorkgroup %[[#Struct]]
+; CHECK-DAG: %[[#Var:]] = OpVariable %[[#PtrStruct]] CrossWorkgroup %[[#VarInit]]
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#Line:]] = OpFunctionParameter %[[#Int]]
+; CHECK: %[[#]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#Casted:]] = OpBitcast %[[#PtrChar]] %[[#Var]]
+; CHECK: %[[#AddrChar:]] = OpInBoundsPtrAccessChain %[[#PtrChar]] %[[#Casted]] %[[#C648]]
+; CHECK: %[[#AddrInt:]] = OpBitcast %[[#PtrInt]] %[[#AddrChar]]
+; CHECK: OpStore %[[#AddrInt]] %[[#Line]]
+
+%struct = type { i32, [257 x i8], [257 x i8], [129 x i8], i32, i64, i64, i64, i64, i64, i64 }
+ at Mem = linkonce_odr dso_local addrspace(1) global %struct zeroinitializer, align 8
+
+define weak dso_local spir_func void @__devicelib_assert_fail(ptr addrspace(4) noundef %expr, i32 noundef %line, i1 %fl) {
+entry:
+ %cmp = icmp eq i32 %line, 0
+ br i1 %cmp, label %lbl, label %exit
+
+lbl:
+ store i32 %line, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @Mem, i64 648), align 8
+ br i1 %fl, label %lbl, label %exit
+
+exit:
+ ret void
+}
>From 533b61c8ccdf0906025c5042195ec8eeae9eb608 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Mon, 9 Sep 2024 10:27:48 -0700
Subject: [PATCH 2/2] address the case when GV is ptr to i8
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 9 +++++++
.../CodeGen/SPIRV/opt-gepoperator-of-gvar.ll | 27 ++++++++++++++++++-
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 05f6ebaebf655c..cbf3d4cece100e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -659,6 +659,15 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
.addUse(NewVReg)
.addUse(I.getOperand(2).getReg())
.constrainAllUses(TII, TRI, RBI);
+ } else {
+ return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSpecConstantOp))
+ .addDef(ResVReg)
+ .addUse(GR.getSPIRVTypeID(ResType))
+ .addImm(
+ static_cast<uint32_t>(SPIRV::Opcode::InBoundsPtrAccessChain))
+ .addUse(GV)
+ .addUse(I.getOperand(2).getReg())
+ .constrainAllUses(TII, TRI, RBI);
}
}
// It's possible to translate G_PTR_ADD to OpSpecConstantOp: either to
diff --git a/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll b/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll
index be9cc351aa4831..5f9229f5a5bd6b 100644
--- a/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll
+++ b/llvm/test/CodeGen/SPIRV/opt-gepoperator-of-gvar.ll
@@ -13,6 +13,9 @@
; CHECK-DAG: %[[#VarInit:]] = OpConstantNull %[[#Struct]]
; CHECK-DAG: %[[#PtrStruct:]] = OpTypePointer CrossWorkgroup %[[#Struct]]
; CHECK-DAG: %[[#Var:]] = OpVariable %[[#PtrStruct]] CrossWorkgroup %[[#VarInit]]
+; CHECK-DAG: %[[#Bytes:]] = OpVariable %[[#PtrChar]] CrossWorkgroup %[[#]]
+; CHECK-DAG: %[[#BytesGEP:]] = OpSpecConstantOp %[[#PtrChar]] 70 %[[#Bytes]] %[[#C648]]
+
; CHECK: OpFunction
; CHECK: %[[#]] = OpFunctionParameter %[[#]]
; CHECK: %[[#Line:]] = OpFunctionParameter %[[#Int]]
@@ -25,7 +28,7 @@
%struct = type { i32, [257 x i8], [257 x i8], [129 x i8], i32, i64, i64, i64, i64, i64, i64 }
@Mem = linkonce_odr dso_local addrspace(1) global %struct zeroinitializer, align 8
-define weak dso_local spir_func void @__devicelib_assert_fail(ptr addrspace(4) noundef %expr, i32 noundef %line, i1 %fl) {
+define weak dso_local spir_func void @foo(ptr addrspace(4) noundef %expr, i32 noundef %line, i1 %fl) {
entry:
%cmp = icmp eq i32 %line, 0
br i1 %cmp, label %lbl, label %exit
@@ -37,3 +40,25 @@ lbl:
exit:
ret void
}
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#Line2:]] = OpFunctionParameter %[[#Int]]
+; CHECK: %[[#]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#AddrInt2:]] = OpBitcast %[[#PtrInt]] %[[#BytesGEP]]
+; CHECK: OpStore %[[#AddrInt2]] %[[#Line2]]
+
+ at Bytes = linkonce_odr dso_local addrspace(1) global i8 zeroinitializer, align 8
+
+define weak dso_local spir_func void @bar(ptr addrspace(4) noundef %expr, i32 noundef %line, i1 %fl) {
+entry:
+ %cmp = icmp eq i32 %line, 0
+ br i1 %cmp, label %lbl, label %exit
+
+lbl:
+ store i32 %line, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @Bytes, i64 648), align 8
+ br i1 %fl, label %lbl, label %exit
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list