[llvm] [SPIR-V] Implement insertion of OpGenericCastToPtr using builtin functions (PR #95055)
Vyacheslav Levytskyy via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 11 03:24:29 PDT 2024
https://github.com/VyacheslavLevytskyy updated https://github.com/llvm/llvm-project/pull/95055
>From 4342cb8fa020940c06fe35a2d0638fd33c3907e3 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Mon, 10 Jun 2024 03:23:55 -0700
Subject: [PATCH 1/4] ensure DuplicatesTracker is working with TypedPointers
pointee types
---
.../lib/Target/SPIRV/SPIRVDuplicatesTracker.h | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h
index 2ec3fb35ca045..3c8405fadd44e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h
+++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h
@@ -16,6 +16,7 @@
#include "MCTargetDesc/SPIRVBaseInfo.h"
#include "MCTargetDesc/SPIRVMCTargetDesc.h"
+#include "SPIRVUtils.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
@@ -285,10 +286,13 @@ class SPIRVGeneralDuplicatesTracker {
TT.add(Ty, MF, R);
}
- void add(const Type *PointerElementType, unsigned AddressSpace,
+ void add(const Type *PointeeTy, unsigned AddressSpace,
const MachineFunction *MF, Register R) {
- ST.add(SPIRV::PointerTypeDescriptor(PointerElementType, AddressSpace), MF,
- R);
+ if (isUntypedPointerTy(PointeeTy))
+ PointeeTy =
+ TypedPointerType::get(IntegerType::getInt8Ty(PointeeTy->getContext()),
+ getPointerAddressSpace(PointeeTy));
+ ST.add(SPIRV::PointerTypeDescriptor(PointeeTy, AddressSpace), MF, R);
}
void add(const Constant *C, const MachineFunction *MF, Register R) {
@@ -320,10 +324,13 @@ class SPIRVGeneralDuplicatesTracker {
return TT.find(const_cast<Type *>(Ty), MF);
}
- Register find(const Type *PointerElementType, unsigned AddressSpace,
+ Register find(const Type *PointeeTy, unsigned AddressSpace,
const MachineFunction *MF) {
- return ST.find(
- SPIRV::PointerTypeDescriptor(PointerElementType, AddressSpace), MF);
+ if (isUntypedPointerTy(PointeeTy))
+ PointeeTy =
+ TypedPointerType::get(IntegerType::getInt8Ty(PointeeTy->getContext()),
+ getPointerAddressSpace(PointeeTy));
+ return ST.find(SPIRV::PointerTypeDescriptor(PointeeTy, AddressSpace), MF);
}
Register find(const Constant *C, const MachineFunction *MF) {
>From afe36853d3fc678e760c24d6c6c890bf9ad4d23a Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Mon, 10 Jun 2024 10:20:32 -0700
Subject: [PATCH 2/4] add built-ins: Address Space Qualifier Functions/Pointers
Conversion Instructions
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 24 +++++++
llvm/lib/Target/SPIRV/SPIRVBuiltins.td | 12 ++++
.../SPIRV/transcoding/OpGenericCastToPtr.ll | 64 +++++++++++++++++++
3 files changed, 100 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 49838e685a6d2..bb66dd2e8eb13 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -798,6 +798,17 @@ static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
return true;
}
+/// Helper function for building OpGenericCastToPtr instruction.
+static bool buildCastToPtrInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry *GR) {
+ MIRBuilder.buildInstr(Opcode)
+ .addDef(Call->ReturnRegister)
+ .addUse(GR->getSPIRVTypeID(Call->ReturnType))
+ .addUse(Call->Arguments[0]);
+ return true;
+}
+
/// Helper function for building barriers, i.e., memory/control ordering
/// operations.
static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
@@ -1371,6 +1382,17 @@ static bool generateBarrierInst(const SPIRV::IncomingCall *Call,
return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
}
+static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call,
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry *GR) {
+ // Lookup the instruction opcode in the TableGen records.
+ const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
+ unsigned Opcode =
+ SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
+
+ return buildCastToPtrInst(Call, Opcode, MIRBuilder, GR);
+}
+
static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,
MachineIRBuilder &MIRBuilder,
SPIRVGlobalRegistry *GR) {
@@ -2322,6 +2344,8 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
case SPIRV::Barrier:
return generateBarrierInst(Call.get(), MIRBuilder, GR);
+ case SPIRV::CastToPtr:
+ return generateCastToPtrInst(Call.get(), MIRBuilder, GR);
case SPIRV::Dot:
return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
case SPIRV::Wave:
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index edc9e1a33d9f5..2edd2992425bd 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -59,6 +59,7 @@ def IntelSubgroups : BuiltinGroup;
def AtomicFloating : BuiltinGroup;
def GroupUniform : BuiltinGroup;
def KernelClock : BuiltinGroup;
+def CastToPtr : BuiltinGroup;
//===----------------------------------------------------------------------===//
// Class defining a demangled builtin record. The information in the record
@@ -595,6 +596,17 @@ defm : DemangledNativeBuiltin<"__spirv_GroupWaitEvents", OpenCL_std, AsyncCopy,
defm : DemangledNativeBuiltin<"__spirv_Load", OpenCL_std, LoadStore, 1, 3, OpLoad>;
defm : DemangledNativeBuiltin<"__spirv_Store", OpenCL_std, LoadStore, 2, 4, OpStore>;
+// Address Space Qualifier Functions/Pointers Conversion Instructions:
+defm : DemangledNativeBuiltin<"to_global", OpenCL_std, CastToPtr, 1, 1, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"to_local", OpenCL_std, CastToPtr, 1, 1, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"to_private", OpenCL_std, CastToPtr, 1, 1, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"__spirv_GenericCastToPtr_ToGlobal", OpenCL_std, CastToPtr, 2, 2, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"__spirv_GenericCastToPtr_ToLocal", OpenCL_std, CastToPtr, 2, 2, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"__spirv_GenericCastToPtr_ToPrivate", OpenCL_std, CastToPtr, 2, 2, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"__spirv_OpGenericCastToPtrExplicit_ToGlobal", OpenCL_std, CastToPtr, 2, 2, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"__spirv_OpGenericCastToPtrExplicit_ToLocal", OpenCL_std, CastToPtr, 2, 2, OpGenericCastToPtr>;
+defm : DemangledNativeBuiltin<"__spirv_OpGenericCastToPtrExplicit_ToPrivate", OpenCL_std, CastToPtr, 2, 2, OpGenericCastToPtr>;
+
//===----------------------------------------------------------------------===//
// Class defining a work/sub group builtin that should be translated into a
// SPIR-V instruction using the defined properties.
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
new file mode 100644
index 0000000000000..16fa160317dff
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
@@ -0,0 +1,64 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefixes=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-SPIRV-DAG: %[[#Char:]] = OpTypeInt 8 0
+; CHECK-SPIRV-DAG: %[[#GlobalPtr:]] = OpTypePointer CrossWorkgroup %[[#Char]]
+; CHECK-SPIRV-DAG: %[[#LocalPtr:]] = OpTypePointer Workgroup %[[#Char]]
+; CHECK-SPIRV-DAG: %[[#PrivatePtr:]] = OpTypePointer Function %[[#Char]]
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
+; CHECK-SPIRV: OpFunctionEnd
+
+%id = type { %arr }
+%arr = type { [1 x i64] }
+
+ at __spirv_BuiltInGlobalInvocationId = external local_unnamed_addr addrspace(1) constant <3 x i64>
+
+define spir_kernel void @foo(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
+entry:
+ %var = alloca i32
+ %p0 = load i64, ptr %_arg_GlobalId
+ %add = getelementptr inbounds i32, ptr addrspace(1) %_arg_GlobalA, i64 %p0
+ %p2 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId
+ %idx = getelementptr inbounds i32, ptr addrspace(1) %add, i64 %p2
+ %var1 = addrspacecast ptr addrspace(1) %idx to ptr addrspace(4)
+ %var2 = addrspacecast ptr addrspace(3) %_arg_LocalA to ptr addrspace(4)
+ %var3 = addrspacecast ptr %var to ptr addrspace(4)
+ %G = call spir_func ptr addrspace(1) @_Z33__spirv_GenericCastToPtr_ToGlobalPvi(ptr addrspace(4) %var1, i32 5)
+ %L = call spir_func ptr addrspace(3) @_Z32__spirv_GenericCastToPtr_ToLocalPvi(ptr addrspace(4) %var2, i32 4)
+ %P = call spir_func ptr @_Z34__spirv_GenericCastToPtr_ToPrivatePvi(ptr addrspace(4) %var3, i32 7)
+ ret void
+}
+
+define spir_kernel void @bar(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
+entry:
+ %var = alloca i32
+ %p0 = load i64, ptr %_arg_GlobalId
+ %add = getelementptr inbounds i32, ptr addrspace(1) %_arg_GlobalA, i64 %p0
+ %p2 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId
+ %idx = getelementptr inbounds i32, ptr addrspace(1) %add, i64 %p2
+ %var1 = addrspacecast ptr addrspace(1) %idx to ptr addrspace(4)
+ %var2 = addrspacecast ptr addrspace(3) %_arg_LocalA to ptr addrspace(4)
+ %var3 = addrspacecast ptr %var to ptr addrspace(4)
+ %G = call spir_func ptr addrspace(1) @_Z9to_globalPv(ptr addrspace(4) %var1, i32 5)
+ %L = call spir_func ptr addrspace(3) @_Z8to_localPv(ptr addrspace(4) %var2, i32 4)
+ %P = call spir_func ptr @_Z10to_privatePv(ptr addrspace(4) %var3, i32 7)
+ ret void
+}
+
+declare spir_func ptr addrspace(1) @_Z33__spirv_GenericCastToPtr_ToGlobalPvi(ptr addrspace(4), i32)
+declare spir_func ptr addrspace(3) @_Z32__spirv_GenericCastToPtr_ToLocalPvi(ptr addrspace(4), i32)
+declare spir_func ptr @_Z34__spirv_GenericCastToPtr_ToPrivatePvi(ptr addrspace(4), i32)
+
+declare spir_func ptr addrspace(1) @_Z9to_globalPv(ptr addrspace(4))
+declare spir_func ptr addrspace(3) @_Z8to_localPv(ptr addrspace(4))
+declare spir_func ptr @_Z10to_privatePv(ptr addrspace(4))
>From d9f880934141b7d99aab46c94a7fe52d3119c4e3 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Mon, 10 Jun 2024 15:12:38 -0700
Subject: [PATCH 3/4] implement OpGenericCastToPtr via builtins
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 131 +++++++++---------
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 35 +++--
llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp | 1 +
.../SPIRV/transcoding/OpGenericCastToPtr.ll | 14 ++
4 files changed, 107 insertions(+), 74 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index bb66dd2e8eb13..53752599b567b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -300,6 +300,72 @@ lookupBuiltin(StringRef DemangledCall,
return nullptr;
}
+static MachineInstr *getBlockStructInstr(Register ParamReg,
+ MachineRegisterInfo *MRI) {
+ // We expect the following sequence of instructions:
+ // %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
+ // or = G_GLOBAL_VALUE @block_literal_global
+ // %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
+ // %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
+ MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
+ assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
+ MI->getOperand(1).isReg());
+ Register BitcastReg = MI->getOperand(1).getReg();
+ MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
+ assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
+ BitcastMI->getOperand(2).isReg());
+ Register ValueReg = BitcastMI->getOperand(2).getReg();
+ MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
+ return ValueMI;
+}
+
+// Return an integer constant corresponding to the given register and
+// defined in spv_track_constant.
+// TODO: maybe unify with prelegalizer pass.
+static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {
+ MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
+ assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
+ DefMI->getOperand(2).isReg());
+ MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
+ assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
+ DefMI2->getOperand(1).isCImm());
+ return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
+}
+
+// Return type of the instruction result from spv_assign_type intrinsic.
+// TODO: maybe unify with prelegalizer pass.
+static const Type *getMachineInstrType(MachineInstr *MI) {
+ MachineInstr *NextMI = MI->getNextNode();
+ if (!NextMI)
+ return nullptr;
+ if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
+ if ((NextMI = NextMI->getNextNode()) == nullptr)
+ return nullptr;
+ Register ValueReg = MI->getOperand(0).getReg();
+ if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&
+ !isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||
+ NextMI->getOperand(1).getReg() != ValueReg)
+ return nullptr;
+ Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
+ assert(Ty && "Type is expected");
+ return Ty;
+}
+
+static const Type *getBlockStructType(Register ParamReg,
+ MachineRegisterInfo *MRI) {
+ // In principle, this information should be passed to us from Clang via
+ // an elementtype attribute. However, said attribute requires that
+ // the function call be an intrinsic, which is not. Instead, we rely on being
+ // able to trace this to the declaration of a variable: OpenCL C specification
+ // section 6.12.5 should guarantee that we can do this.
+ MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
+ if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
+ return MI->getOperand(1).getGlobal()->getType();
+ assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
+ "Blocks in OpenCL C must be traceable to allocation site");
+ return getMachineInstrType(MI);
+}
+
//===----------------------------------------------------------------------===//
// Helper functions for building misc instructions
//===----------------------------------------------------------------------===//
@@ -802,10 +868,11 @@ static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
static bool buildCastToPtrInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
MachineIRBuilder &MIRBuilder,
SPIRVGlobalRegistry *GR) {
+ Register Src = Call->Arguments[0];
MIRBuilder.buildInstr(Opcode)
.addDef(Call->ReturnRegister)
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
- .addUse(Call->Arguments[0]);
+ .addUse(Src);
return true;
}
@@ -1869,68 +1936,6 @@ static bool buildNDRange(const SPIRV::IncomingCall *Call,
.addUse(TmpReg);
}
-static MachineInstr *getBlockStructInstr(Register ParamReg,
- MachineRegisterInfo *MRI) {
- // We expect the following sequence of instructions:
- // %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
- // or = G_GLOBAL_VALUE @block_literal_global
- // %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
- // %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
- MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
- assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
- MI->getOperand(1).isReg());
- Register BitcastReg = MI->getOperand(1).getReg();
- MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
- assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
- BitcastMI->getOperand(2).isReg());
- Register ValueReg = BitcastMI->getOperand(2).getReg();
- MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
- return ValueMI;
-}
-
-// Return an integer constant corresponding to the given register and
-// defined in spv_track_constant.
-// TODO: maybe unify with prelegalizer pass.
-static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {
- MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
- assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
- DefMI->getOperand(2).isReg());
- MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
- assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
- DefMI2->getOperand(1).isCImm());
- return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
-}
-
-// Return type of the instruction result from spv_assign_type intrinsic.
-// TODO: maybe unify with prelegalizer pass.
-static const Type *getMachineInstrType(MachineInstr *MI) {
- MachineInstr *NextMI = MI->getNextNode();
- if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
- NextMI = NextMI->getNextNode();
- Register ValueReg = MI->getOperand(0).getReg();
- if (!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) ||
- NextMI->getOperand(1).getReg() != ValueReg)
- return nullptr;
- Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
- assert(Ty && "Type is expected");
- return Ty;
-}
-
-static const Type *getBlockStructType(Register ParamReg,
- MachineRegisterInfo *MRI) {
- // In principle, this information should be passed to us from Clang via
- // an elementtype attribute. However, said attribute requires that
- // the function call be an intrinsic, which is not. Instead, we rely on being
- // able to trace this to the declaration of a variable: OpenCL C specification
- // section 6.12.5 should guarantee that we can do this.
- MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
- if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
- return MI->getOperand(1).getGlobal()->getType();
- assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
- "Blocks in OpenCL C must be traceable to allocation site");
- return getMachineInstrType(MI);
-}
-
// TODO: maybe move to the global register.
static SPIRVType *
getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 7b8e3230bf553..685ab20b43394 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -69,7 +69,7 @@ class SPIRVEmitIntrinsics
DenseSet<Instruction *> AggrStores;
// deduce element type of untyped pointers
- Type *deduceElementType(Value *I);
+ Type *deduceElementType(Value *I, bool UnknownElemTypeI8);
Type *deduceElementTypeHelper(Value *I);
Type *deduceElementTypeHelper(Value *I, std::unordered_set<Value *> &Visited);
Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand,
@@ -105,7 +105,8 @@ class SPIRVEmitIntrinsics
void replaceMemInstrUses(Instruction *Old, Instruction *New, IRBuilder<> &B);
void processInstrAfterVisit(Instruction *I, IRBuilder<> &B);
- void insertAssignPtrTypeIntrs(Instruction *I, IRBuilder<> &B);
+ bool insertAssignPtrTypeIntrs(Instruction *I, IRBuilder<> &B,
+ bool UnknownElemTypeI8);
void insertAssignTypeIntrs(Instruction *I, IRBuilder<> &B);
void insertAssignPtrTypeTargetExt(TargetExtType *AssignedType, Value *V,
IRBuilder<> &B);
@@ -460,10 +461,10 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
return OrigTy;
}
-Type *SPIRVEmitIntrinsics::deduceElementType(Value *I) {
+Type *SPIRVEmitIntrinsics::deduceElementType(Value *I, bool UnknownElemTypeI8) {
if (Type *Ty = deduceElementTypeHelper(I))
return Ty;
- return IntegerType::getInt8Ty(I->getContext());
+ return UnknownElemTypeI8 ? IntegerType::getInt8Ty(I->getContext()) : nullptr;
}
// If the Instruction has Pointer operands with unresolved types, this function
@@ -1152,16 +1153,23 @@ void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV,
B.CreateIntrinsic(Intrinsic::spv_unref_global, GV.getType(), &GV);
}
-void SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *I,
- IRBuilder<> &B) {
+// Return true, if we can't decide what is the pointee type now and will get
+// back to the question later. Return false is spv_assign_ptr_type is not needed
+// or can be inserted immediately.
+bool SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *I,
+ IRBuilder<> &B,
+ bool UnknownElemTypeI8) {
reportFatalOnTokenType(I);
if (!isPointerTy(I->getType()) || !requireAssignType(I) ||
isa<BitCastInst>(I))
- return;
+ return false;
setInsertPointAfterDef(B, I);
- Type *ElemTy = deduceElementType(I);
- buildAssignPtr(B, ElemTy, I);
+ if (Type *ElemTy = deduceElementType(I, UnknownElemTypeI8)) {
+ buildAssignPtr(B, ElemTy, I);
+ return false;
+ }
+ return true;
}
void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I,
@@ -1199,7 +1207,7 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I,
buildAssignPtr(B, PType->getElementType(), Op);
} else if (isPointerTy(OpTy)) {
Type *ElemTy = GR->findDeducedElementType(Op);
- buildAssignPtr(B, ElemTy ? ElemTy : deduceElementType(Op), Op);
+ buildAssignPtr(B, ElemTy ? ElemTy : deduceElementType(Op, true), Op);
} else {
CallInst *AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type,
{OpTy}, Op, Op, {}, B);
@@ -1395,10 +1403,15 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
if (isConvergenceIntrinsic(I))
continue;
- insertAssignPtrTypeIntrs(I, B);
+ bool Postpone = insertAssignPtrTypeIntrs(I, B, false);
+ // if Postpone is true, we can't decide on pointee type yet
insertAssignTypeIntrs(I, B);
insertPtrCastOrAssignTypeInstr(I, B);
insertSpirvDecorations(I, B);
+ // if instruction requires a pointee type set, let's check if we know it
+ // already, and force it to be i8 if not
+ if (Postpone && !GR->findAssignPtrTypeInstr(I))
+ insertAssignPtrTypeIntrs(I, B, true);
}
for (auto &I : instructions(Func))
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index 5ccbaf12ddee2..4383d1c5c0e25 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -339,6 +339,7 @@ void SPIRVTargetLowering::finalizeLowering(MachineFunction &MF) const {
GR.getSPIRVTypeForVReg(MI.getOperand(1).getReg()));
break;
case SPIRV::OpPtrCastToGeneric:
+ case SPIRV::OpGenericCastToPtr:
validateAccessChain(STI, MRI, GR, MI);
break;
case SPIRV::OpInBoundsPtrAccessChain:
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
index 16fa160317dff..60948ca140e08 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
@@ -18,6 +18,11 @@
; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
; CHECK-SPIRV: OpFunctionEnd
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
+; CHECK-SPIRV: OpFunctionEnd
+
%id = type { %arr }
%arr = type { [1 x i64] }
@@ -55,6 +60,15 @@ entry:
ret void
}
+define spir_kernel void @test(ptr addrspace(3) %_arg_LocalA) {
+entry:
+ %var = alloca i32, align 4
+ %var3 = addrspacecast ptr %var to ptr addrspace(4)
+ %P1 = call spir_func ptr @_Z34__spirv_GenericCastToPtr_ToPrivatePvi(ptr addrspace(4) %var3, i32 7)
+ %P2 = call spir_func ptr @_Z10to_privatePv(ptr addrspace(4) %var3, i32 7)
+ ret void
+}
+
declare spir_func ptr addrspace(1) @_Z33__spirv_GenericCastToPtr_ToGlobalPvi(ptr addrspace(4), i32)
declare spir_func ptr addrspace(3) @_Z32__spirv_GenericCastToPtr_ToLocalPvi(ptr addrspace(4), i32)
declare spir_func ptr @_Z34__spirv_GenericCastToPtr_ToPrivatePvi(ptr addrspace(4), i32)
>From db33958db7945e0b3ba44766c7ccd22ff175d60a Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Tue, 11 Jun 2024 03:24:16 -0700
Subject: [PATCH 4/4] complete insertion of OpGenericCastToPtr using builtin
functions
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 27 +---
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 17 +++
.../SPIRV/transcoding/OpGenericCastToPtr.ll | 124 +++++++++++++-----
3 files changed, 115 insertions(+), 53 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 53752599b567b..6bb3e215240a8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -864,18 +864,6 @@ static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
return true;
}
-/// Helper function for building OpGenericCastToPtr instruction.
-static bool buildCastToPtrInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
- MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
- Register Src = Call->Arguments[0];
- MIRBuilder.buildInstr(Opcode)
- .addDef(Call->ReturnRegister)
- .addUse(GR->getSPIRVTypeID(Call->ReturnType))
- .addUse(Src);
- return true;
-}
-
/// Helper function for building barriers, i.e., memory/control ordering
/// operations.
static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
@@ -1450,14 +1438,11 @@ static bool generateBarrierInst(const SPIRV::IncomingCall *Call,
}
static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call,
- MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
- // Lookup the instruction opcode in the TableGen records.
- const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
- unsigned Opcode =
- SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
-
- return buildCastToPtrInst(Call, Opcode, MIRBuilder, GR);
+ MachineIRBuilder &MIRBuilder) {
+ MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
+ .addDef(Call->ReturnRegister)
+ .addUse(Call->Arguments[0]);
+ return true;
}
static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,
@@ -2350,7 +2335,7 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
case SPIRV::Barrier:
return generateBarrierInst(Call.get(), MIRBuilder, GR);
case SPIRV::CastToPtr:
- return generateCastToPtrInst(Call.get(), MIRBuilder, GR);
+ return generateCastToPtrInst(Call.get(), MIRBuilder);
case SPIRV::Dot:
return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
case SPIRV::Wave:
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 685ab20b43394..5c10e04325d51 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -368,6 +368,23 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
if (Ty)
break;
}
+ } else if (auto *CI = dyn_cast<CallInst>(I)) {
+ static StringMap<unsigned> ResTypeByArg = {
+ {"to_global", 0},
+ {"to_local", 0},
+ {"to_private", 0},
+ {"__spirv_GenericCastToPtr_ToGlobal", 0},
+ {"__spirv_GenericCastToPtr_ToLocal", 0},
+ {"__spirv_GenericCastToPtr_ToPrivate", 0}};
+ // TODO: maybe improve performance by caching demangled names
+ if (Function *CalledF = CI->getCalledFunction()) {
+ std::string DemangledName =
+ getOclOrSpirvBuiltinDemangledName(CalledF->getName());
+ auto AsArgIt = ResTypeByArg.find(DemangledName);
+ if (AsArgIt != ResTypeByArg.end())
+ Ty = deduceElementTypeHelper(CI->getArgOperand(AsArgIt->second),
+ Visited);
+ }
}
// remember the found relationship
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
index 60948ca140e08..e3a82b3577701 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
@@ -2,33 +2,42 @@
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
; CHECK-SPIRV-DAG: %[[#Char:]] = OpTypeInt 8 0
-; CHECK-SPIRV-DAG: %[[#GlobalPtr:]] = OpTypePointer CrossWorkgroup %[[#Char]]
-; CHECK-SPIRV-DAG: %[[#LocalPtr:]] = OpTypePointer Workgroup %[[#Char]]
-; CHECK-SPIRV-DAG: %[[#PrivatePtr:]] = OpTypePointer Function %[[#Char]]
+; CHECK-SPIRV-DAG: %[[#GlobalCharPtr:]] = OpTypePointer CrossWorkgroup %[[#Char]]
+; CHECK-SPIRV-DAG: %[[#LocalCharPtr:]] = OpTypePointer Workgroup %[[#Char]]
+; CHECK-SPIRV-DAG: %[[#PrivateCharPtr:]] = OpTypePointer Function %[[#Char]]
+; CHECK-SPIRV-DAG: %[[#GenericCharPtr:]] = OpTypePointer Generic %[[#Char]]
-; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
-; CHECK-SPIRV: OpFunctionEnd
+; CHECK-SPIRV-DAG: %[[#Int:]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: %[[#GlobalIntPtr:]] = OpTypePointer CrossWorkgroup %[[#Int]]
+; CHECK-SPIRV-DAG: %[[#PrivateIntPtr:]] = OpTypePointer Function %[[#Int]]
+; CHECK-SPIRV-DAG: %[[#GenericIntPtr:]] = OpTypePointer Generic %[[#Int]]
+
+%id = type { %arr }
+%arr = type { [1 x i64] }
+
+ at __spirv_BuiltInGlobalInvocationId = external local_unnamed_addr addrspace(1) constant <3 x i64>
+
+; Mangling
; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]]
; CHECK-SPIRV: OpFunctionEnd
; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivatePtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]]
; CHECK-SPIRV: OpFunctionEnd
-%id = type { %arr }
-%arr = type { [1 x i64] }
-
- at __spirv_BuiltInGlobalInvocationId = external local_unnamed_addr addrspace(1) constant <3 x i64>
-
-define spir_kernel void @foo(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
+define spir_kernel void @test1(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
entry:
%var = alloca i32
%p0 = load i64, ptr %_arg_GlobalId
@@ -44,7 +53,7 @@ entry:
ret void
}
-define spir_kernel void @bar(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
+define spir_kernel void @test2(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
entry:
%var = alloca i32
%p0 = load i64, ptr %_arg_GlobalId
@@ -54,18 +63,9 @@ entry:
%var1 = addrspacecast ptr addrspace(1) %idx to ptr addrspace(4)
%var2 = addrspacecast ptr addrspace(3) %_arg_LocalA to ptr addrspace(4)
%var3 = addrspacecast ptr %var to ptr addrspace(4)
- %G = call spir_func ptr addrspace(1) @_Z9to_globalPv(ptr addrspace(4) %var1, i32 5)
- %L = call spir_func ptr addrspace(3) @_Z8to_localPv(ptr addrspace(4) %var2, i32 4)
- %P = call spir_func ptr @_Z10to_privatePv(ptr addrspace(4) %var3, i32 7)
- ret void
-}
-
-define spir_kernel void @test(ptr addrspace(3) %_arg_LocalA) {
-entry:
- %var = alloca i32, align 4
- %var3 = addrspacecast ptr %var to ptr addrspace(4)
- %P1 = call spir_func ptr @_Z34__spirv_GenericCastToPtr_ToPrivatePvi(ptr addrspace(4) %var3, i32 7)
- %P2 = call spir_func ptr @_Z10to_privatePv(ptr addrspace(4) %var3, i32 7)
+ %G = call spir_func ptr addrspace(1) @_Z9to_globalPv(ptr addrspace(4) %var1)
+ %L = call spir_func ptr addrspace(3) @_Z8to_localPv(ptr addrspace(4) %var2)
+ %P = call spir_func ptr @_Z10to_privatePv(ptr addrspace(4) %var3)
ret void
}
@@ -76,3 +76,63 @@ declare spir_func ptr @_Z34__spirv_GenericCastToPtr_ToPrivatePvi(ptr addrspace(4
declare spir_func ptr addrspace(1) @_Z9to_globalPv(ptr addrspace(4))
declare spir_func ptr addrspace(3) @_Z8to_localPv(ptr addrspace(4))
declare spir_func ptr @_Z10to_privatePv(ptr addrspace(4))
+
+; No mangling
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV: OpFunctionEnd
+
+define spir_kernel void @test3(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
+entry:
+ %var = alloca i32
+ %p0 = load i64, ptr %_arg_GlobalId
+ %add = getelementptr inbounds i32, ptr addrspace(1) %_arg_GlobalA, i64 %p0
+ %p2 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId
+ %idx = getelementptr inbounds i32, ptr addrspace(1) %add, i64 %p2
+ %var1 = addrspacecast ptr addrspace(1) %idx to ptr addrspace(4)
+ %var2 = addrspacecast ptr addrspace(3) %_arg_LocalA to ptr addrspace(4)
+ %var3 = addrspacecast ptr %var to ptr addrspace(4)
+ %G = call spir_func ptr addrspace(1) @__spirv_GenericCastToPtr_ToGlobal(ptr addrspace(4) %var1, i32 5)
+ %L = call spir_func ptr addrspace(3) @__spirv_GenericCastToPtr_ToLocal(ptr addrspace(4) %var2, i32 4)
+ %P = call spir_func ptr @__spirv_GenericCastToPtr_ToPrivate(ptr addrspace(4) %var3, i32 7)
+ ret void
+}
+
+define spir_kernel void @test4(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
+entry:
+ %var = alloca i32
+ %p0 = load i64, ptr %_arg_GlobalId
+ %add = getelementptr inbounds i32, ptr addrspace(1) %_arg_GlobalA, i64 %p0
+ %p2 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId
+ %idx = getelementptr inbounds i32, ptr addrspace(1) %add, i64 %p2
+ %var1 = addrspacecast ptr addrspace(1) %idx to ptr addrspace(4)
+ %var2 = addrspacecast ptr addrspace(3) %_arg_LocalA to ptr addrspace(4)
+ %var3 = addrspacecast ptr %var to ptr addrspace(4)
+ %G = call spir_func ptr addrspace(1) @to_global(ptr addrspace(4) %var1)
+ %L = call spir_func ptr addrspace(3) @to_local(ptr addrspace(4) %var2)
+ %P = call spir_func ptr @to_private(ptr addrspace(4) %var3)
+ ret void
+}
+
+declare spir_func ptr addrspace(1) @__spirv_GenericCastToPtr_ToGlobal(ptr addrspace(4), i32)
+declare spir_func ptr addrspace(3) @__spirv_GenericCastToPtr_ToLocal(ptr addrspace(4), i32)
+declare spir_func ptr @__spirv_GenericCastToPtr_ToPrivate(ptr addrspace(4), i32)
+
+declare spir_func ptr addrspace(1) @to_global(ptr addrspace(4))
+declare spir_func ptr addrspace(3) @to_local(ptr addrspace(4))
+declare spir_func ptr @to_private(ptr addrspace(4))
More information about the llvm-commits
mailing list