[llvm] [SPIR-V] Implement SPV_KHR_untyped_pointers extension (PR #201233)
Dmitry Sidorov via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 3 02:06:21 PDT 2026
https://github.com/MrSidims updated https://github.com/llvm/llvm-project/pull/201233
>From 04741aaef2ff38940a8e8d89bdd8e9813d4cbb18 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Thu, 28 May 2026 07:27:44 -0500
Subject: [PATCH 1/3] [SPIR-V] Implement SPV_KHR_untyped_pointers extension
Add support for SPV_KHR_untyped_pointers, which replaces typed
OpTypePointer with OpTypeUntypedPointerKHR for ordinary data pointers.
An untyped pointer no longer carries its pointee, so the element type
that used to live in the pointer type is now supplied per instruction:
the Data Type of OpUntypedVariableKHR, the Base Type of the
OpUntyped*AccessChainKHR family, the element size for async copy and
prefetch, and so on. These are taken from the element types the backend
already deduces for the typed path.
Pointers whose pointee has to keep its type stay typed even with the extension
enabled: opaque builtin types (images, samplers, pipes, events), function
pointers, and byval/byref/sret aggregate arguments.
The implementation targets the compute path for now.
The Shader/Vulkan path is not covered. This includes OpUntypedArrayLengthKHR.
Assisted-by: Claude Code, Opus 4.8
---
llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp | 3 +-
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 146 +++++++++++++----
llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp | 6 +-
llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp | 4 +-
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 6 +-
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 130 +++++++++++++--
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h | 56 ++++++-
llvm/lib/Target/SPIRV/SPIRVIRMapping.h | 6 +
llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp | 6 +-
llvm/lib/Target/SPIRV/SPIRVInstrInfo.td | 27 +++
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 154 ++++++++++++++----
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 23 ++-
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 29 +++-
.../lib/Target/SPIRV/SPIRVSymbolicOperands.td | 2 +
...gcnspirv-feature-predicate-specconstant.ll | 44 ++---
.../fp-select-untyped-ptr.ll | 27 +++
.../untyped-access-chain.ll | 64 ++++++++
.../untyped-ptr-access-chain-base-type.ll | 39 +++++
.../untyped-ptr-addrspacecast.ll | 21 +++
.../untyped-ptr-array.ll | 64 ++++++++
.../untyped-ptr-async-copy.ll | 17 ++
.../untyped-ptr-atomics.ll | 21 +++
.../untyped-ptr-basics.ll | 40 +++++
.../untyped-ptr-byval-arg.ll | 41 +++++
.../untyped-ptr-coopmatrix.ll | 15 ++
.../untyped-ptr-element-type.ll | 31 ++++
.../untyped-ptr-global-gep-atomic.ll | 28 ++++
.../untyped-ptr-global.ll | 19 +++
.../untyped-ptr-memcpy.ll | 12 ++
.../untyped-ptr-no-ext.ll | 39 +++++
.../untyped-ptr-prefetch.ll | 28 ++++
.../untyped-ptr-ptr-ops.ll | 32 ++++
.../untyped-ptr-struct.ll | 59 +++++++
.../untyped-ptr-types.ll | 59 +++++++
.../untyped-ptr-vector.ll | 52 ++++++
.../untyped-variable.ll | 84 ++++++++++
.../CodeGen/SPIRV/externally-initialized.ll | 2 +-
.../CodeGen/SPIRV/fembed-bitcode-marker.ll | 4 +-
llvm/test/CodeGen/SPIRV/fembed-bitcode.ll | 15 +-
llvm/test/CodeGen/SPIRV/hip_dyn_lds.ll | 6 +-
40 files changed, 1333 insertions(+), 128 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-select-untyped-ptr.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-access-chain.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-access-chain-base-type.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-addrspacecast.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-array.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-async-copy.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-atomics.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-basics.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-byval-arg.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-coopmatrix.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-element-type.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global-gep-atomic.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-memcpy.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-no-ext.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-prefetch.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-ptr-ops.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-struct.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-types.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-vector.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-variable.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index fa2a356521a9b..30722c2464103 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -376,7 +376,8 @@ void SPIRVAsmPrinter::outputEntryPoints() {
// Find all OpVariable IDs with required StorageClass.
DenseSet<MCRegister> InterfaceIDs;
for (const MachineInstr *MI : MAI->GlobalVarList) {
- assert(MI->getOpcode() == SPIRV::OpVariable);
+ assert(MI->getOpcode() == SPIRV::OpVariable ||
+ MI->getOpcode() == SPIRV::OpUntypedVariableKHR);
auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
MI->getOperand(2).getImm());
// Before version 1.4, the interface's storage classes are limited to
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 1febfc21ed3b0..c35debbf3121f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -731,16 +731,19 @@ static bool buildAtomicCompareExchangeInst(
SPIRVTypeInst SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
LLT DesiredLLT = MRI->getType(Desired);
- assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==
- SPIRV::OpTypePointer);
+ [[maybe_unused]] auto IsPtrTy = [](unsigned Op) {
+ return Op == SPIRV::OpTypePointer || Op == SPIRV::OpTypeUntypedPointerKHR;
+ };
+ assert(IsPtrTy(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode()));
unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();
(void)ExpectedType;
- assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt
- : ExpectedType == SPIRV::OpTypePointer);
+ assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt : IsPtrTy(ExpectedType));
assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
SPIRVTypeInst SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
- assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");
+ assert((SpvObjectPtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR ||
+ SpvObjectPtrTy->getOperand(2).isReg()) &&
+ "SPIRV type is expected");
auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
SpvObjectPtrTy->getOperand(1).getImm());
auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
@@ -1255,6 +1258,38 @@ static bool generateExtInst(const SPIRV::IncomingCall *Call,
: SPIRV::OpenCLExtInst::fmax;
}
+ // ExtInst prefetch cannot take an untyped pointer, so emit
+ // OpUntypedPrefetchKHR with Num Bytes = num elements * element byte size.
+ if (Number == SPIRV::OpenCLExtInst::prefetch && Call->Arguments.size() >= 2) {
+ Register PtrReg = Call->Arguments[0];
+ SPIRVTypeInst PtrTy = GR->getSPIRVTypeForVReg(PtrReg);
+ if (PtrTy && PtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR) {
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ Register NumElems = Call->Arguments[1];
+ SPIRVTypeInst SizeTy = GR->getSPIRVTypeForVReg(NumElems);
+ unsigned ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(0));
+ Register NumBytes = NumElems;
+ if (ElemBytes > 1 && SizeTy) {
+ Register ElemBytesReg = GR->buildConstantInt(ElemBytes, MIRBuilder,
+ SizeTy, /*EmitIR=*/true);
+ Register Mul =
+ MRI->createGenericVirtualRegister(MRI->getType(NumElems));
+ MRI->setRegClass(Mul, GR->getRegClass(SizeTy));
+ GR->assignSPIRVTypeToVReg(SizeTy, Mul, MIRBuilder.getMF());
+ MIRBuilder.buildInstr(TargetOpcode::G_MUL)
+ .addDef(Mul)
+ .addUse(NumElems)
+ .addUse(ElemBytesReg);
+ updateRegType(Mul, nullptr, SizeTy, GR, MIRBuilder, *MRI);
+ NumBytes = Mul;
+ }
+ MIRBuilder.buildInstr(SPIRV::OpUntypedPrefetchKHR)
+ .addUse(PtrReg)
+ .addUse(NumBytes);
+ return true;
+ }
+ }
+
Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType);
SmallVector<Register> Arguments =
getBuiltinCallArguments(Call, Number, MIRBuilder, GR);
@@ -2030,7 +2065,8 @@ static void buildSRetInst(unsigned Opcode, Register SRetReg, Register Op1Reg,
// OpStore RetVariable Res
static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call,
MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ SPIRVGlobalRegistry *GR,
+ const CallBase &CB) {
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
unsigned Opcode =
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
@@ -2038,6 +2074,13 @@ static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call,
Register SRetReg = Call->Arguments[0];
SPIRVTypeInst PtrRetType = GR->getSPIRVTypeForVReg(SRetReg);
SPIRVTypeInst RetType = GR->getPointeeType(PtrRetType);
+ if (!RetType) {
+ // For an untyped sret pointer, recover the result type from the deduced
+ // type.
+ if (Type *ElemTy = GR->findDeducedElementType(CB.getArgOperand(0)))
+ RetType = GR->getOrCreateSPIRVType(
+ ElemTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
+ }
if (!RetType)
report_fatal_error("The first parameter must be a pointer");
if (RetType->getOpcode() != SPIRV::OpTypeStruct)
@@ -2077,7 +2120,8 @@ static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call,
// RetType is a struct with two members of the same type as the operands.
static bool generateMulExtendedInst(const SPIRV::IncomingCall *Call,
MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ SPIRVGlobalRegistry *GR,
+ const CallBase &CB) {
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
unsigned Opcode =
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
@@ -2094,6 +2138,13 @@ static bool generateMulExtendedInst(const SPIRV::IncomingCall *Call,
Register SRetReg = Call->Arguments[0];
SPIRVTypeInst PtrRetType = GR->getSPIRVTypeForVReg(SRetReg);
RetType = GR->getPointeeType(PtrRetType);
+ if (!RetType) {
+ // For an untyped sret pointer, recover the result type from the deduced
+ // type.
+ if (Type *ElemTy = GR->findDeducedElementType(CB.getArgOperand(0)))
+ RetType = GR->getOrCreateSPIRVType(
+ ElemTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
+ }
if (!RetType)
report_fatal_error("The first parameter must be a pointer");
} else {
@@ -2646,7 +2697,7 @@ static bool generateBlockingPipesInst(const SPIRV::IncomingCall *Call,
static bool buildAPFixedPointInst(const SPIRV::IncomingCall *Call,
unsigned Opcode, MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ SPIRVGlobalRegistry *GR, const CallBase &CB) {
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
SmallVector<uint32_t, 1> ImmArgs;
Register InputReg = Call->Arguments[0];
@@ -2658,6 +2709,12 @@ static bool buildAPFixedPointInst(const SPIRV::IncomingCall *Call,
Register ActualRetValReg = MRI->createGenericVirtualRegister(ValTy);
SPIRVTypeInst InstructionType =
GR->getPointeeType(GR->getSPIRVTypeForVReg(InputReg));
+ if (!InstructionType)
+ // For an untyped sret pointer, recover the result type from the deduced
+ // type.
+ if (Type *ElemTy = GR->findDeducedElementType(CB.getArgOperand(0)))
+ InstructionType = GR->getOrCreateSPIRVType(
+ ElemTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
InputReg = Call->Arguments[1];
auto InputType = GR->getTypeForSPIRVType(GR->getSPIRVTypeForVReg(InputReg));
Register PtrInputReg;
@@ -2707,12 +2764,13 @@ static bool buildAPFixedPointInst(const SPIRV::IncomingCall *Call,
static bool generateAPFixedPointInst(const SPIRV::IncomingCall *Call,
MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ SPIRVGlobalRegistry *GR,
+ const CallBase &CB) {
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
unsigned Opcode =
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
- return buildAPFixedPointInst(Call, Opcode, MIRBuilder, GR);
+ return buildAPFixedPointInst(Call, Opcode, MIRBuilder, GR, CB);
}
static bool
@@ -2784,8 +2842,8 @@ static bool generatePredicatedLoadStoreInst(const SPIRV::IncomingCall *Call,
}
static bool buildNDRange(const SPIRV::IncomingCall *Call,
- MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR,
+ const CallBase &CB) {
// The OpenCL ndrange_*D functions are overloaded and support 1D, 2D, and 3D
// variants, accepting 1 to 3 arguments:
// (global_work_size)
@@ -2876,7 +2934,9 @@ static bool buildNDRange(const SPIRV::IncomingCall *Call,
return Reg;
}
- assert(GR->getSPIRVTypeForVReg(Reg)->getOpcode() == SPIRV::OpTypePointer &&
+ assert((GR->getSPIRVTypeForVReg(Reg)->getOpcode() == SPIRV::OpTypePointer ||
+ GR->getSPIRVTypeForVReg(Reg)->getOpcode() ==
+ SPIRV::OpTypeUntypedPointerKHR) &&
"Only pointer types are supported for loading values");
Register Ptr = Reg;
@@ -2909,6 +2969,13 @@ static bool buildNDRange(const SPIRV::IncomingCall *Call,
Register SRetReg = Call->Arguments[SRetArgIdx];
SPIRVTypeInst SRetPtrType = GR->getSPIRVTypeForVReg(SRetReg);
SPIRVTypeInst SRetType = GR->getPointeeType(SRetPtrType);
+ if (!SRetType) {
+ // For an untyped sret pointer, recover the nd_range type from the deduced
+ // type.
+ if (Type *ElemTy = GR->findDeducedElementType(CB.getArgOperand(SRetArgIdx)))
+ SRetType = GR->getOrCreateSPIRVType(
+ ElemTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
+ }
Register TmpReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
GR->assignSPIRVTypeToVReg(SRetType, TmpReg, MF);
@@ -3022,7 +3089,7 @@ static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,
static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ SPIRVGlobalRegistry *GR, const CallBase &CB) {
// Lookup the instruction opcode in the TableGen records.
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
unsigned Opcode =
@@ -3052,7 +3119,7 @@ static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
.addUse(Call->Arguments[1])
.addUse(Call->Arguments[2]);
case SPIRV::OpBuildNDRange:
- return buildNDRange(Call, MIRBuilder, GR);
+ return buildNDRange(Call, MIRBuilder, GR, CB);
case SPIRV::OpEnqueueKernel:
return buildEnqueueKernel(Call, MIRBuilder, GR);
default:
@@ -3062,7 +3129,7 @@ static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
MachineIRBuilder &MIRBuilder,
- SPIRVGlobalRegistry *GR) {
+ SPIRVGlobalRegistry *GR, const CallBase &CB) {
// Lookup the instruction opcode in the TableGen records.
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
unsigned Opcode =
@@ -3085,17 +3152,40 @@ static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
unsigned NumArgs = Call->Arguments.size();
Register EventReg = Call->Arguments[NumArgs - 1];
- bool Res = MIRBuilder.buildInstr(Opcode)
+ Register NumElemReg = Call->Arguments[2];
+
+ // Untyped pointers use OpUntypedGroupAsyncCopyKHR, which adds an explicit
+ // Element Num Bytes operand.
+ SPIRVTypeInst DestPtrTy = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
+ bool IsUntyped =
+ DestPtrTy && DestPtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
+ SPIRVTypeInst SizeTy = GR->getSPIRVTypeForVReg(NumElemReg);
+ Register StrideReg =
+ Call->Arguments.size() > 4
+ ? Call->Arguments[3]
+ : (IsUntyped ? GR->buildConstantInt(1, MIRBuilder, SizeTy,
+ /*EmitIR=*/false)
+ : buildConstantIntReg32(1, MIRBuilder, GR));
+
+ auto MIB = MIRBuilder
+ .buildInstr(IsUntyped ? SPIRV::OpUntypedGroupAsyncCopyKHR
+ : SPIRV::OpGroupAsyncCopy)
.addDef(Call->ReturnRegister)
.addUse(TypeReg)
.addUse(Scope)
.addUse(Call->Arguments[0])
- .addUse(Call->Arguments[1])
- .addUse(Call->Arguments[2])
- .addUse(Call->Arguments.size() > 4
- ? Call->Arguments[3]
- : buildConstantIntReg32(1, MIRBuilder, GR))
- .addUse(EventReg);
+ .addUse(Call->Arguments[1]);
+ if (IsUntyped) {
+ // Element Num Bytes from the deduced element type of dest (or source).
+ unsigned ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(0));
+ if (!ElemBytes)
+ ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(1));
+ if (!ElemBytes)
+ ElemBytes = 1;
+ MIB.addUse(GR->buildConstantInt(ElemBytes, MIRBuilder, SizeTy,
+ /*EmitIR=*/false));
+ }
+ bool Res = MIB.addUse(NumElemReg).addUse(StrideReg).addUse(EventReg);
if (NewType)
updateRegType(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
MIRBuilder.getMF().getRegInfo());
@@ -3478,9 +3568,9 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
case SPIRV::Wave:
return generateWaveInst(Call.get(), MIRBuilder, GR);
case SPIRV::ICarryBorrow:
- return generateICarryBorrowInst(Call.get(), MIRBuilder, GR);
+ return generateICarryBorrowInst(Call.get(), MIRBuilder, GR, CB);
case SPIRV::MulExtended:
- return generateMulExtendedInst(Call.get(), MIRBuilder, GR);
+ return generateMulExtendedInst(Call.get(), MIRBuilder, GR, CB);
case SPIRV::Arithmetic:
return generateArithmeticInst(Call.get(), MIRBuilder, GR);
case SPIRV::GetQuery:
@@ -3502,9 +3592,9 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
case SPIRV::SpecConstant:
return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
case SPIRV::Enqueue:
- return generateEnqueueInst(Call.get(), MIRBuilder, GR);
+ return generateEnqueueInst(Call.get(), MIRBuilder, GR, CB);
case SPIRV::AsyncCopy:
- return generateAsyncCopy(Call.get(), MIRBuilder, GR);
+ return generateAsyncCopy(Call.get(), MIRBuilder, GR, CB);
case SPIRV::Convert:
return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
case SPIRV::VectorLoadStore:
@@ -3534,7 +3624,7 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
case SPIRV::BlockingPipes:
return generateBlockingPipesInst(Call.get(), MIRBuilder, GR);
case SPIRV::ArbitraryPrecisionFixedPoint:
- return generateAPFixedPointInst(Call.get(), MIRBuilder, GR);
+ return generateAPFixedPointInst(Call.get(), MIRBuilder, GR, CB);
case SPIRV::ImageChannelDataTypes:
return generateImageChannelDataTypeInst(Call.get(), MIRBuilder, GR);
case SPIRV::ArbitraryFloatingPoint:
diff --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
index 14c33b5190f5e..d703c0d3bb6a4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
@@ -185,9 +185,13 @@ static SPIRVTypeInst getArgSPIRVType(const Function &F, unsigned ArgIdx,
// spv_assign_ptr_type intrinsic or otherwise use default pointer element
// type.
if (hasPointeeTypeAttr(Arg)) {
+ // byval/byref/sret carry the aggregate layout in the pointee type, so keep
+ // a typed pointer here. An untyped one drops the type and breaks the
+ // argument ABI on the way back from SPIR-V.
return GR->getOrCreateSPIRVPointerType(
getPointeeTypeByAttr(Arg), MIRBuilder,
- addressSpaceToStorageClass(getPointerAddressSpace(ArgType), ST));
+ addressSpaceToStorageClass(getPointerAddressSpace(ArgType), ST),
+ /*ForceTyped=*/true);
}
for (auto User : Arg->users()) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index bced67087a6d2..6217b31e20c89 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -180,7 +180,9 @@ static const StringMap<SPIRV::Extension::Extension> SPIRVExtensionMap = {
{"SPV_AMD_weak_linkage", SPIRV::Extension::Extension::SPV_AMD_weak_linkage},
{"SPV_KHR_abort", SPIRV::Extension::Extension::SPV_KHR_abort},
{"SPV_KHR_poison_freeze",
- SPIRV::Extension::Extension::SPV_KHR_poison_freeze}};
+ SPIRV::Extension::Extension::SPV_KHR_poison_freeze},
+ {"SPV_KHR_untyped_pointers",
+ SPIRV::Extension::Extension::SPV_KHR_untyped_pointers}};
bool SPIRVExtensionsParser::parse(cl::Option &O, StringRef ArgName,
StringRef ArgValue, ExtensionSet &Vals) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 97fa49d8836fb..69c6df08988f5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -1012,7 +1012,11 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
Ty = BestTy;
} else if (auto *Ref = dyn_cast<SelectInst>(I)) {
for (Value *Op : {Ref->getTrueValue(), Ref->getFalseValue()}) {
- Ty = deduceElementTypeByUsersDeep(Op, Visited, UnknownElemTypeI8);
+ // A function pointer operand carries its function type directly. Other
+ // operands are deduced from their uses.
+ Ty = isa<Function>(Op)
+ ? deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8)
+ : deduceElementTypeByUsersDeep(Op, Visited, UnknownElemTypeI8);
if (Ty)
break;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 343311fb44475..8640eab30c95d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -330,7 +330,8 @@ SPIRVGlobalRegistry::getOpTypeVector(uint32_t NumElems, SPIRVTypeInst ElemType,
auto EleOpc = ElemType->getOpcode();
assert(NumElems >= 2 && "SPIR-V OpTypeVector requires at least 2 components");
- if (EleOpc == SPIRV::OpTypePointer) {
+ if (EleOpc == SPIRV::OpTypePointer ||
+ EleOpc == SPIRV::OpTypeUntypedPointerKHR) {
if (!cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget())
.canUseExtension(
SPIRV::Extension::SPV_INTEL_masked_gather_scatter)) {
@@ -841,10 +842,40 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
if (&GVBuilder.getMBB() != &EntryBB)
GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
- auto MIB = GVBuilder.buildInstr(SPIRV::OpVariable)
+ // Pointers to opaque types stay typed even with the extension on, so emit the
+ // untyped variant only when the result is actually an untyped pointer.
+ const SPIRVSubtarget &Subtarget =
+ cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
+ const bool UseUntypedPointers =
+ BaseType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
+ const unsigned VariableOpcode =
+ UseUntypedPointers ? SPIRV::OpUntypedVariableKHR : SPIRV::OpVariable;
+
+ auto MIB = GVBuilder.buildInstr(VariableOpcode)
.addDef(ResVReg)
- .addUse(getSPIRVTypeID(BaseType))
- .addImm(static_cast<uint32_t>(Storage));
+ .addUse(getSPIRVTypeID(BaseType));
+
+ // Add storage class (comes before DataType for OpUntypedVariableKHR).
+ MIB.addImm(static_cast<uint32_t>(Storage));
+
+ // For OpUntypedVariableKHR, add the Data Type operand after the storage
+ // class.
+ if (UseUntypedPointers) {
+ // Data Type = the global's value type.
+ SPIRVTypeInst DataType = getPointeeType(BaseType);
+ if (!DataType && GVar)
+ DataType = getOrCreateSPIRVType(GVar->getValueType(), GVBuilder,
+ SPIRV::AccessQualifier::ReadWrite,
+ /*EmitIR=*/false);
+ if (!DataType) {
+ // Use i8 as a last resort.
+ const SPIRVInstrInfo &TII = *Subtarget.getInstrInfo();
+ DataType = getOrCreateSPIRVIntegerType(
+ 8, MIRBuilder.getMF().front().front(), TII);
+ }
+ MIB.addUse(getSPIRVTypeID(DataType));
+ }
+
if (Init)
MIB.addUse(Init->getOperand(0).getReg());
// ISel may introduce a new register on this step, so we need to add it to
@@ -1079,6 +1110,13 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSpecialType(
SPIRVTypeInst SPIRVGlobalRegistry::getOpTypePointer(
SPIRV::StorageClass::StorageClass SC, SPIRVTypeInst ElemType,
MachineIRBuilder &MIRBuilder, Register Reg) {
+ // Check if we should use untyped pointers.
+ const SPIRVSubtarget &ST =
+ cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
+ if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers) &&
+ !isSpecialOpaqueElementType(ElemType))
+ return getOrCreateSPIRVUntypedPointerType(SC, MIRBuilder);
+
if (!Reg.isValid())
Reg = createTypeVReg(MIRBuilder);
@@ -1468,6 +1506,29 @@ unsigned SPIRVGlobalRegistry::getNumScalarOrVectorTotalBitWidth(
: 0;
}
+unsigned SPIRVGlobalRegistry::getSPIRVTypeByteSize(SPIRVTypeInst Type) const {
+ if (!Type)
+ return 0;
+ if (const llvm::Type *LLVMTy = getTypeForSPIRVType(Type))
+ return DL.getTypeStoreSize(const_cast<llvm::Type *>(LLVMTy))
+ .getFixedValue();
+ // Fall back to the scalar/vector bit width.
+ if (unsigned Bits = getNumScalarOrVectorTotalBitWidth(Type))
+ return (Bits + 7) / 8;
+ return 0;
+}
+
+bool SPIRVGlobalRegistry::isSpecialOpaqueElementType(
+ SPIRVTypeInst ElemType) const {
+ // A pointer to a function (SPV_INTEL_function_pointers) must keep its
+ // function type, which an untyped pointer cannot express.
+ if (ElemType && ElemType->getOpcode() == SPIRV::OpTypeFunction)
+ return true;
+ auto It = SPIRVToLLVMType.find(ElemType);
+ return It != SPIRVToLLVMType.end() && It->second &&
+ isSpecialOpaqueType(It->second);
+}
+
SPIRVTypeInst
SPIRVGlobalRegistry::retrieveScalarOrVectorIntType(SPIRVTypeInst Type) const {
SPIRVTypeInst ScalarType = getScalarOrVectorComponentType(Type);
@@ -1495,15 +1556,16 @@ bool SPIRVGlobalRegistry::isBitcastCompatible(SPIRVTypeInst Type1,
SPIRVTypeInst Type2) const {
if (!Type1 || !Type2)
return false;
+ auto IsPtr = [](unsigned Op) {
+ return Op == SPIRV::OpTypePointer || Op == SPIRV::OpTypeUntypedPointerKHR;
+ };
auto Op1 = Type1->getOpcode(), Op2 = Type2->getOpcode();
// Ignore difference between <1.5 and >=1.5 protocol versions:
// it's valid if either Result Type or Operand is a pointer, and the other
// is a pointer, an integer scalar, or an integer vector.
- if (Op1 == SPIRV::OpTypePointer &&
- (Op2 == SPIRV::OpTypePointer || retrieveScalarOrVectorIntType(Type2)))
+ if (IsPtr(Op1) && (IsPtr(Op2) || retrieveScalarOrVectorIntType(Type2)))
return true;
- if (Op2 == SPIRV::OpTypePointer &&
- (Op1 == SPIRV::OpTypePointer || retrieveScalarOrVectorIntType(Type1)))
+ if (IsPtr(Op2) && (IsPtr(Op1) || retrieveScalarOrVectorIntType(Type1)))
return true;
unsigned Bits1 = getNumScalarOrVectorTotalBitWidth(Type1),
Bits2 = getNumScalarOrVectorTotalBitWidth(Type2);
@@ -1513,7 +1575,9 @@ bool SPIRVGlobalRegistry::isBitcastCompatible(SPIRVTypeInst Type1,
SPIRV::StorageClass::StorageClass
SPIRVGlobalRegistry::getPointerStorageClass(Register VReg) const {
SPIRVTypeInst Type = getSPIRVTypeForVReg(VReg);
- assert(Type && Type->getOpcode() == SPIRV::OpTypePointer &&
+ assert(Type &&
+ (Type->getOpcode() == SPIRV::OpTypePointer ||
+ Type->getOpcode() == SPIRV::OpTypeUntypedPointerKHR) &&
Type->getOperand(1).isImm() && "Pointer type is expected");
return getPointerStorageClass(Type);
}
@@ -1987,21 +2051,22 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVVectorType(
}
SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
- const Type *BaseType, MachineInstr &I,
- SPIRV::StorageClass::StorageClass SC) {
+ const Type *BaseType, MachineInstr &I, SPIRV::StorageClass::StorageClass SC,
+ bool ForceTyped) {
MachineIRBuilder MIRBuilder(I);
- return getOrCreateSPIRVPointerType(BaseType, MIRBuilder, SC);
+ return getOrCreateSPIRVPointerType(BaseType, MIRBuilder, SC, ForceTyped);
}
SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
const Type *BaseType, MachineIRBuilder &MIRBuilder,
- SPIRV::StorageClass::StorageClass SC) {
+ SPIRV::StorageClass::StorageClass SC, bool ForceTyped) {
// TODO: Need to check if EmitIr should always be true.
SPIRVTypeInst SpirvBaseType = getOrCreateSPIRVType(
BaseType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
storageClassRequiresExplictLayout(SC), true);
assert(SpirvBaseType);
- return getOrCreateSPIRVPointerTypeInternal(SpirvBaseType, MIRBuilder, SC);
+ return getOrCreateSPIRVPointerTypeInternal(SpirvBaseType, MIRBuilder, SC,
+ ForceTyped);
}
SPIRVTypeInst SPIRVGlobalRegistry::changePointerStorageClass(
@@ -2024,14 +2089,23 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
assert(!storageClassRequiresExplictLayout(SC));
SPIRVTypeInst R = getOrCreateSPIRVPointerType(LLVMType, MIRBuilder, SC);
assert(
- getPointeeType(R) == BaseType &&
+ (R->getOpcode() == SPIRV::OpTypeUntypedPointerKHR ||
+ getPointeeType(R) == BaseType) &&
"The base type was not correctly laid out for the given storage class.");
return R;
}
SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerTypeInternal(
SPIRVTypeInst BaseType, MachineIRBuilder &MIRBuilder,
- SPIRV::StorageClass::StorageClass SC) {
+ SPIRV::StorageClass::StorageClass SC, bool ForceTyped) {
+ // Check if we should use untyped pointers.
+ const SPIRVSubtarget &ST =
+ cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
+ if (!ForceTyped &&
+ ST.canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers) &&
+ !isSpecialOpaqueElementType(BaseType))
+ return getOrCreateSPIRVUntypedPointerType(SC, MIRBuilder);
+
const Type *PointerElementType = getTypeForSPIRVType(BaseType);
unsigned AddressSpace = storageClassToAddressSpace(SC);
if (const MachineInstr *MI = findMI(PointerElementType, AddressSpace, CurMF))
@@ -2051,6 +2125,28 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerTypeInternal(
return finishCreatingSPIRVType(Ty, NewMI);
}
+SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVUntypedPointerType(
+ SPIRV::StorageClass::StorageClass SC, MachineIRBuilder &MIRBuilder) {
+ unsigned AddressSpace = storageClassToAddressSpace(SC);
+ // Use STK_UntypedPointer handle keyed by address space only
+ auto Handle = SPIRV::irhandle_untyped_pointer(AddressSpace);
+ if (const MachineInstr *MI = findMI(Handle, CurMF))
+ return MI;
+
+ Type *Ty = PointerType::get(MIRBuilder.getMF().getFunction().getContext(),
+ AddressSpace);
+ const MachineInstr *NewMI = createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return BuildMI(MIRBuilder.getMBB(), MIRBuilder.getInsertPt(),
+ MIRBuilder.getDebugLoc(),
+ MIRBuilder.getTII().get(SPIRV::OpTypeUntypedPointerKHR))
+ .addDef(createTypeVReg(CurMF->getRegInfo()))
+ .addImm(static_cast<uint32_t>(SC));
+ });
+ add(Handle, NewMI);
+ return finishCreatingSPIRVType(Ty, NewMI);
+}
+
Register SPIRVGlobalRegistry::getOrCreateUndef(MachineInstr &I,
SPIRVTypeInst SpvType,
const SPIRVInstrInfo &TII) {
@@ -2119,12 +2215,14 @@ LLT SPIRVGlobalRegistry::getRegType(SPIRVTypeInst SpvType) const {
case SPIRV::OpTypeBool:
return LLT::scalar(getScalarOrVectorBitWidth(SpvType));
case SPIRV::OpTypePointer:
+ case SPIRV::OpTypeUntypedPointerKHR:
return LLT::pointer(getAS(SpvType), getPointerSize());
case SPIRV::OpTypeVector: {
SPIRVTypeInst ElemType = getScalarOrVectorComponentType(SpvType);
LLT ET;
switch (ElemType ? ElemType->getOpcode() : 0) {
case SPIRV::OpTypePointer:
+ case SPIRV::OpTypeUntypedPointerKHR:
ET = LLT::pointer(getAS(ElemType), getPointerSize());
break;
case SPIRV::OpTypeInt:
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index f05bdc2cc9861..1ae4230fbe525 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -79,6 +79,12 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
// Maps composite values to deduced types where untyped pointers are replaced
// with typed ones.
DenseMap<Value *, Type *> DeducedNestedTys;
+
+ // Element type for each untyped-pointer register, which
+ // OpTypeUntypedPointerKHR omits but OpUntypedVariableKHR needs as a Data
+ // Type.
+ DenseMap<std::pair<const MachineFunction *, Register>, SPIRVTypeInst>
+ UntypedPointerElementTypes;
// Maps values to "assign type" calls, thus being a registry of created
// Intrinsic::spv_assign_ptr_type instructions.
DenseMap<Value *, CallInst *> AssignPtrTypeInstr;
@@ -208,6 +214,16 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
auto It = DeducedNestedTys.find(Val);
return It == DeducedNestedTys.end() ? nullptr : It->second;
}
+
+ // Store the element type associated with an untyped-pointer register.
+ void setUntypedPtrElementType(Register Reg, SPIRVTypeInst ElemType) {
+ UntypedPointerElementTypes[{CurMF, Reg}] = ElemType;
+ }
+ // Get the element type associated with an untyped-pointer register.
+ SPIRVTypeInst getUntypedPtrElementType(Register Reg) const {
+ auto It = UntypedPointerElementTypes.find({CurMF, Reg});
+ return It == UntypedPointerElementTypes.end() ? nullptr : It->second;
+ }
// - Find a type of the given Global value
Type *getDeducedGlobalValueType(const GlobalValue *Global) {
// we may know element type if it was deduced earlier
@@ -404,6 +420,20 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
// argument. Otherwise returns 0.
unsigned getNumScalarOrVectorTotalBitWidth(SPIRVTypeInst Type) const;
+ // Size in bytes of the LLVM type for the given SPIR-V type, or 0 if unknown.
+ unsigned getSPIRVTypeByteSize(SPIRVTypeInst Type) const;
+
+ // True if a pointer to this element type must stay typed rather than become
+ // OpTypeUntypedPointerKHR (an opaque builtin type like an image or sampler).
+ bool isSpecialOpaqueElementType(SPIRVTypeInst ElemType) const;
+
+ // Byte size of a pointer value's IR-deduced element type, or 0 if unknown.
+ unsigned getDeducedPointeeByteSize(const Value *PtrVal) {
+ if (Type *ElemTy = findDeducedElementType(PtrVal))
+ return DL.getTypeStoreSize(ElemTy).getFixedValue();
+ return 0;
+ }
+
// Returns either pointer to integer type, that may be a type of vector
// elements or an original type, or nullptr if the argument is niether
// an integer scalar, nor an integer vector
@@ -504,10 +534,12 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
// storage class. It is the responsibility of the caller to make sure the
// decorations on the base type are valid for the given storage class. For
// example, it has the correct offset and stride decorations.
- SPIRVTypeInst
- getOrCreateSPIRVPointerTypeInternal(SPIRVTypeInst BaseType,
- MachineIRBuilder &MIRBuilder,
- SPIRV::StorageClass::StorageClass SC);
+ // ForceTyped keeps an OpTypePointer even when untyped pointers are available,
+ // for cases where the pointee type must be preserved (e.g. a byval/byref/sret
+ // aggregate argument).
+ SPIRVTypeInst getOrCreateSPIRVPointerTypeInternal(
+ SPIRVTypeInst BaseType, MachineIRBuilder &MIRBuilder,
+ SPIRV::StorageClass::StorageClass SC, bool ForceTyped = false);
void addStructOffsetDecorations(Register Reg, StructType *Ty,
MachineIRBuilder &MIRBuilder);
@@ -606,13 +638,14 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
// Returns a pointer to a SPIR-V pointer type with the given base type and
// storage class. The base type will be translated to a SPIR-V type, and the
// appropriate layout decorations will be added to the base type.
- SPIRVTypeInst
- getOrCreateSPIRVPointerType(const Type *BaseType,
- MachineIRBuilder &MIRBuilder,
- SPIRV::StorageClass::StorageClass SC);
+ // See getOrCreateSPIRVPointerTypeInternal for ForceTyped.
+ SPIRVTypeInst getOrCreateSPIRVPointerType(
+ const Type *BaseType, MachineIRBuilder &MIRBuilder,
+ SPIRV::StorageClass::StorageClass SC, bool ForceTyped = false);
SPIRVTypeInst
getOrCreateSPIRVPointerType(const Type *BaseType, MachineInstr &I,
- SPIRV::StorageClass::StorageClass SC);
+ SPIRV::StorageClass::StorageClass SC,
+ bool ForceTyped = false);
// Returns a pointer to a SPIR-V pointer type with the given base type and
// storage class. It is the responsibility of the caller to make sure the
@@ -631,6 +664,11 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
SPIRV::StorageClass::StorageClass SC,
MachineInstr &I);
+ // Returns OpTypeUntypedPointerKHR for the given storage class.
+ SPIRVTypeInst
+ getOrCreateSPIRVUntypedPointerType(SPIRV::StorageClass::StorageClass SC,
+ MachineIRBuilder &MIRBuilder);
+
SPIRVTypeInst
getOrCreateVulkanBufferType(MachineIRBuilder &MIRBuilder, Type *ElemType,
SPIRV::StorageClass::StorageClass SC,
diff --git a/llvm/lib/Target/SPIRV/SPIRVIRMapping.h b/llvm/lib/Target/SPIRV/SPIRVIRMapping.h
index 47c7676d5631c..c845a87a80843 100644
--- a/llvm/lib/Target/SPIRV/SPIRVIRMapping.h
+++ b/llvm/lib/Target/SPIRV/SPIRVIRMapping.h
@@ -66,6 +66,7 @@ enum SpecialTypeKind {
STK_VkBuffer,
STK_Padding,
STK_ExplictLayoutType,
+ STK_UntypedPointer,
STK_Last = -1
};
@@ -154,6 +155,11 @@ inline IRHandle irhandle_padding() {
return std::make_tuple(nullptr, 0, SpecialTypeKind::STK_Padding);
}
+inline IRHandle irhandle_untyped_pointer(unsigned AddressSpace) {
+ return std::make_tuple(nullptr, AddressSpace,
+ SpecialTypeKind::STK_UntypedPointer);
+}
+
inline IRHandle irhandle_explict_layout_type(const Type *Ty) {
const Type *WrpTy = unifyPtrType(Ty);
return irhandle_ptr(WrpTy, Ty->getTypeID(), STK_ExplictLayoutType);
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index eb16c9a314a23..f4a0346a7427c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -614,13 +614,17 @@ bool SPIRVTargetLowering::enforcePtrTypeCompatibility(
MachineInstr &I, unsigned int PtrOpIdx, unsigned int OpIdx) const {
SPIRVGlobalRegistry &GR = *STI.getSPIRVGlobalRegistry();
SPIRVTypeInst PtrType = GR.getResultType(I.getOperand(PtrOpIdx).getReg());
+
+ if (PtrType && PtrType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR)
+ return true;
+
SPIRVTypeInst PointeeType = GR.getPointeeType(PtrType);
SPIRVTypeInst OpType = GR.getResultType(I.getOperand(OpIdx).getReg());
if (PointeeType == OpType)
return true;
- if (typesLogicallyMatch(PointeeType, OpType, GR)) {
+ if (PointeeType && OpType && typesLogicallyMatch(PointeeType, OpType, GR)) {
// Apply OpCopyLogical to OpIdx.
if (I.getOperand(OpIdx).isDef() &&
insertLogicalCopyOnResult(I, PointeeType)) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index 66e5d2f6a626e..b173bb7df166c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -289,6 +289,33 @@ def OpPtrNotEqual: Op<402, (outs ID:$res), (ins TYPE:$resType, ID:$a, ID:$b),
def OpPtrDiff: Op<403, (outs ID:$res), (ins TYPE:$resType, ID:$a, ID:$b),
"$res = OpPtrDiff $resType $a $b">;
+// - SPV_KHR_untyped_pointers
+
+def OpTypeUntypedPointerKHR: Op<4417, (outs TYPE:$res), (ins StorageClass:$storage),
+ "$res = OpTypeUntypedPointerKHR $storage">;
+def OpUntypedVariableKHR: Op<4418, (outs ID:$res),
+ (ins TYPE:$type, StorageClass:$sc, TYPE:$dataType, variable_ops),
+ "$res = OpUntypedVariableKHR $type $sc $dataType">;
+def OpUntypedAccessChainKHR: Op<4419, (outs ID:$res),
+ (ins TYPE:$type, TYPE:$baseType, ID:$base, variable_ops),
+ "$res = OpUntypedAccessChainKHR $type $baseType $base">;
+def OpUntypedInBoundsAccessChainKHR: Op<4420, (outs ID:$res),
+ (ins TYPE:$type, TYPE:$baseType, ID:$base, variable_ops),
+ "$res = OpUntypedInBoundsAccessChainKHR $type $baseType $base">;
+def OpUntypedPtrAccessChainKHR: Op<4423, (outs ID:$res),
+ (ins TYPE:$type, TYPE:$baseType, ID:$base, ID:$element, variable_ops),
+ "$res = OpUntypedPtrAccessChainKHR $type $baseType $base $element">;
+def OpUntypedInBoundsPtrAccessChainKHR: Op<4424, (outs ID:$res),
+ (ins TYPE:$type, TYPE:$baseType, ID:$base, ID:$element, variable_ops),
+ "$res = OpUntypedInBoundsPtrAccessChainKHR $type $baseType $base $element">;
+def OpUntypedPrefetchKHR: Op<4426, (outs),
+ (ins ID:$pointer, ID:$numBytes, variable_ops),
+ "OpUntypedPrefetchKHR $pointer $numBytes">;
+def OpUntypedGroupAsyncCopyKHR: Op<4434, (outs ID:$res),
+ (ins TYPE:$ty, ID:$scope, ID:$dst, ID:$src, ID:$elemNumBytes,
+ ID:$nelts, ID:$stride, ID:$event),
+ "$res = OpUntypedGroupAsyncCopyKHR $ty $scope $dst $src $elemNumBytes $nelts $stride $event">;
+
// - SPV_INTEL_variable_length_array
def OpVariableLengthArrayINTEL: Op<5818, (outs ID:$res), (ins TYPE:$type, ID:$length),
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index c575932cd8709..d7c06b8b12fc2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1267,7 +1267,8 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
(void)II;
assert(((*II).getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
(*II).getOpcode() == TargetOpcode::COPY ||
- (*II).getOpcode() == SPIRV::OpVariable) &&
+ (*II).getOpcode() == SPIRV::OpVariable ||
+ (*II).getOpcode() == SPIRV::OpUntypedVariableKHR) &&
getImm(I.getOperand(2), MRI));
// It may be the initialization of a global variable.
bool IsGVInit = false;
@@ -2051,7 +2052,11 @@ bool SPIRVInstructionSelector::selectAtomicStore(MachineInstr &I) const {
SPIRVTypeInst PtrType = GR.getSPIRVTypeForVReg(Ptr);
SPIRVTypeInst PointeeType = GR.getPointeeType(PtrType);
- if (!PointeeType.isTypeIntOrFloat())
+ // For an untyped pointer the data type is the stored value's type.
+ if (!PointeeType && PtrType &&
+ PtrType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR)
+ PointeeType = GR.getSPIRVTypeForVReg(StoreVal);
+ if (!PointeeType || !PointeeType.isTypeIntOrFloat())
return diagnoseUnsupported(I,
"Lowering to SPIR-V of atomic store is only "
"allowed for integer or floating point types");
@@ -2593,8 +2598,11 @@ bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg,
SPIRVTypeInst SrcPtrTy = GR.getSPIRVTypeForVReg(SrcPtr);
// don't generate a cast for a null that may be represented by OpTypeInt
- if (SrcPtrTy->getOpcode() != SPIRV::OpTypePointer ||
- ResType->getOpcode() != SPIRV::OpTypePointer)
+ auto IsPtrTy = [](SPIRVTypeInst T) {
+ return T && (T->getOpcode() == SPIRV::OpTypePointer ||
+ T->getOpcode() == SPIRV::OpTypeUntypedPointerKHR);
+ };
+ if (!IsPtrTy(SrcPtrTy) || !IsPtrTy(ResType))
return BuildCOPY(ResVReg, SrcPtr, I);
SPIRV::StorageClass::StorageClass SrcSC = GR.getPointerStorageClass(SrcPtrTy);
@@ -4017,7 +4025,11 @@ bool SPIRVInstructionSelector::selectICmp(Register ResVReg,
unsigned CmpOpc;
Register CmpOperand = I.getOperand(2).getReg();
- if (GR.isScalarOfType(CmpOperand, SPIRV::OpTypePointer)) {
+ SPIRVTypeInst CmpOperandType = GR.getSPIRVTypeForVReg(CmpOperand);
+ bool IsPtrCmp = GR.isScalarOfType(CmpOperand, SPIRV::OpTypePointer) ||
+ (CmpOperandType && CmpOperandType->getOpcode() ==
+ SPIRV::OpTypeUntypedPointerKHR);
+ if (IsPtrCmp) {
CmpOpc = getPtrCmpOpcode(Pred);
// OpPtrEqual/OpPtrNotEqual require both operands to share an identical
// pointer type. If they are not OpBitcast is inserted.
@@ -4451,7 +4463,9 @@ bool SPIRVInstructionSelector::selectConst(Register ResVReg,
unsigned Opcode = I.getOpcode();
unsigned TpOpcode = ResType->getOpcode();
Register Reg;
- if (TpOpcode == SPIRV::OpTypePointer || TpOpcode == SPIRV::OpTypeEvent) {
+ if (TpOpcode == SPIRV::OpTypePointer ||
+ TpOpcode == SPIRV::OpTypeUntypedPointerKHR ||
+ TpOpcode == SPIRV::OpTypeEvent) {
assert(Opcode == TargetOpcode::G_CONSTANT &&
I.getOperand(1).getCImm()->isZero());
MachineBasicBlock &DepMBB = I.getMF()->front();
@@ -4553,32 +4567,88 @@ bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
const bool IsGEPInBounds = I.getOperand(2).getImm();
+ // Pointers to opaque types stay typed even with the extension on, so emit the
+ // untyped variant only when the result is actually an untyped pointer.
+ const bool UseUntypedPointers =
+ ResType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
- // OpAccessChain could be used for OpenCL, but the SPIRV-LLVM Translator only
- // relies on PtrAccessChain, so we'll try not to deviate. For Vulkan however,
- // we have to use Op[InBounds]AccessChain.
- const unsigned Opcode = STI.isLogicalSPIRV()
- ? (IsGEPInBounds ? SPIRV::OpInBoundsAccessChain
- : SPIRV::OpAccessChain)
- : (IsGEPInBounds ? SPIRV::OpInBoundsPtrAccessChain
- : SPIRV::OpPtrAccessChain);
+ // Determine the opcode based on pointer type and bounds checking.
+ // When using untyped pointers, use OpUntyped*AccessChainKHR variants.
+ unsigned Opcode;
+ if (UseUntypedPointers) {
+ if (STI.isLogicalSPIRV()) {
+ Opcode = IsGEPInBounds ? SPIRV::OpUntypedInBoundsAccessChainKHR
+ : SPIRV::OpUntypedAccessChainKHR;
+ } else {
+ Opcode = IsGEPInBounds ? SPIRV::OpUntypedInBoundsPtrAccessChainKHR
+ : SPIRV::OpUntypedPtrAccessChainKHR;
+ }
+ } else {
+ // OpAccessChain could be used for OpenCL, but the SPIRV-LLVM Translator
+ // only relies on PtrAccessChain, so we'll try not to deviate. For Vulkan
+ // however, we have to use Op[InBounds]AccessChain.
+ // FIXME: fix llvm-spirv.
+ if (STI.isLogicalSPIRV()) {
+ Opcode =
+ IsGEPInBounds ? SPIRV::OpInBoundsAccessChain : SPIRV::OpAccessChain;
+ } else {
+ Opcode = IsGEPInBounds ? SPIRV::OpInBoundsPtrAccessChain
+ : SPIRV::OpPtrAccessChain;
+ }
+ }
auto Res = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode))
.addDef(ResVReg)
- .addUse(GR.getSPIRVTypeID(ResType))
- // Object to get a pointer to.
- .addUse(I.getOperand(3).getReg());
- assert(
- (Opcode == SPIRV::OpPtrAccessChain ||
- Opcode == SPIRV::OpInBoundsPtrAccessChain ||
- (getImm(I.getOperand(4), MRI) && foldImm(I.getOperand(4), MRI) == 0)) &&
- "Cannot translate GEP to OpAccessChain. First index must be 0.");
+ .addUse(GR.getSPIRVTypeID(ResType));
+
+ // For untyped access chains, we need to add the base type operand.
+ if (UseUntypedPointers) {
+ // Get the element type from the base pointer register.
+ // For untyped pointers, this was stored when processing
+ // spv_assign_ptr_type.
+ Register BaseReg = I.getOperand(3).getReg();
+ SPIRVTypeInst BaseType = GR.getUntypedPtrElementType(BaseReg);
+ if (!BaseType) {
+ // Otherwise try the pointee type for mixed typed-pointer usage.
+ SPIRVTypeInst BasePtrType = GR.getSPIRVTypeForVReg(BaseReg);
+ BaseType = BasePtrType ? GR.getPointeeType(BasePtrType) : nullptr;
+ }
+ if (!BaseType) {
+ // The base may be a not-yet-selected global. Read its value type from
+ // the defining G_GLOBAL_VALUE, following copies.
+ Register DefReg = BaseReg;
+ MachineInstr *Def = MRI->getVRegDef(DefReg);
+ while (Def && Def->getOpcode() == TargetOpcode::COPY &&
+ Def->getOperand(1).isReg())
+ Def = MRI->getVRegDef(Def->getOperand(1).getReg());
+ if (Def && Def->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
+ if (const auto *GVar =
+ dyn_cast<GlobalVariable>(Def->getOperand(1).getGlobal()))
+ BaseType = GR.getOrCreateSPIRVType(GVar->getValueType(), I,
+ SPIRV::AccessQualifier::ReadWrite,
+ /*EmitIR=*/false);
+ }
+ if (!BaseType) {
+ BaseType = GR.getOrCreateSPIRVIntegerType(8, I, TII);
+ }
+ Res.addUse(GR.getSPIRVTypeID(BaseType));
+ }
+
+ // Object to get a pointer to.
+ Res.addUse(I.getOperand(3).getReg());
+
+ const bool IsAccessChainOpcode =
+ (Opcode == SPIRV::OpAccessChain ||
+ Opcode == SPIRV::OpInBoundsAccessChain ||
+ Opcode == SPIRV::OpUntypedAccessChainKHR ||
+ Opcode == SPIRV::OpUntypedInBoundsAccessChainKHR);
+
+ assert((!IsAccessChainOpcode || (getImm(I.getOperand(4), MRI) &&
+ foldImm(I.getOperand(4), MRI) == 0)) &&
+ "Cannot translate GEP to OpAccessChain.");
// Adding indices.
- const unsigned StartingIndex =
- (Opcode == SPIRV::OpAccessChain || Opcode == SPIRV::OpInBoundsAccessChain)
- ? 5
- : 4;
+ const unsigned StartingIndex = IsAccessChainOpcode ? 5 : 4;
for (unsigned i = StartingIndex; i < I.getNumExplicitOperands(); ++i)
Res.addUse(I.getOperand(i).getReg());
Res.constrainAllUses(TII, TRI, RBI);
@@ -6543,11 +6613,33 @@ bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg,
// Change order of instructions if needed: all OpVariable instructions in a
// function must be the first instructions in the first block
auto It = getOpVariableMBBIt(*I.getMF());
- BuildMI(*It->getParent(), It, It->getDebugLoc(), TII.get(SPIRV::OpVariable))
- .addDef(ResVReg)
- .addUse(GR.getSPIRVTypeID(ResType))
- .addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function))
- .constrainAllUses(TII, TRI, RBI);
+
+ // Pointers to opaque types stay typed even with the extension on, so emit the
+ // untyped variant only when the result is actually an untyped pointer.
+ bool UseUntypedPointers =
+ ResType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
+ unsigned Opcode =
+ UseUntypedPointers ? SPIRV::OpUntypedVariableKHR : SPIRV::OpVariable;
+
+ auto MIB = BuildMI(*It->getParent(), It, It->getDebugLoc(), TII.get(Opcode))
+ .addDef(ResVReg)
+ .addUse(GR.getSPIRVTypeID(ResType));
+
+ // Add storage class (comes before DataType for OpUntypedVariableKHR).
+ MIB.addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function));
+
+ // OpUntypedVariableKHR requires a DataType operand after the storage class.
+ if (UseUntypedPointers) {
+ // Get the element type that was stored when processing spv_assign_ptr_type.
+ SPIRVTypeInst DataType = GR.getUntypedPtrElementType(ResVReg);
+ if (!DataType) {
+ // Use i8 as a last resort.
+ DataType = GR.getOrCreateSPIRVIntegerType(8, I, TII);
+ }
+ MIB.addUse(GR.getSPIRVTypeID(DataType));
+ }
+ MIB.constrainAllUses(TII, TRI, RBI);
+
if (!STI.isShader()) {
unsigned Alignment = I.getOperand(2).getImm();
buildOpDecorate(ResVReg, *It, TII, SPIRV::Decoration::Alignment,
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 217e7619a9a3c..3641b40e9338e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -334,6 +334,7 @@ bool SPIRVModuleAnalysis::isDeclSection(const MachineRegisterInfo &MRI,
// omit now, collect later
return false;
case SPIRV::OpVariable:
+ case SPIRV::OpUntypedVariableKHR:
return static_cast<SPIRV::StorageClass::StorageClass>(
MI.getOperand(2).getImm()) != SPIRV::StorageClass::Function;
case SPIRV::OpFunction:
@@ -456,7 +457,8 @@ void SPIRVModuleAnalysis::visitDecl(
} else if (TII->isTypeDeclInstr(MI) || TII->isConstantInstr(MI) ||
TII->isInlineAsmDefInstr(MI)) {
GReg = handleTypeDeclOrConstant(MI, SignatureToGReg);
- } else if (Opcode == SPIRV::OpVariable) {
+ } else if (Opcode == SPIRV::OpVariable ||
+ Opcode == SPIRV::OpUntypedVariableKHR) {
GReg = handleVariable(MF, MI, GlobalToGReg);
} else {
LLVM_DEBUG({
@@ -2431,6 +2433,25 @@ void addInstrRequirements(const MachineInstr &MI,
// TODO: Add UntypedPointersKHR when implemented.
break;
}
+ case SPIRV::OpTypeUntypedPointerKHR:
+ Reqs.getAndAddRequirements(SPIRV::OperandCategory::StorageClassOperand,
+ MI.getOperand(1).getImm(), ST);
+ [[fallthrough]];
+ case SPIRV::OpUntypedVariableKHR:
+ case SPIRV::OpUntypedAccessChainKHR:
+ case SPIRV::OpUntypedInBoundsAccessChainKHR:
+ case SPIRV::OpUntypedPtrAccessChainKHR:
+ case SPIRV::OpUntypedInBoundsPtrAccessChainKHR:
+ case SPIRV::OpUntypedPrefetchKHR:
+ case SPIRV::OpUntypedGroupAsyncCopyKHR: {
+ if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers))
+ report_fatal_error("Untyped pointer instructions require the following "
+ "SPIR-V extension: SPV_KHR_untyped_pointers",
+ false);
+ Reqs.addExtension(SPIRV::Extension::SPV_KHR_untyped_pointers);
+ Reqs.addCapability(SPIRV::Capability::UntypedPointersKHR);
+ break;
+ }
case SPIRV::OpPredicatedLoadINTEL:
case SPIRV::OpPredicatedStoreINTEL: {
if (!ST.canUseExtension(SPIRV::Extension::SPV_INTEL_predicated_io))
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 75cc7133f1766..34b4a3765f79e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -266,7 +266,20 @@ static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
// If the ptrcast would be redundant, replace all uses with the source
// register.
MachineRegisterInfo *MRI = MIB.getMRI();
- if (GR->getSPIRVTypeForVReg(Source) == AssignedPtrType) {
+ // For untyped pointers the SPIR-V pointer type does not encode the
+ // pointee, so two pointers with different element types share the same
+ // pointer type. The element type still matters because it selects the
+ // Base Type operand of OpUntyped*AccessChainKHR. Treat the cast as
+ // redundant only when the source already carries the same element type.
+ // Otherwise keep a distinct register so the element type is preserved.
+ bool Redundant =
+ AssignedPtrType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR
+ ? GR->getUntypedPtrElementType(Source) ==
+ GR->getOrCreateSPIRVType(ElemTy, MIB,
+ SPIRV::AccessQualifier::ReadWrite,
+ /*EmitIR=*/true)
+ : GR->getSPIRVTypeForVReg(Source) == AssignedPtrType;
+ if (Redundant) {
// Erase Def's assign type instruction if we are going to replace Def.
if (MachineInstr *AssignMI = findAssignTypeInstr(Def, MRI))
ToErase.push_back(AssignMI);
@@ -576,9 +589,17 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
Register Reg = MI.getOperand(1).getReg();
MIB.setInsertPt(*MI.getParent(), MI.getIterator());
Type *ElementTy = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0);
- SPIRVTypeInst AssignedPtrType = GR->getOrCreateSPIRVPointerType(
- ElementTy, MI,
- addressSpaceToStorageClass(MI.getOperand(3).getImm(), *ST));
+ auto SC = addressSpaceToStorageClass(MI.getOperand(3).getImm(), *ST);
+ SPIRVTypeInst AssignedPtrType =
+ GR->getOrCreateSPIRVPointerType(ElementTy, MI, SC);
+
+ // For untyped pointers, store the element type for later use.
+ if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers)) {
+ SPIRVTypeInst ElemSpvType = GR->getOrCreateSPIRVType(
+ ElementTy, MIB, SPIRV::AccessQualifier::ReadWrite, true);
+ GR->setUntypedPtrElementType(Reg, ElemSpvType);
+ }
+
MachineInstr *Def = MRI.getVRegDef(Reg);
assert(Def && "Expecting an instruction that defines the register");
// G_GLOBAL_VALUE already has type info.
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index c38f9e2d0243a..1305b30f71bc6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -404,6 +404,7 @@ defm SPV_AMD_weak_linkage : ExtensionOperand<137, [EnvOpenCL]>;
// Message Type, which is not yet implemented.
defm SPV_KHR_abort : ExtensionOperand<138, [EnvOpenCL]>;
defm SPV_KHR_poison_freeze : ExtensionOperand<139, [EnvVulkan, EnvOpenCL]>;
+defm SPV_KHR_untyped_pointers : ExtensionOperand<140, [EnvVulkan, EnvOpenCL]>;
//===----------------------------------------------------------------------===//
// Multiclass used to define Capabilities enum values and at the same time
@@ -642,6 +643,7 @@ defm ArbitraryPrecisionFixedPointALTERA : CapabilityOperand<5922, 0, 0, [SPV_ALT
defm ArbitraryPrecisionFloatingPointALTERA : CapabilityOperand<5845, 0, 0,[SPV_ALTERA_arbitrary_precision_floating_point], []>;
defm UnstructuredLoopControlsINTEL : CapabilityOperand<5886, 0, 0, [SPV_INTEL_unstructured_loop_controls], []>;
defm PoisonFreezeKHR : CapabilityOperand<5156, 0, 0, [SPV_KHR_poison_freeze], []>;
+defm UntypedPointersKHR : CapabilityOperand<4473, 0, 0, [SPV_KHR_untyped_pointers], []>;
//===----------------------------------------------------------------------===//
// Multiclass used to define SourceLanguage enum values and at the same time
diff --git a/llvm/test/CodeGen/SPIRV/SpecConstants/amdgcnspirv-feature-predicate-specconstant.ll b/llvm/test/CodeGen/SPIRV/SpecConstants/amdgcnspirv-feature-predicate-specconstant.ll
index 04b10e946ea32..08f40ea35259b 100644
--- a/llvm/test/CodeGen/SPIRV/SpecConstants/amdgcnspirv-feature-predicate-specconstant.ll
+++ b/llvm/test/CodeGen/SPIRV/SpecConstants/amdgcnspirv-feature-predicate-specconstant.ll
@@ -26,7 +26,7 @@
; CHECK: %[[#FEATURE_PREDICATE_IDS_MAP_STRLEN:]] = OpConstant %[[#]] 137
; CHECK: %[[#FEATURE_PREDICATE_IDS_MAP_STRTY:]] = OpTypeArray %[[#UCHAR]] %[[#FEATURE_PREDICATE_IDS_MAP_STRLEN]]
; CHECK: %[[#FEATURE_PREDICATE_IDS_MAP_STRVAL:]] = OpConstantComposite %[[#FEATURE_PREDICATE_IDS_MAP_STRTY]]
-; CHECK: %[[#FEATURE_PREDICATE_IDS]] = OpVariable %[[#]] CrossWorkgroup %[[#FEATURE_PREDICATE_IDS_MAP_STRVAL]]
+; CHECK: %[[#FEATURE_PREDICATE_IDS]] = OpUntypedVariableKHR %[[#]] CrossWorkgroup %[[#FEATURE_PREDICATE_IDS_MAP_STRTY]] %[[#FEATURE_PREDICATE_IDS_MAP_STRVAL]]
; CHECK: %[[#IS_GFX950]] = OpSpecConstantFalse %[[#BOOL]]
; CHECK: %[[#IS_GFX1201]] = OpSpecConstantFalse %[[#BOOL]]
; CHECK: %[[#HAS_GFX12_INSTS]] = OpSpecConstantFalse %[[#BOOL]]
@@ -63,32 +63,32 @@ declare void @llvm.amdgcn.s.ttracedata.imm(i16 immarg) addrspace(4) #6
define void @kernel() addrspace(4) {
; CHECK-DAG: %[[#KERNEL]] = OpFunction %39 None %40 ; -- Begin function kernel
; CHECK-NEXT: %2 = OpLabel
-; CHECK-NEXT: %112 = OpLoad %44 %85 Aligned 4
+; CHECK-NEXT: %111 = OpLoad %44 %84 Aligned 4
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX950]] %4 %3
; CHECK-NEXT: %3 = OpLabel
-; CHECK-NEXT: %113 = OpFunctionCall %39 %[[#SET_FPENV_I64]] %57
+; CHECK-NEXT: %112 = OpFunctionCall %39 %[[#SET_FPENV_I64]] %56
; CHECK-NEXT: OpBranch %5
; CHECK-NEXT: %4 = OpLabel
-; CHECK-NEXT: %114 = OpFunctionCall %43 %[[#ASHR_PK_I8_I32]] %56 %56 %56
+; CHECK-NEXT: %113 = OpFunctionCall %43 %[[#ASHR_PK_I8_I32]] %55 %55 %55
; CHECK-NEXT: OpBranch %5
; CHECK-NEXT: %5 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX1201]] %7 %6
; CHECK-NEXT: %6 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#HAS_GFX12_INSTS]] %7 %8
; CHECK-NEXT: %7 = OpLabel
-; CHECK-NEXT: %115 = OpFunctionCall %39 %[[#S_SLEEP_VAR]] %112
+; CHECK-NEXT: %114 = OpFunctionCall %39 %[[#S_SLEEP_VAR]] %111
; CHECK-NEXT: OpBranch %8
; CHECK-NEXT: %8 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX906]] %10 %9
; CHECK-NEXT: %9 = OpLabel
-; CHECK-NEXT: %116 = OpFunctionCall %39 %[[#S_WAIT_EVENT_EXPORT_READY]]
+; CHECK-NEXT: %115 = OpFunctionCall %39 %[[#S_WAIT_EVENT_EXPORT_READY]]
; CHECK-NEXT: OpBranch %14
; CHECK-NEXT: %10 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX1010]] %12 %11
; CHECK-NEXT: %11 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX1101]] %12 %13
; CHECK-NEXT: %12 = OpLabel
-; CHECK-NEXT: %117 = OpFunctionCall %39 %[[#S_TTRACEDATA_IMM]] %55
+; CHECK-NEXT: %116 = OpFunctionCall %39 %[[#S_TTRACEDATA_IMM]] %54
; CHECK-NEXT: OpBranch %13
; CHECK-NEXT: %13 = OpLabel
; CHECK-NEXT: OpBranch %14
@@ -97,24 +97,24 @@ define void @kernel() addrspace(4) {
; CHECK-NEXT: %15 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX1101_1]] %16 %17
; CHECK-NEXT: %16 = OpLabel
-; CHECK-NEXT: %118 = OpLoad %44 %97 Aligned 4
-; CHECK-NEXT: %119 = OpIAdd %44 %118 %112
-; CHECK-NEXT: OpStore %97 %119 Aligned 4
+; CHECK-NEXT: %117 = OpLoad %44 %96 Aligned 4
+; CHECK-NEXT: %118 = OpIAdd %44 %117 %111
+; CHECK-NEXT: OpStore %96 %118 Aligned 4
; CHECK-NEXT: OpBranch %17
; CHECK-NEXT: %17 = OpLabel
; CHECK-NEXT: OpBranch %18
; CHECK-NEXT: %18 = OpLabel
-; CHECK-NEXT: %120 = OpLoad %44 %97 Aligned 4
-; CHECK-NEXT: %121 = OpISub %44 %120 %112
-; CHECK-NEXT: OpStore %97 %121 Aligned 4
+; CHECK-NEXT: %119 = OpLoad %44 %96 Aligned 4
+; CHECK-NEXT: %120 = OpISub %44 %119 %111
+; CHECK-NEXT: OpStore %96 %120 Aligned 4
; CHECK-NEXT: OpBranch %19
; CHECK-NEXT: %19 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#IS_GFX1010_1]] %20 %21
; CHECK-NEXT: %20 = OpLabel
; CHECK-NEXT: OpBranch %21
; CHECK-NEXT: %21 = OpLabel
-; CHECK-NEXT: %122 = OpPhi %48 %54 %19 %54 %20
-; CHECK-NEXT: OpBranchConditional %122 %18 %22
+; CHECK-NEXT: %121 = OpPhi %48 %53 %19 %53 %20
+; CHECK-NEXT: OpBranchConditional %121 %18 %22
; CHECK-NEXT: %22 = OpLabel
; CHECK-NEXT: OpBranch %23
; CHECK-NEXT: %23 = OpLabel
@@ -126,27 +126,27 @@ define void @kernel() addrspace(4) {
; CHECK-NEXT: %26 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#HAS_GFX10_INSTS]] %27 %28
; CHECK-NEXT: %27 = OpLabel
-; CHECK-NEXT: %123 = OpFunctionCall %39 %[[#S_TTRACEDATA_IMM]] %55
+; CHECK-NEXT: %122 = OpFunctionCall %39 %[[#S_TTRACEDATA_IMM]] %54
; CHECK-NEXT: OpBranch %28
; CHECK-NEXT: %28 = OpLabel
; CHECK-NEXT: OpBranch %30
; CHECK-NEXT: %29 = OpLabel
-; CHECK-NEXT: %124 = OpFunctionCall %39 %[[#S_WAIT_EVENT_EXPORT_READY]]
+; CHECK-NEXT: %123 = OpFunctionCall %39 %[[#S_WAIT_EVENT_EXPORT_READY]]
; CHECK-NEXT: OpBranch %30
; CHECK-NEXT: %30 = OpLabel
; CHECK-NEXT: OpBranch %31
; CHECK-NEXT: %31 = OpLabel
-; CHECK-NEXT: %125 = OpLoad %44 %97 Aligned 4
-; CHECK-NEXT: %126 = OpISub %44 %125 %112
-; CHECK-NEXT: OpStore %97 %126 Aligned 4
+; CHECK-NEXT: %124 = OpLoad %44 %96 Aligned 4
+; CHECK-NEXT: %125 = OpISub %44 %124 %111
+; CHECK-NEXT: OpStore %96 %125 Aligned 4
; CHECK-NEXT: OpBranch %32
; CHECK-NEXT: %32 = OpLabel
; CHECK-NEXT: OpBranchConditional %[[#HAS_GFX1250_INSTS]] %33 %34
; CHECK-NEXT: %33 = OpLabel
; CHECK-NEXT: OpBranch %34
; CHECK-NEXT: %34 = OpLabel
-; CHECK-NEXT: %127 = OpPhi %48 %54 %32 %54 %33
-; CHECK-NEXT: OpBranchConditional %127 %31 %35
+; CHECK-NEXT: %126 = OpPhi %48 %53 %32 %53 %33
+; CHECK-NEXT: OpBranchConditional %126 %31 %35
; CHECK-NEXT: %35 = OpLabel
; CHECK-NEXT: OpBranch %36
; CHECK-NEXT: %36 = OpLabel
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-select-untyped-ptr.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-select-untyped-ptr.ll
new file mode 100644
index 0000000000000..b52c592acbb17
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-select-untyped-ptr.ll
@@ -0,0 +1,27 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers,+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers,+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; With untyped pointers enabled a function pointer must stay typed, since an
+; untyped pointer cannot express the function type. A select between two function
+; pointers then has matching typed operands and result.
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#FNTY:]] = OpTypeFunction %[[#I32]] %[[#I32]] %[[#I32]]
+; CHECK-DAG: %[[#FPTY:]] = OpTypePointer CodeSectionINTEL %[[#FNTY]]
+; CHECK: %[[#BAR:]] = OpConstantFunctionPointerINTEL %[[#FPTY]]
+; CHECK: %[[#BAZ:]] = OpConstantFunctionPointerINTEL %[[#FPTY]]
+; CHECK: OpSelect %[[#]] %[[#]] %[[#BAZ]] %[[#BAR]]
+; CHECK: OpFunctionPointerCallINTEL
+define spir_func i32 @caller(i1 %c, i32 %a, i32 %b) {
+ %fp = select i1 %c, ptr @bar, ptr @baz
+ %r = call spir_func i32 %fp(i32 %a, i32 %b)
+ ret i32 %r
+}
+define spir_func i32 @bar(i32 %a, i32 %b) {
+ %s = add i32 %a, %b
+ ret i32 %s
+}
+define spir_func i32 @baz(i32 %a, i32 %b) {
+ %s = mul i32 %a, %b
+ ret i32 %s
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-access-chain.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-access-chain.ll
new file mode 100644
index 0000000000000..633d30a4d35f3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-access-chain.ll
@@ -0,0 +1,64 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Test OpUntypedPtrAccessChainKHR for GEP instructions in physical SPIR-V.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_gep_const_index(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr i32, ptr addrspace(1) %base, i64 5
+ %val = load i32, ptr addrspace(1) %ptr, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_gep_var_index(ptr addrspace(1) %base, i64 %idx, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr i32, ptr addrspace(1) %base, i64 %idx
+ %val = load i32, ptr addrspace(1) %ptr, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpUntypedInBoundsPtrAccessChainKHR
+define spir_kernel void @test_gep_inbounds(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr inbounds i32, ptr addrspace(1) %base, i64 3
+ %val = load i32, ptr addrspace(1) %ptr, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_gep_i8(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr i8, ptr addrspace(1) %base, i64 10
+ %val = load i8, ptr addrspace(1) %ptr, align 1
+ store i8 %val, ptr addrspace(1) %out, align 1
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_gep_i64(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr i64, ptr addrspace(1) %base, i64 2
+ %val = load i64, ptr addrspace(1) %ptr, align 8
+ store i64 %val, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_gep_float(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr float, ptr addrspace(1) %base, i64 4
+ %val = load float, ptr addrspace(1) %ptr, align 4
+ store float %val, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-access-chain-base-type.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-access-chain-base-type.ll
new file mode 100644
index 0000000000000..3ff7e167628a7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-access-chain-base-type.ll
@@ -0,0 +1,39 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; When an untyped pointer is used first with one element type and then indexed
+; as a struct through a multi-index GEP, the struct access chain must use the
+; struct as its Base Type, not the pointer's first-deduced i8 type. An i8 Base
+; Type with member indices is invalid SPIR-V and reverse-translates to an
+; invalid multi-index getelementptr i8.
+
+%struct.inner = type { i32, i32 }
+%struct.middle = type { i64, i32, %struct.inner }
+%struct.outer = type { i32, i32, i32, %struct.middle }
+
+; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#INNER:]] = OpTypeStruct %[[#I32]] %[[#I32]]
+; CHECK-DAG: %[[#MIDDLE:]] = OpTypeStruct %[[#I64]] %[[#I32]] %[[#INNER]]
+; CHECK-DAG: %[[#OUTER:]] = OpTypeStruct %[[#I32]] %[[#I32]] %[[#I32]] %[[#MIDDLE]]
+
+; The i8 access keeps an i8 Base Type with a single index.
+; CHECK: OpUntypedInBoundsPtrAccessChainKHR %[[#]] %[[#I8]] %[[#PTR:]] %[[#]]
+; The struct accesses on the same pointer must use the struct Base Types, not i8.
+; CHECK: OpUntypedInBoundsPtrAccessChainKHR %[[#]] %[[#OUTER]] %[[#PTR]] %[[#]] %[[#]]
+; CHECK: OpUntypedInBoundsPtrAccessChainKHR %[[#]] %[[#MIDDLE]] %[[#]] %[[#]] %[[#]]
+; CHECK: OpUntypedInBoundsPtrAccessChainKHR %[[#]] %[[#INNER]] %[[#]] %[[#]] %[[#]]
+define spir_kernel void @i8_use_then_struct_chain(ptr addrspace(4) %p, ptr addrspace(1) %out) #0 {
+entry:
+ %b = getelementptr inbounds i8, ptr addrspace(4) %p, i64 0
+ store i8 7, ptr addrspace(4) %b, align 1
+ %gep1 = getelementptr inbounds %struct.outer, ptr addrspace(4) %p, i32 0, i32 3
+ %gep2 = getelementptr inbounds %struct.middle, ptr addrspace(4) %gep1, i32 0, i32 2
+ %gep3 = getelementptr inbounds %struct.inner, ptr addrspace(4) %gep2, i32 0, i32 1
+ %val = load i32, ptr addrspace(4) %gep3, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-addrspacecast.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-addrspacecast.ll
new file mode 100644
index 0000000000000..619c062a3e157
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-addrspacecast.ll
@@ -0,0 +1,21 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; addrspacecast between untyped pointers emits OpPtrCastToGeneric and needs the
+; GenericPointer capability.
+
+; CHECK-DAG: OpCapability UntypedPointersKHR
+; CHECK-DAG: OpCapability GenericPointer
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#CROSS:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK-DAG: %[[#GEN:]] = OpTypeUntypedPointerKHR Generic
+
+; CHECK: %[[#G:]] = OpFunctionParameter %[[#CROSS]]
+; CHECK: %[[#CAST:]] = OpPtrCastToGeneric %[[#GEN]] %[[#G]]
+; CHECK: OpStore %[[#CAST]]
+define spir_kernel void @test(ptr addrspace(1) %g) {
+ %gen = addrspacecast ptr addrspace(1) %g to ptr addrspace(4)
+ store i32 3, ptr addrspace(4) %gen, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-array.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-array.ll
new file mode 100644
index 0000000000000..9d0de93ac3dad
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-array.ll
@@ -0,0 +1,64 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Test untyped pointers with array accesses.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#CROSS_PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR %[[#CROSS_PTR]]
+define spir_kernel void @test_array_const_idx(ptr addrspace(1) %arr, ptr addrspace(1) %out) {
+entry:
+ %elem = getelementptr i32, ptr addrspace(1) %arr, i64 10
+ %val = load i32, ptr addrspace(1) %elem, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR %[[#CROSS_PTR]]
+define spir_kernel void @test_array_var_idx(ptr addrspace(1) %arr, i64 %idx, ptr addrspace(1) %out) {
+entry:
+ %elem = getelementptr i32, ptr addrspace(1) %arr, i64 %idx
+ %val = load i32, ptr addrspace(1) %elem, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_2d_array(ptr addrspace(1) %arr, ptr addrspace(1) %out) {
+entry:
+ ; Access arr[2][3] where arr is [10 x [10 x i32]]
+ %row = getelementptr [10 x i32], ptr addrspace(1) %arr, i64 2
+ %elem = getelementptr [10 x i32], ptr addrspace(1) %row, i64 0, i64 3
+ %val = load i32, ptr addrspace(1) %elem, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_array_loop(ptr addrspace(1) %arr, i64 %n, ptr addrspace(1) %out) {
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %sum = phi i32 [ 0, %entry ], [ %sum.next, %loop ]
+ %elem_ptr = getelementptr i32, ptr addrspace(1) %arr, i64 %i
+ %elem = load i32, ptr addrspace(1) %elem_ptr, align 4
+ %sum.next = add i32 %sum, %elem
+ %i.next = add i64 %i, 1
+ %cond = icmp slt i64 %i.next, %n
+ br i1 %cond, label %loop, label %exit
+
+exit:
+ store i32 %sum.next, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-async-copy.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-async-copy.ll
new file mode 100644
index 0000000000000..dc2c4e31fcd6f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-async-copy.ll
@@ -0,0 +1,17 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; async_work_group_copy with untyped pointers uses OpUntypedGroupAsyncCopyKHR
+; with an explicit Element Num Bytes operand.
+
+; CHECK-DAG: OpCapability UntypedPointersKHR
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#EVENT:]] = OpTypeEvent
+; CHECK-DAG: %[[#ELEMBYTES:]] = OpConstant %[[#I32]] 4
+
+; CHECK: OpUntypedGroupAsyncCopyKHR %[[#EVENT]] %[[#]] %[[#]] %[[#]] %[[#ELEMBYTES]] %[[#]] %[[#]] %[[#]]
+define spir_kernel void @t(ptr addrspace(1) %dst, ptr addrspace(3) %src, i32 %n) {
+ %e = call spir_func ptr @_Z21async_work_group_copyPU3AS1iPKU3AS3ij9ocl_event(ptr addrspace(1) %dst, ptr addrspace(3) %src, i32 %n, ptr null)
+ ret void
+}
+declare spir_func ptr @_Z21async_work_group_copyPU3AS1iPKU3AS3ij9ocl_event(ptr addrspace(1), ptr addrspace(3), i32, ptr)
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-atomics.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-atomics.ll
new file mode 100644
index 0000000000000..f19ae243dd67c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-atomics.ll
@@ -0,0 +1,21 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Atomic load/store/rmw through an untyped pointer.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+
+; CHECK: %[[#P:]] = OpFunctionParameter %[[#PTR]]
+; CHECK: OpAtomicStore %[[#P]] %[[#]] %[[#]] %[[#]]
+; CHECK: OpAtomicLoad %[[#I32]] %[[#P]] %[[#]] %[[#]]
+; CHECK: OpAtomicIAdd %[[#I32]] %[[#P]] %[[#]] %[[#]] %[[#]]
+define spir_kernel void @test(ptr addrspace(1) %p) {
+ store atomic i32 7, ptr addrspace(1) %p seq_cst, align 4
+ %v = load atomic i32, ptr addrspace(1) %p seq_cst, align 4
+ %old = atomicrmw add ptr addrspace(1) %p, i32 1 seq_cst
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-basics.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-basics.ll
new file mode 100644
index 0000000000000..bd261b53e2071
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-basics.ll
@@ -0,0 +1,40 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#VOID:]] = OpTypeVoid
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#CROSS_PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK-DAG: %[[#FUNC_PTR:]] = OpTypeUntypedPointerKHR Function
+
+; CHECK: OpFunctionParameter %[[#CROSS_PTR]]
+; CHECK: OpFunctionParameter %[[#CROSS_PTR]]
+
+define spir_kernel void @test_kernel(ptr addrspace(1) %input, ptr addrspace(1) %output) {
+entry:
+ %val = load i32, ptr addrspace(1) %input, align 4
+ store i32 %val, ptr addrspace(1) %output, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#ALLOCA:]] = OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#I32]]
+define spir_kernel void @test_alloca() {
+entry:
+ %local = alloca i32, align 4
+ store i32 42, ptr %local, align 4
+ %val = load i32, ptr %local, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR %[[#CROSS_PTR]]
+define spir_kernel void @test_gep(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr i32, ptr addrspace(1) %base, i64 5
+ %val = load i32, ptr addrspace(1) %ptr, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-byval-arg.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-byval-arg.ll
new file mode 100644
index 0000000000000..671296d60255c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-byval-arg.ll
@@ -0,0 +1,41 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; byval/byref/sret arguments must keep a typed OpTypePointer even with the
+; untyped pointers extension on, so the aggregate layout survives in the pointee
+; type. An untyped pointer here loses the type and breaks the argument ABI.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#F32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#STRUCT:]] = OpTypeStruct %[[#F32]] %[[#F32]] %[[#F32]]
+; The by-value struct argument keeps a typed pointer to the struct...
+; CHECK-DAG: %[[#STRUCT_PTR:]] = OpTypePointer Function %[[#STRUCT]]
+; ...while ordinary pointer arguments become untyped.
+; CHECK-DAG: %[[#CROSS_PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+
+%struct.Params = type { float, float, float }
+
+; CHECK: OpFunction
+; CHECK: OpFunctionParameter %[[#STRUCT_PTR]]
+; CHECK: OpFunctionParameter %[[#CROSS_PTR]]
+define spir_kernel void @test_byval(ptr byval(%struct.Params) %params,
+ ptr addrspace(1) %out) {
+entry:
+ %f1.addr = getelementptr inbounds %struct.Params, ptr %params, i32 0, i32 1
+ %f1 = load float, ptr %f1.addr, align 4
+ store float %f1, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpFunctionParameter %[[#STRUCT_PTR]]
+define spir_kernel void @test_byref(ptr byref(%struct.Params) %params,
+ ptr addrspace(1) %out) {
+entry:
+ %f2.addr = getelementptr inbounds %struct.Params, ptr %params, i32 0, i32 2
+ %f2 = load float, ptr %f2.addr, align 4
+ store float %f2, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-coopmatrix.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-coopmatrix.ll
new file mode 100644
index 0000000000000..2b77049a95aaa
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-coopmatrix.ll
@@ -0,0 +1,15 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_cooperative_matrix,+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_cooperative_matrix,+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Cooperative matrix load/store accept an untyped pointer operand unchanged.
+
+; CHECK-DAG: %[[#CROSS:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK: OpCooperativeMatrixLoadKHR
+; CHECK: OpCooperativeMatrixStoreKHR
+define spir_kernel void @t(ptr addrspace(1) %src, ptr addrspace(1) %dst, i64 %stride) {
+ %m = tail call spir_func target("spirv.CooperativeMatrixKHR", i32, 3, 12, 12, 2) @_Z32__spirv_CooperativeMatrixLoadKHR(ptr addrspace(1) %src, i32 0, i64 %stride, i32 1)
+ tail call spir_func void @_Z33__spirv_CooperativeMatrixStoreKHR(ptr addrspace(1) %dst, target("spirv.CooperativeMatrixKHR", i32, 3, 12, 12, 2) %m, i32 0, i64 %stride, i32 1)
+ ret void
+}
+declare spir_func target("spirv.CooperativeMatrixKHR", i32, 3, 12, 12, 2) @_Z32__spirv_CooperativeMatrixLoadKHR(ptr addrspace(1), i32, i64, i32)
+declare spir_func void @_Z33__spirv_CooperativeMatrixStoreKHR(ptr addrspace(1), target("spirv.CooperativeMatrixKHR", i32, 3, 12, 12, 2), i32, i64, i32)
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-element-type.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-element-type.ll
new file mode 100644
index 0000000000000..e96969fe9ba3c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-element-type.ll
@@ -0,0 +1,31 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Untyped access chains carry the real element type as Base Type, not a fallback.
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#ARR:]] = OpTypeArray %[[#I32]]
+; CHECK-DAG: %[[#CROSS:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK-DAG: %[[#FUNC:]] = OpTypeUntypedPointerKHR Function
+
+; A chained GEP carries the i32 Base Type on both links, including the one based
+; on the previous chain's result.
+; CHECK: %[[#P1:]] = OpUntypedPtrAccessChainKHR %[[#CROSS]] %[[#I32]] %[[#]] %[[#]]
+; CHECK: OpUntypedPtrAccessChainKHR %[[#CROSS]] %[[#I32]] %[[#P1]] %[[#]]
+define spir_kernel void @chain(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+ %p1 = getelementptr i32, ptr addrspace(1) %base, i64 5
+ %p2 = getelementptr i32, ptr addrspace(1) %p1, i64 3
+ %v = load i32, ptr addrspace(1) %p2
+ store i32 %v, ptr addrspace(1) %out
+ ret void
+}
+
+; An aggregate alloca uses the array type for both Data Type and Base Type.
+; CHECK: %[[#VAR:]] = OpUntypedVariableKHR %[[#FUNC]] Function %[[#ARR]]
+; CHECK: OpUntypedPtrAccessChainKHR %[[#FUNC]] %[[#ARR]] %[[#VAR]]
+define spir_kernel void @agg(ptr addrspace(1) %out) {
+ %arr = alloca [4 x i32]
+ %e = getelementptr [4 x i32], ptr %arr, i64 0, i64 2
+ store i32 7, ptr %e
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global-gep-atomic.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global-gep-atomic.ll
new file mode 100644
index 0000000000000..b2d13d8b801aa
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global-gep-atomic.ll
@@ -0,0 +1,28 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; A GEP based on a module-scope global carries the global's value type as the
+; access-chain Base Type, and cmpxchg through an untyped pointer selects
+; OpAtomicCompareExchange.
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#ARR:]] = OpTypeArray %[[#I32]]
+; CHECK-DAG: %[[#CROSS:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+
+; The global's Data Type and the access chain's Base Type are both the array.
+; CHECK: %[[#G:]] = OpUntypedVariableKHR %[[#CROSS]] CrossWorkgroup %[[#ARR]]
+; CHECK: OpUntypedInBoundsPtrAccessChainKHR %[[#CROSS]] %[[#ARR]] %[[#G]]
+ at arr = addrspace(1) global [4 x i32] zeroinitializer, align 4
+define spir_kernel void @gep_global(ptr addrspace(1) %out) {
+ %p = getelementptr inbounds [4 x i32], ptr addrspace(1) @arr, i64 0, i64 2
+ %v = load i32, ptr addrspace(1) %p, align 4
+ store i32 %v, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpAtomicCompareExchange %[[#I32]]
+define spir_func i32 @cmpxchg_fn(ptr addrspace(1) %p, i32 %cmp, i32 %new) {
+ %pair = cmpxchg ptr addrspace(1) %p, i32 %cmp, i32 %new seq_cst seq_cst
+ %old = extractvalue { i32, i1 } %pair, 0
+ ret i32 %old
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global.ll
new file mode 100644
index 0000000000000..a70d4b03da1e0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-global.ll
@@ -0,0 +1,19 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; A module-scope global emits OpUntypedVariableKHR at module scope with the
+; global's value type.
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#CROSS:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+
+; The variable is emitted before the first OpFunction.
+; CHECK: OpUntypedVariableKHR %[[#CROSS]] CrossWorkgroup %[[#I32]]
+; CHECK: OpFunction
+
+ at g = addrspace(1) global i32 42, align 4
+define spir_kernel void @use(ptr addrspace(1) %out) {
+ %v = load i32, ptr addrspace(1) @g, align 4
+ store i32 %v, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-memcpy.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-memcpy.ll
new file mode 100644
index 0000000000000..41712c93983d7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-memcpy.ll
@@ -0,0 +1,12 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; memcpy lowers to OpCopyMemorySized with untyped pointer operands.
+
+; CHECK-DAG: %[[#CROSS:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK: OpCopyMemorySized %[[#]] %[[#]] %[[#]]
+define spir_kernel void @t(ptr addrspace(1) %dst, ptr addrspace(1) %src) {
+ call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) %dst, ptr addrspace(1) %src, i64 128, i1 false)
+ ret void
+}
+declare void @llvm.memcpy.p1.p1.i64(ptr addrspace(1), ptr addrspace(1), i64, i1)
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-no-ext.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-no-ext.ll
new file mode 100644
index 0000000000000..9bbf5999838c1
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-no-ext.ll
@@ -0,0 +1,39 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that without the extension we use typed pointers.
+; Ensure the extension does not change the typed path.
+
+; CHECK-NOT: OpCapability UntypedPointersKHR
+; CHECK-NOT: OpExtension "SPV_KHR_untyped_pointers"
+; CHECK-NOT: OpTypeUntypedPointerKHR
+; CHECK-NOT: OpUntypedVariableKHR
+; CHECK-NOT: OpUntypedAccessChainKHR
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: OpTypePointer CrossWorkgroup %[[#I32]]
+; CHECK-DAG: OpTypePointer Function %[[#I32]]
+
+define spir_kernel void @test_kernel(ptr addrspace(1) %input, ptr addrspace(1) %output) {
+entry:
+ %val = load i32, ptr addrspace(1) %input, align 4
+ store i32 %val, ptr addrspace(1) %output, align 4
+ ret void
+}
+
+; CHECK: OpVariable
+define spir_kernel void @test_alloca() {
+entry:
+ %local = alloca i32, align 4
+ store i32 42, ptr %local, align 4
+ ret void
+}
+
+; CHECK: OpPtrAccessChain
+define spir_kernel void @test_gep(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr i32, ptr addrspace(1) %base, i64 5
+ %val = load i32, ptr addrspace(1) %ptr, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-prefetch.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-prefetch.ll
new file mode 100644
index 0000000000000..b41713b79c02f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-prefetch.ll
@@ -0,0 +1,28 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; prefetch on an untyped pointer uses OpUntypedPrefetchKHR with Num Bytes equal
+; to num_elements scaled by the element byte size.
+
+; CHECK-DAG: OpCapability UntypedPointersKHR
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#FOUR:]] = OpConstant %[[#I32]] 4
+
+; i32 element, so Num Bytes is num_elements * 4.
+; CHECK: %[[#NB:]] = OpIMul %[[#I32]] %[[#]] %[[#FOUR]]
+; CHECK: OpUntypedPrefetchKHR %[[#]] %[[#NB]]
+define spir_kernel void @pf_i32(ptr addrspace(1) %p, i32 %n) {
+ call spir_func void @_Z8prefetchPU3AS1Kij(ptr addrspace(1) %p, i32 %n)
+ ret void
+}
+
+; i8 element, so Num Bytes is the count with no multiply.
+; CHECK: OpUntypedPrefetchKHR
+; CHECK-NOT: OpIMul
+define spir_kernel void @pf_i8(ptr addrspace(1) %p) {
+ call spir_func void @_Z20__spirv_ocl_prefetchPU3AS1Kcm(ptr addrspace(1) %p, i64 4)
+ ret void
+}
+
+declare spir_func void @_Z8prefetchPU3AS1Kij(ptr addrspace(1), i32)
+declare spir_func void @_Z20__spirv_ocl_prefetchPU3AS1Kcm(ptr addrspace(1), i64)
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-ptr-ops.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-ptr-ops.ll
new file mode 100644
index 0000000000000..59da3edc8f29d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-ptr-ops.ll
@@ -0,0 +1,32 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val --target-env spv1.4 %}
+
+; icmp on untyped pointers uses OpPtrEqual/OpPtrNotEqual, and ptrtoint/inttoptr
+; use the pointer conversion ops.
+
+; CHECK-DAG: %[[#PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+
+; CHECK: OpPtrEqual %[[#]] %[[#]] %[[#]]
+define spir_func i1 @ptr_eq(ptr addrspace(1) %a, ptr addrspace(1) %b) {
+ %c = icmp eq ptr addrspace(1) %a, %b
+ ret i1 %c
+}
+
+; CHECK: OpPtrNotEqual %[[#]] %[[#]] %[[#]]
+define spir_func i1 @ptr_ne(ptr addrspace(1) %a, ptr addrspace(1) %b) {
+ %c = icmp ne ptr addrspace(1) %a, %b
+ ret i1 %c
+}
+
+; CHECK: OpConvertPtrToU %[[#I64]] %[[#]]
+define spir_func i64 @p2i(ptr addrspace(1) %a) {
+ %i = ptrtoint ptr addrspace(1) %a to i64
+ ret i64 %i
+}
+
+; CHECK: OpConvertUToPtr %[[#PTR]] %[[#]]
+define spir_func ptr addrspace(1) @i2p(i64 %i) {
+ %p = inttoptr i64 %i to ptr addrspace(1)
+ ret ptr addrspace(1) %p
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-struct.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-struct.ll
new file mode 100644
index 0000000000000..893cc4d8b53ba
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-struct.ll
@@ -0,0 +1,59 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Test untyped pointers with struct types.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#F32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#CROSS_PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
+
+%struct.Point = type { float, float }
+%struct.Data = type { i32, float, i32 }
+
+; CHECK: OpFunction
+; CHECK: OpFunctionParameter %[[#CROSS_PTR]]
+define spir_kernel void @test_struct_load(ptr addrspace(1) %point, ptr addrspace(1) %out) {
+entry:
+ %x = load float, ptr addrspace(1) %point, align 4
+ store float %x, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR %[[#CROSS_PTR]]
+define spir_kernel void @test_struct_gep(ptr addrspace(1) %point, ptr addrspace(1) %out) {
+entry:
+ %y_ptr = getelementptr %struct.Point, ptr addrspace(1) %point, i64 0, i32 1
+ %y = load float, ptr addrspace(1) %y_ptr, align 4
+ store float %y, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_struct_multi_field(ptr addrspace(1) %data, ptr addrspace(1) %out_i, ptr addrspace(1) %out_f) {
+entry:
+ %field0_ptr = getelementptr %struct.Data, ptr addrspace(1) %data, i64 0, i32 0
+ %field1_ptr = getelementptr %struct.Data, ptr addrspace(1) %data, i64 0, i32 1
+ %val0 = load i32, ptr addrspace(1) %field0_ptr, align 4
+ %val1 = load float, ptr addrspace(1) %field1_ptr, align 4
+ store i32 %val0, ptr addrspace(1) %out_i, align 4
+ store float %val1, ptr addrspace(1) %out_f, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR
+define spir_kernel void @test_struct_alloca(ptr addrspace(1) %out) {
+entry:
+ %local = alloca %struct.Point, align 4
+ %x_ptr = getelementptr %struct.Point, ptr %local, i64 0, i32 0
+ store float 1.0, ptr %x_ptr, align 4
+ %x = load float, ptr %x_ptr, align 4
+ store float %x, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-types.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-types.ll
new file mode 100644
index 0000000000000..e6c288d8be771
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-types.ll
@@ -0,0 +1,59 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Test untyped pointer types for different storage classes and data types.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: OpTypeUntypedPointerKHR CrossWorkgroup
+; CHECK-DAG: OpTypeUntypedPointerKHR Workgroup
+
+define spir_kernel void @test_i8_ptr(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load i8, ptr addrspace(1) %in, align 1
+ store i8 %val, ptr addrspace(1) %out, align 1
+ ret void
+}
+
+define spir_kernel void @test_i16_ptr(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load i16, ptr addrspace(1) %in, align 2
+ store i16 %val, ptr addrspace(1) %out, align 2
+ ret void
+}
+
+define spir_kernel void @test_i32_ptr(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load i32, ptr addrspace(1) %in, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+define spir_kernel void @test_i64_ptr(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load i64, ptr addrspace(1) %in, align 8
+ store i64 %val, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+define spir_kernel void @test_f32_ptr(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load float, ptr addrspace(1) %in, align 4
+ store float %val, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+define spir_kernel void @test_f64_ptr(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load double, ptr addrspace(1) %in, align 8
+ store double %val, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+; CHECK: OpFunctionParameter
+define spir_kernel void @test_local_ptr(ptr addrspace(3) %local_mem) {
+entry:
+ store i32 42, ptr addrspace(3) %local_mem, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-vector.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-vector.ll
new file mode 100644
index 0000000000000..473978dd20a47
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-ptr-vector.ll
@@ -0,0 +1,52 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Test untyped pointers with vector types.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: OpTypeUntypedPointerKHR CrossWorkgroup
+
+; CHECK: OpFunction
+; CHECK: OpFunctionParameter
+; CHECK: OpLoad
+; CHECK: OpStore
+define spir_kernel void @test_v4i32(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load <4 x i32>, ptr addrspace(1) %in, align 16
+ store <4 x i32> %val, ptr addrspace(1) %out, align 16
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpFunctionParameter
+; CHECK: OpLoad
+; CHECK: OpStore
+define spir_kernel void @test_v4f32(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+entry:
+ %val = load <4 x float>, ptr addrspace(1) %in, align 16
+ store <4 x float> %val, ptr addrspace(1) %out, align 16
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedPtrAccessChainKHR
+define spir_kernel void @test_vector_gep(ptr addrspace(1) %base, ptr addrspace(1) %out) {
+entry:
+ %ptr = getelementptr <4 x i32>, ptr addrspace(1) %base, i64 2
+ %val = load <4 x i32>, ptr addrspace(1) %ptr, align 16
+ store <4 x i32> %val, ptr addrspace(1) %out, align 16
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR
+define spir_kernel void @test_vector_alloca(ptr addrspace(1) %out) {
+entry:
+ %local = alloca <4 x float>, align 16
+ store <4 x float> <float 1.0, float 2.0, float 3.0, float 4.0>, ptr %local, align 16
+ %val = load <4 x float>, ptr %local, align 16
+ store <4 x float> %val, ptr addrspace(1) %out, align 16
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-variable.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-variable.ll
new file mode 100644
index 0000000000000..1b0804a1e729d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_untyped_pointers/untyped-variable.ll
@@ -0,0 +1,84 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_untyped_pointers %s -o - -filetype=obj | spirv-val %}
+
+; Test OpUntypedVariableKHR for local variables.
+
+; CHECK: OpCapability UntypedPointersKHR
+; CHECK: OpExtension "SPV_KHR_untyped_pointers"
+
+; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#F32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#F64:]] = OpTypeFloat 64
+; CHECK-DAG: %[[#FUNC_PTR:]] = OpTypeUntypedPointerKHR Function
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#I8]]
+define spir_kernel void @test_alloca_i8() {
+entry:
+ %local = alloca i8, align 1
+ store i8 42, ptr %local, align 1
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#I32]]
+define spir_kernel void @test_alloca_i32() {
+entry:
+ %local = alloca i32, align 4
+ store i32 42, ptr %local, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#I64]]
+define spir_kernel void @test_alloca_i64() {
+entry:
+ %local = alloca i64, align 8
+ store i64 42, ptr %local, align 8
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#F32]]
+define spir_kernel void @test_alloca_float() {
+entry:
+ %local = alloca float, align 4
+ store float 3.14, ptr %local, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#F64]]
+define spir_kernel void @test_alloca_double() {
+entry:
+ %local = alloca double, align 8
+ store double 3.14, ptr %local, align 8
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK-DAG: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#I32]]
+; CHECK-DAG: OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#F32]]
+define spir_kernel void @test_multiple_alloca() {
+entry:
+ %int_local = alloca i32, align 4
+ %float_local = alloca float, align 4
+ store i32 42, ptr %int_local, align 4
+ store float 3.14, ptr %float_local, align 4
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#VAR:]] = OpUntypedVariableKHR %[[#FUNC_PTR]] Function %[[#I32]]
+; CHECK: OpStore %[[#VAR]]
+; CHECK: OpLoad %[[#I32]] %[[#VAR]]
+define spir_kernel void @test_alloca_load_store(ptr addrspace(1) %out) {
+entry:
+ %local = alloca i32, align 4
+ store i32 100, ptr %local, align 4
+ %val = load i32, ptr %local, align 4
+ store i32 %val, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/externally-initialized.ll b/llvm/test/CodeGen/SPIRV/externally-initialized.ll
index ac547417359d3..76d3a3ca20594 100644
--- a/llvm/test/CodeGen/SPIRV/externally-initialized.ll
+++ b/llvm/test/CodeGen/SPIRV/externally-initialized.ll
@@ -14,7 +14,7 @@
; CHECK-AMDGCNSPIRV: OpExtension "SPV_INTEL_global_variable_host_access"
; CHECK-AMDGCNSPIRV: OpName %[[#G:]] "G"
; CHECK-AMDGCNSPIRV: OpDecorate %[[#G]] HostAccessINTEL 3 "G"
-; CHECK-AMDGCNSPIRV-DAG: %[[#G]] = OpVariable
+; CHECK-AMDGCNSPIRV-DAG: %[[#G]] = OpUntypedVariableKHR
@G = external addrspace(1) externally_initialized global i32
diff --git a/llvm/test/CodeGen/SPIRV/fembed-bitcode-marker.ll b/llvm/test/CodeGen/SPIRV/fembed-bitcode-marker.ll
index 4c29a8db8c743..6cf712703588d 100644
--- a/llvm/test/CodeGen/SPIRV/fembed-bitcode-marker.ll
+++ b/llvm/test/CodeGen/SPIRV/fembed-bitcode-marker.ll
@@ -15,10 +15,10 @@
; SPIRV: %[[#UCHAR_PTR:]] = OpTypePointer Generic %[[#UCHAR]]
; AMDGCNSPIRV: %[[#UINT64_MAX:]] = OpConstant %[[#UINT64]] 18446744073709551615
; AMDGCNSPIRV: %[[#UCHAR_ARR_UINT64_MAX:]] = OpTypeArray %[[#UCHAR]] %[[#UINT64_MAX]]
-; AMDGCNSPIRV: %[[#UCHAR_ARR_UINT64_MAX_PTR:]] = OpTypePointer CrossWorkgroup %[[#UCHAR_ARR_UINT64_MAX]]
+; AMDGCNSPIRV: %[[#CROSS_PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
; AMDGCNSPIRV: %[[#CONST_UCHAR_ARR_UINT64_MAX:]] = OpConstantNull %[[#UCHAR_ARR_UINT64_MAX]]
; SPIRV: %[[#CONST_UCHAR_NULL_PTR:]] = OpConstantNull %[[#UCHAR_PTR]]
-; AMDGCNSPIRV: %[[#LLVM_EMBEDDED_MODULE]] = OpVariable %[[#UCHAR_ARR_UINT64_MAX_PTR]] CrossWorkgroup %[[#CONST_UCHAR_ARR_UINT64_MAX]]
+; AMDGCNSPIRV: %[[#LLVM_EMBEDDED_MODULE]] = OpUntypedVariableKHR %[[#CROSS_PTR]] CrossWorkgroup %[[#UCHAR_ARR_UINT64_MAX]] %[[#CONST_UCHAR_ARR_UINT64_MAX]]
; SPIRV: %[[#LLVM_EMBEDDED_MODULE]] = OpVariable %[[#]] CrossWorkgroup %[[#CONST_UCHAR_NULL_PTR]]
define spir_kernel void @foo() {
diff --git a/llvm/test/CodeGen/SPIRV/fembed-bitcode.ll b/llvm/test/CodeGen/SPIRV/fembed-bitcode.ll
index a75b44925a1ea..0092cbf19b5e6 100644
--- a/llvm/test/CodeGen/SPIRV/fembed-bitcode.ll
+++ b/llvm/test/CodeGen/SPIRV/fembed-bitcode.ll
@@ -1,6 +1,6 @@
-; RUN: llc -verify-machineinstrs -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s --check-prefixes=CHECK,SPIRV
; RUN: %if spirv-tools %{ llc -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-; RUN: llc -verify-machineinstrs -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -mtriple=spirv64-amd-amdhsa %s -o - | FileCheck %s --check-prefixes=CHECK,AMDGCNSPIRV
; RUN: %if spirv-tools %{ llc -mtriple=spirv64-amd-amdhsa %s -o - -filetype=obj | spirv-val %}
;
; Verify that we can lower the embedded module and cmdline.
@@ -19,12 +19,15 @@
; CHECK: %[[#UCHAR_ARR_5:]] = OpTypeArray %[[#UCHAR]] %[[#FIVE]]
; CHECK: %[[#FOUR:]] = OpConstant %[[#UINT]] 4
; CHECK: %[[#UCHAR_ARR_4:]] = OpTypeArray %[[#UCHAR]] %[[#FOUR]]
-; CHECK: %[[#UCHAR_ARR_5_PTR:]] = OpTypePointer CrossWorkgroup %[[#UCHAR_ARR_5]]
-; CHECK: %[[#UCHAR_ARR_4_PTR:]] = OpTypePointer CrossWorkgroup %[[#UCHAR_ARR_4]]
+; SPIRV: %[[#UCHAR_ARR_5_PTR:]] = OpTypePointer CrossWorkgroup %[[#UCHAR_ARR_5]]
+; SPIRV: %[[#UCHAR_ARR_4_PTR:]] = OpTypePointer CrossWorkgroup %[[#UCHAR_ARR_4]]
+; AMDGCNSPIRV: %[[#CROSS_PTR:]] = OpTypeUntypedPointerKHR CrossWorkgroup
; CHECK: %[[#CONST_UCHAR_ARR_4:]] = OpConstantComposite %[[#UCHAR_ARR_4]]
-; CHECK: %[[#LLVM_EMBEDDED_MODULE]] = OpVariable %[[#UCHAR_ARR_4_PTR]] CrossWorkgroup %[[#CONST_UCHAR_ARR_4]]
+; SPIRV: %[[#LLVM_EMBEDDED_MODULE]] = OpVariable %[[#UCHAR_ARR_4_PTR]] CrossWorkgroup %[[#CONST_UCHAR_ARR_4]]
+; AMDGCNSPIRV: %[[#LLVM_EMBEDDED_MODULE]] = OpUntypedVariableKHR %[[#CROSS_PTR]] CrossWorkgroup %[[#UCHAR_ARR_4]] %[[#CONST_UCHAR_ARR_4]]
; CHECK: %[[#CONST_UCHAR_ARR_5:]] = OpConstantComposite %[[#UCHAR_ARR_5]]
-; CHECK: %[[#LLVM_CMDLINE]] = OpVariable %[[#UCHAR_ARR_5_PTR]] CrossWorkgroup %[[#CONST_UCHAR_ARR_5]]
+; SPIRV: %[[#LLVM_CMDLINE]] = OpVariable %[[#UCHAR_ARR_5_PTR]] CrossWorkgroup %[[#CONST_UCHAR_ARR_5]]
+; AMDGCNSPIRV: %[[#LLVM_CMDLINE]] = OpUntypedVariableKHR %[[#CROSS_PTR]] CrossWorkgroup %[[#UCHAR_ARR_5]] %[[#CONST_UCHAR_ARR_5]]
define spir_kernel void @foo() {
entry:
diff --git a/llvm/test/CodeGen/SPIRV/hip_dyn_lds.ll b/llvm/test/CodeGen/SPIRV/hip_dyn_lds.ll
index 531d5817ee86c..1f2d7c9fd2d7f 100644
--- a/llvm/test/CodeGen/SPIRV/hip_dyn_lds.ll
+++ b/llvm/test/CodeGen/SPIRV/hip_dyn_lds.ll
@@ -3,12 +3,12 @@
; CHECK: OpName %[[#LDS:]] "lds"
; CHECK: OpDecorate %[[#LDS]] LinkageAttributes "lds" Import
-; CHECK: %[[#UINT:]] = OpTypeInt 32 0
; CHECK: %[[#UINT64:]] = OpTypeInt 64 0
+; CHECK: %[[#UINT:]] = OpTypeInt 32 0
; CHECK: %[[#UINT64_MAX:]] = OpConstant %[[#UINT64]] 18446744073709551615
; CHECK: %[[#LDS_ARR_TY:]] = OpTypeArray %[[#UINT]] %[[#UINT64_MAX]]
-; CHECK: %[[#LDS_ARR_PTR_WG:]] = OpTypePointer Workgroup %[[#LDS_ARR_TY]]
-; CHECK: %[[#LDS]] = OpVariable %[[#LDS_ARR_PTR_WG]] Workgroup
+; CHECK: %[[#LDS_PTR_WG:]] = OpTypeUntypedPointerKHR Workgroup
+; CHECK: %[[#LDS]] = OpUntypedVariableKHR %[[#LDS_PTR_WG]] Workgroup %[[#LDS_ARR_TY]]
@lds = external addrspace(3) global [0 x i32]
>From f0636021fdf2fa2d62c23e23d2847d2b55ec40f7 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Wed, 3 Jun 2026 11:00:21 +0200
Subject: [PATCH 2/3] gate shader +all
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 4 ++--
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 8640eab30c95d..102c9a4b7f289 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -1114,7 +1114,7 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypePointer(
const SPIRVSubtarget &ST =
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers) &&
- !isSpecialOpaqueElementType(ElemType))
+ !ST.isShader() && !isSpecialOpaqueElementType(ElemType))
return getOrCreateSPIRVUntypedPointerType(SC, MIRBuilder);
if (!Reg.isValid())
@@ -2103,7 +2103,7 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerTypeInternal(
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
if (!ForceTyped &&
ST.canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers) &&
- !isSpecialOpaqueElementType(BaseType))
+ !ST.isShader() && !isSpecialOpaqueElementType(BaseType))
return getOrCreateSPIRVUntypedPointerType(SC, MIRBuilder);
const Type *PointerElementType = getTypeForSPIRVType(BaseType);
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 34b4a3765f79e..439198bb24eb6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -594,7 +594,8 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
GR->getOrCreateSPIRVPointerType(ElementTy, MI, SC);
// For untyped pointers, store the element type for later use.
- if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers)) {
+ if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_untyped_pointers) &&
+ !ST->isShader()) {
SPIRVTypeInst ElemSpvType = GR->getOrCreateSPIRVType(
ElementTy, MIB, SPIRV::AccessQualifier::ReadWrite, true);
GR->setUntypedPtrElementType(Reg, ElemSpvType);
>From 88145c94c96027cd25c1d3e8e84cc3940196d9b9 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Wed, 3 Jun 2026 11:06:04 +0200
Subject: [PATCH 3/3] fix comment
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 102c9a4b7f289..b6346d59eae27 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -1520,8 +1520,8 @@ unsigned SPIRVGlobalRegistry::getSPIRVTypeByteSize(SPIRVTypeInst Type) const {
bool SPIRVGlobalRegistry::isSpecialOpaqueElementType(
SPIRVTypeInst ElemType) const {
- // A pointer to a function (SPV_INTEL_function_pointers) must keep its
- // function type, which an untyped pointer cannot express.
+ // A function pointer has to keep its function type, which an untyped pointer
+ // cannot express.
if (ElemType && ElemType->getOpcode() == SPIRV::OpTypeFunction)
return true;
auto It = SPIRVToLLVMType.find(ElemType);
More information about the llvm-commits
mailing list