From b47d791ba002383c909a434c42db75d3e9fb86d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 5 Mar 2025 15:46:24 +0100 Subject: [PATCH] [SPIR-V] Handle struct member loading from ptrcast Adds support for loading the first element of an aggregate from a GEP instruction & load to the member type. --- .../Target/SPIRV/SPIRVLegalizePointerCast.cpp | 23 ++++--- .../CodeGen/SPIRV/pointers/load-struct.ll | 64 +++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 llvm/test/CodeGen/SPIRV/pointers/load-struct.ll diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp index ec419d25cd317..2ccff9dd321ec 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp @@ -103,12 +103,8 @@ class SPIRVLegalizePointerCast : public FunctionPass { auto *GEP = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args}); GR->buildAssignPtr(B, ElementType, GEP); - const auto *TLI = TM->getSubtargetImpl()->getTargetLowering(); - MachineMemOperand::Flags Flags = TLI->getLoadMemOperandFlags( - *BadLoad, BadLoad->getFunction()->getDataLayout()); - Instruction *LI = B.CreateIntrinsic( - Intrinsic::spv_load, {BadLoad->getOperand(0)->getType()}, - {GEP, B.getInt16(Flags), B.getInt8(BadLoad->getAlign().value())}); + LoadInst *LI = B.CreateLoad(ElementType, GEP); + LI->setAlignment(BadLoad->getAlign()); buildAssignType(B, ElementType, LI); return LI; } @@ -123,6 +119,7 @@ class SPIRVLegalizePointerCast : public FunctionPass { auto *SAT = dyn_cast(FromTy); auto *SVT = dyn_cast(FromTy); + auto *SST = dyn_cast(FromTy); auto *DVT = dyn_cast(ToTy); B.SetInsertPoint(LI); @@ -144,6 +141,11 @@ class SPIRVLegalizePointerCast : public FunctionPass { // - float3 v3 = vector4; else if (SVT && DVT) Output = loadVectorFromVector(B, SVT, DVT, OriginalOperand); + // Destination is the scalar type stored at the start of an aggregate. + // - struct S { float m }; + // - float v = s.m; + else if (SST && SST->getTypeAtIndex(0u) == ToTy) + Output = loadFirstValueFromAggregate(B, ToTy, OriginalOperand, LI); else llvm_unreachable("Unimplemented implicit down-cast from load."); @@ -166,10 +168,11 @@ class SPIRVLegalizePointerCast : public FunctionPass { continue; } - IntrinsicInst *Intrin = dyn_cast(User); - if (Intrin->getIntrinsicID() == Intrinsic::spv_assign_ptr_type) { - DeadInstructions.push_back(Intrin); - continue; + if (IntrinsicInst *Intrin = dyn_cast(User)) { + if (Intrin->getIntrinsicID() == Intrinsic::spv_assign_ptr_type) { + DeadInstructions.push_back(Intrin); + continue; + } } llvm_unreachable("Unsupported ptrcast user. Please fix."); diff --git a/llvm/test/CodeGen/SPIRV/pointers/load-struct.ll b/llvm/test/CodeGen/SPIRV/pointers/load-struct.ll new file mode 100644 index 0000000000000..280a7e4f9053b --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/pointers/load-struct.ll @@ -0,0 +1,64 @@ +; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0 +; CHECK-DAG: %[[#float:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#float_fp:]] = OpTypePointer Function %[[#float]] +; CHECK-DAG: %[[#float_pp:]] = OpTypePointer Private %[[#float]] +; CHECK-DAG: %[[#uint_fp:]] = OpTypePointer Function %[[#uint]] +; CHECK-DAG: %[[#uint_0:]] = OpConstant %[[#uint]] 0 +; CHECK-DAG: %[[#sf:]] = OpTypeStruct %[[#float]] +; CHECK-DAG: %[[#su:]] = OpTypeStruct %[[#uint]] +; CHECK-DAG: %[[#sfuf:]] = OpTypeStruct %[[#float]] %[[#uint]] %[[#float]] +; CHECK-DAG: %[[#sf_fp:]] = OpTypePointer Function %[[#sf]] +; CHECK-DAG: %[[#su_fp:]] = OpTypePointer Function %[[#su]] +; CHECK-DAG: %[[#sfuf_fp:]] = OpTypePointer Function %[[#sfuf]] +; CHECK-DAG: %[[#sfuf_pp:]] = OpTypePointer Private %[[#sfuf]] + +%struct.SF = type { float } +%struct.SU = type { i32 } +%struct.SFUF = type { float, i32, float } + +@gsfuf = external addrspace(10) global %struct.SFUF +; CHECK: %[[#gsfuf:]] = OpVariable %[[#sfuf_pp]] Private + +define internal spir_func float @foo() { + %1 = alloca %struct.SF, align 4 +; CHECK: %[[#var:]] = OpVariable %[[#sf_fp]] Function + + %2 = load float, ptr %1, align 4 +; CHECK: %[[#tmp:]] = OpAccessChain %[[#float_fp]] %[[#var]] %[[#uint_0]] +; CHECK: %[[#val:]] = OpLoad %[[#float]] %[[#tmp]] Aligned 4 + + ret float %2 +} + +define internal spir_func i32 @bar() { + %1 = alloca %struct.SU, align 4 +; CHECK: %[[#var:]] = OpVariable %[[#su_fp]] Function + + %2 = load i32, ptr %1, align 4 +; CHECK: %[[#tmp:]] = OpAccessChain %[[#uint_fp]] %[[#var]] %[[#uint_0]] +; CHECK: %[[#val:]] = OpLoad %[[#uint]] %[[#tmp]] Aligned 4 + + ret i32 %2 +} + +define internal spir_func float @baz() { + %1 = alloca %struct.SFUF, align 4 +; CHECK: %[[#var:]] = OpVariable %[[#sfuf_fp]] Function + + %2 = load float, ptr %1, align 4 +; CHECK: %[[#tmp:]] = OpAccessChain %[[#float_fp]] %[[#var]] %[[#uint_0]] +; CHECK: %[[#val:]] = OpLoad %[[#float]] %[[#tmp]] Aligned 4 + + ret float %2 +} + +define internal spir_func float @biz() { + %2 = load float, ptr addrspace(10) @gsfuf, align 4 +; CHECK: %[[#tmp:]] = OpAccessChain %[[#float_pp]] %[[#gsfuf]] %[[#uint_0]] +; CHECK: %[[#val:]] = OpLoad %[[#float]] %[[#tmp]] Aligned 4 + + ret float %2 +}