From 690f8afb4ace25cb09df1c0650437126eb3b7d5e Mon Sep 17 00:00:00 2001 From: Victor Lomuller Date: Mon, 28 Apr 2025 12:12:48 +0100 Subject: [PATCH] [SPIRV] Add intrinsic for OpGenericCastToPtrExplicit The patch adds an intrinsic to encode OpGenericCastToPtrExplicit and the associated lowering logic. --- llvm/include/llvm/IR/IntrinsicsSPIRV.td | 7 +++ llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 4 ++ .../Target/SPIRV/SPIRVInstructionSelector.cpp | 15 +++++++ .../pointers/generic_cast_to_ptr_explicit.ll | 44 +++++++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 llvm/test/CodeGen/SPIRV/pointers/generic_cast_to_ptr_explicit.ll diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 77cca0a58424f..404467781b4d0 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +def generic_ptr_ty : LLVMQualPointerType<4>; + let TargetPrefix = "spv" in { def int_spv_assign_type : Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>; def int_spv_assign_ptr_type : Intrinsic<[], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg>]>; @@ -146,4 +148,9 @@ let TargetPrefix = "spv" in { // FPMaxErrorDecorationINTEL def int_spv_assign_fpmaxerror_decoration: Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>; + + // Convert between the generic storage class and a concrete one. + def int_spv_generic_cast_to_ptr_explicit + : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [generic_ptr_ty], + [IntrNoMem, NoUndef]>; } diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index 6205dfedb79fb..4325023406c7c 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -680,6 +680,10 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( } else { llvm_unreachable("Unknown handle type for spv_resource_getpointer."); } + } else if (II && II->getIntrinsicID() == + Intrinsic::spv_generic_cast_to_ptr_explicit) { + Ty = deduceElementTypeHelper(CI->getArgOperand(0), Visited, + UnknownElemTypeI8); } else if (Function *CalledF = CI->getCalledFunction()) { std::string DemangledName = getOclOrSpirvBuiltinDemangledName(CalledF->getName()); diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 8304077f049a3..e947d1b3592da 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -3120,6 +3120,21 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, .addUse(MemSemReg) .constrainAllUses(TII, TRI, RBI); } + case Intrinsic::spv_generic_cast_to_ptr_explicit: { + Register PtrReg = I.getOperand(I.getNumExplicitDefs() + 1).getReg(); + SPIRV::StorageClass::StorageClass ResSC = + GR.getPointerStorageClass(ResType); + if (!isGenericCastablePtr(ResSC)) + report_fatal_error("The target storage class is not castable from the " + "Generic storage class"); + return BuildMI(BB, I, I.getDebugLoc(), + TII.get(SPIRV::OpGenericCastToPtrExplicit)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(PtrReg) + .addImm(ResSC) + .constrainAllUses(TII, TRI, RBI); + } case Intrinsic::spv_lifetime_start: case Intrinsic::spv_lifetime_end: { unsigned Op = IID == Intrinsic::spv_lifetime_start ? SPIRV::OpLifetimeStart diff --git a/llvm/test/CodeGen/SPIRV/pointers/generic_cast_to_ptr_explicit.ll b/llvm/test/CodeGen/SPIRV/pointers/generic_cast_to_ptr_explicit.ll new file mode 100644 index 0000000000000..f2c5a82dca64d --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/pointers/generic_cast_to_ptr_explicit.ll @@ -0,0 +1,44 @@ +; 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 %} + +; Make sure SPIRV operation function calls for generic_cast_to_ptr_explicit are lowered correctly. + +; CHECK: %[[#Char:]] = OpTypeInt 8 0 +; CHECK: %[[#GenericPtr:]] = OpTypePointer Generic %[[#Char]] +; CHECK: %[[#GlobalPtr:]] = OpTypePointer CrossWorkgroup %[[#Char]] +; CHECK: %[[#LocalPtr:]] = OpTypePointer Workgroup %[[#Char]] +; CHECK: %[[#PrivatePtr:]] = OpTypePointer Function %[[#Char]] + +; CHECK: OpFunction %[[#GlobalPtr]] +; CHECK-NEXT: %[[#Arg:]] = OpFunctionParameter %[[#GenericPtr]] +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpGenericCastToPtrExplicit %[[#GlobalPtr]] %[[#Arg]] CrossWorkgroup +define ptr addrspace(1) @test_to_global(ptr addrspace(4) noundef %ptr) { +entry: + %cast = call spir_func noundef ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit.p1(ptr addrspace(4) noundef %ptr) + ret ptr addrspace(1) %cast +} + +; CHECK: OpFunction %[[#LocalPtr]] +; CHECK-NEXT: %[[#Arg:]] = OpFunctionParameter %[[#GenericPtr]] +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpGenericCastToPtrExplicit %[[#LocalPtr]] %[[#Arg]] Workgroup +define ptr addrspace(3) @test_to_local(ptr addrspace(4) noundef %ptr) { +entry: + %cast = call spir_func noundef ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit.p3(ptr addrspace(4) noundef %ptr) + ret ptr addrspace(3) %cast +} + +; CHECK: OpFunction %[[#PrivatePtr]] +; CHECK-NEXT: %[[#Arg:]] = OpFunctionParameter %[[#GenericPtr]] +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpGenericCastToPtrExplicit %[[#PrivatePtr]] %[[#Arg]] Function +define ptr @test_to_private(ptr addrspace(4) noundef %ptr) { +entry: + %cast = call spir_func noundef ptr @llvm.spv.generic.cast.to.ptr.explicit.p0(ptr addrspace(4) noundef %ptr) + ret ptr %cast +} + +declare noundef ptr @llvm.spv.generic.cast.to.ptr.explicit.p0(ptr addrspace(4)) +declare noundef ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit.p1(ptr addrspace(4)) +declare noundef ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit.p3(ptr addrspace(4))