[llvm] [SPIR-V] Emit SPIR-V bitcasts between source/expected pointer type (PR #69621)
Michal Paszkowski via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 7 11:26:18 PST 2023
https://github.com/michalpaszkowski updated https://github.com/llvm/llvm-project/pull/69621
>From 7f821c46eb4f87dedbe364b9f78ffac460a30b13 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at outlook.com>
Date: Thu, 19 Oct 2023 10:58:05 -0700
Subject: [PATCH] [SPIR-V] Emit SPIR-V bitcasts between source/expected pointer
type
This patch introduces a new spv_ptrcast intrinsic for tracking exptected
pointer types. The change fixes multiple OpenCL CTS regressions due
the switch to opaque pointers (e.g. basic/hiloeo).
---
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 1 +
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 97 ++++++++++++++++++-
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 26 +++--
.../SPIRV/function/alloca-load-store.ll | 2 -
llvm/test/CodeGen/SPIRV/opaque_pointers.ll | 20 ++--
5 files changed, 128 insertions(+), 18 deletions(-)
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 736be8ca3212b..ea0074d22a441 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -28,6 +28,7 @@ let TargetPrefix = "spv" in {
def int_spv_insertelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_anyint_ty]>;
def int_spv_const_composite : Intrinsic<[llvm_i32_ty], [llvm_vararg_ty]>;
def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
+ def int_spv_ptrcast : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>]>;
def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
def int_spv_unreachable : Intrinsic<[], []>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 610d9a033aeea..b4245f00ec585 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -74,6 +74,10 @@ class SPIRVEmitIntrinsics
void processInstrAfterVisit(Instruction *I);
void insertAssignPtrTypeIntrs(Instruction *I);
void insertAssignTypeIntrs(Instruction *I);
+
+ DenseSet<Instruction *> PtrCastInstrs;
+ void insertPtrCastInstr(Instruction *I);
+
void processGlobalValue(GlobalVariable &GV);
public:
@@ -255,7 +259,19 @@ Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &I) {
}
Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &I) {
- SmallVector<Type *, 2> Types = {I.getType(), I.getOperand(0)->getType()};
+ Value *Source = I.getOperand(0);
+
+ // SPIR-V, contrary to LLVM 17+ IR, supports bitcasts between pointers of
+ // varying element types. In case of IR coming from older versions of LLVM
+ // such bitcasts do not provide sufficient information, should be just skipped
+ // here, and handled in insertPtrCastInstr.
+ if (I.getType()->isPointerTy()) {
+ I.replaceAllUsesWith(Source);
+ I.eraseFromParent();
+ return &I;
+ }
+
+ SmallVector<Type *, 2> Types = {I.getType(), Source->getType()};
SmallVector<Value *> Args(I.op_begin(), I.op_end());
auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_bitcast, {Types}, {Args});
std::string InstName = I.hasName() ? I.getName().str() : "";
@@ -265,6 +281,81 @@ Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &I) {
return NewI;
}
+void SPIRVEmitIntrinsics::insertPtrCastInstr(Instruction *I) {
+ Value *Pointer;
+ Type *ExpectedElementType;
+ unsigned OperandToReplace;
+ if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
+ Pointer = SI->getPointerOperand();
+ ExpectedElementType = SI->getValueOperand()->getType();
+ OperandToReplace = 1;
+ } else if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
+ Pointer = LI->getPointerOperand();
+ ExpectedElementType = LI->getType();
+ OperandToReplace = 0;
+ } else {
+ return;
+ }
+
+ // If Pointer is the result of nop BitCastInst (ptr -> ptr), use the source
+ // pointer instead. The BitCastInst should be later removed when visited.
+ if (BitCastInst *BC = dyn_cast<BitCastInst>(Pointer))
+ Pointer = BC->getOperand(0);
+
+ // Do not emit spv_ptrcast if Pointer is a GlobalValue of expected type.
+ GlobalValue *GV = dyn_cast<GlobalValue>(Pointer);
+ if (GV && GV->getValueType() == ExpectedElementType)
+ return;
+
+ setInsertPointSkippingPhis(*IRB, I);
+ Constant *ExpectedElementTypeConst =
+ Constant::getNullValue(ExpectedElementType);
+ ConstantAsMetadata *CM =
+ ValueAsMetadata::getConstant(ExpectedElementTypeConst);
+ MDTuple *TyMD = MDNode::get(F->getContext(), CM);
+ MetadataAsValue *VMD = MetadataAsValue::get(F->getContext(), TyMD);
+ unsigned AddressSpace = Pointer->getType()->getPointerAddressSpace();
+ bool FirstPtrCastOrAssignPtrType = true;
+
+ // Do not emit new spv_ptrcast if equivalent one already exists or when
+ // spv_assign_ptr_type already targets this pointer with the same element
+ // type.
+ for (auto User : Pointer->users()) {
+ if (auto *II = dyn_cast<IntrinsicInst>(User)) {
+ if ((II->getIntrinsicID() != Intrinsic::spv_assign_ptr_type &&
+ II->getIntrinsicID() != Intrinsic::spv_ptrcast) ||
+ II->getOperand(0) != Pointer)
+ continue;
+
+ FirstPtrCastOrAssignPtrType = false;
+ if (II->getOperand(1) == VMD &&
+ dyn_cast<ConstantInt>(II->getOperand(2))->getSExtValue() ==
+ AddressSpace)
+ return;
+ }
+ }
+
+ // Do not emit spv_ptrcast if it would cast to the default pointer element
+ // type (i8) of the same address space.
+ if (ExpectedElementType->isIntegerTy(8))
+ return;
+
+ // If this would be the first spv_ptrcast and there is no spv_assign_ptr_type
+ // for this pointer before, do not emit spv_ptrcast but emit
+ // spv_assign_ptr_type instead.
+ if (FirstPtrCastOrAssignPtrType) {
+ buildIntrWithMD(Intrinsic::spv_assign_ptr_type, {Pointer->getType()},
+ ExpectedElementTypeConst, Pointer,
+ {IRB->getInt32(AddressSpace)});
+ } else {
+ SmallVector<Type *, 2> Types = {Pointer->getType(), Pointer->getType()};
+ SmallVector<Value *, 2> Args = {Pointer, VMD, IRB->getInt32(AddressSpace)};
+ auto *PtrCastI =
+ IRB->CreateIntrinsic(Intrinsic::spv_ptrcast, {Types}, Args);
+ I->setOperand(OperandToReplace, PtrCastI);
+ }
+}
+
Instruction *SPIRVEmitIntrinsics::visitInsertElementInst(InsertElementInst &I) {
SmallVector<Type *, 4> Types = {I.getType(), I.getOperand(0)->getType(),
I.getOperand(1)->getType(),
@@ -524,6 +615,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
for (auto &I : Worklist) {
insertAssignPtrTypeIntrs(I);
insertAssignTypeIntrs(I);
+ insertPtrCastInstr(I);
}
for (auto *I : Worklist) {
@@ -531,7 +623,8 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
if (!I->getType()->isVoidTy() || isa<StoreInst>(I))
IRB->SetInsertPoint(I->getNextNode());
I = visit(*I);
- processInstrAfterVisit(I);
+ if (I->getParent())
+ processInstrAfterVisit(I);
}
return true;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index f4076be2a7b77..113f0ca2235f9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -125,12 +125,26 @@ static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
SmallVector<MachineInstr *, 10> ToErase;
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : MBB) {
- if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast))
- continue;
- assert(MI.getOperand(2).isReg());
- MIB.setInsertPt(*MI.getParent(), MI);
- MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg());
- ToErase.push_back(&MI);
+ if (isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) {
+ assert(MI.getOperand(2).isReg());
+ MIB.setInsertPt(*MI.getParent(), MI);
+ MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg());
+ ToErase.push_back(&MI);
+ } else if (isSpvIntrinsic(MI, Intrinsic::spv_ptrcast)) {
+ assert(MI.getOperand(2).isReg());
+ MIB.setInsertPt(*MI.getParent(), MI);
+
+ SPIRVType *BaseTy = GR->getOrCreateSPIRVType(
+ getMDOperandAsType(MI.getOperand(3).getMetadata(), 0), MIB);
+ SPIRVType *AssignedPtrType = GR->getOrCreateSPIRVPointerType(
+ BaseTy, MI, *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo(),
+ addressSpaceToStorageClass(MI.getOperand(4).getImm()));
+
+ GR->assignSPIRVTypeToVReg(AssignedPtrType, MI.getOperand(0).getReg(),
+ MF);
+ MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg());
+ ToErase.push_back(&MI);
+ }
}
}
for (MachineInstr *MI : ToErase)
diff --git a/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll b/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll
index 5e76eb11de633..691f882d07135 100644
--- a/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll
+++ b/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll
@@ -1,7 +1,5 @@
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
-target triple = "spirv32-unknown-unknown"
-
; CHECK-DAG: OpName %[[#BAR:]] "bar"
; CHECK-DAG: OpName %[[#FOO:]] "foo"
; CHECK-DAG: OpName %[[#GOO:]] "goo"
diff --git a/llvm/test/CodeGen/SPIRV/opaque_pointers.ll b/llvm/test/CodeGen/SPIRV/opaque_pointers.ll
index aaddea1372721..30c9d8dc774e2 100644
--- a/llvm/test/CodeGen/SPIRV/opaque_pointers.ll
+++ b/llvm/test/CodeGen/SPIRV/opaque_pointers.ll
@@ -1,19 +1,23 @@
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK
; CHECK-DAG: %[[#Int8Ty:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#PtrTy:]] = OpTypePointer Function %[[#Int8Ty]]
-; CHECK-DAG: %[[#Int64Ty:]] = OpTypeInt 64 0
-; CHECK-DAG: %[[#FTy:]] = OpTypeFunction %[[#Int64Ty]] %[[#PtrTy]]
+; CHECK-DAG: %[[#PtrInt8Ty:]] = OpTypePointer Function %[[#Int8Ty]]
; CHECK-DAG: %[[#Int32Ty:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#PtrInt32Ty:]] = OpTypePointer Function %[[#Int32Ty]]
+; CHECK-DAG: %[[#Int64Ty:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#PtrInt64Ty:]] = OpTypePointer Function %[[#Int64Ty]]
+; CHECK-DAG: %[[#FTy:]] = OpTypeFunction %[[#Int64Ty]] %[[#PtrInt8Ty]]
; CHECK-DAG: %[[#Const:]] = OpConstant %[[#Int32Ty]] 0
; CHECK: OpFunction %[[#Int64Ty]] None %[[#FTy]]
-; CHECK: %[[#Parm:]] = OpFunctionParameter %[[#PtrTy]]
-; CHECK: OpStore %[[#Parm]] %[[#Const]] Aligned 4
-; CHECK: %[[#Res:]] = OpLoad %[[#Int64Ty]] %[[#Parm]] Aligned 8
+; CHECK: %[[#Parm:]] = OpFunctionParameter %[[#PtrInt8Ty]]
+; CHECK-DAG: %[[#Bitcast1:]] = OpBitcast %[[#PtrInt32Ty]] %[[#Parm]]
+; CHECK: OpStore %[[#Bitcast1]] %[[#Const]] Aligned 4
+; CHECK-DAG: %[[#Bitcast2:]] = OpBitcast %[[#PtrInt64Ty]] %[[#Parm]]
+; CHECK: %[[#Res:]] = OpLoad %[[#Int64Ty]] %[[#Bitcast2]] Aligned 4
; CHECK: OpReturnValue %[[#Res]]
define i64 @test(ptr %p) {
- store i32 0, ptr %p
- %v = load i64, ptr %p
+ store i32 0, ptr %p, align 4
+ %v = load i64, ptr %p, align 4
ret i64 %v
}
More information about the llvm-commits
mailing list