https://github.com/Keenuts updated https://github.com/llvm/llvm-project/pull/177332
From 80b6fc569e72d87c1e598d1f022b93cc7696924b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Wed, 21 Jan 2026 16:47:24 +0100 Subject: [PATCH 01/10] [Clang][HLSL] Start emitting structured GEP instruction StructuredGEP is a new LLVM intrinsic which will allow to emit proper logical SPIR-V or DXIL. To properly stage this change going across FE, BE and optimizations, this commits adds a new flag: - `-fexperimental-emit-sgep` When used, this flag will allow compatible frontends to emit the new instructions. This will also allow us to migrate tests bit by bit, adding the flag to each migrated test as we make progress on the implementation. Once the frontend migration complete, the flag will remain, and work on the backend will start. Compatible backends like SPIR-V will first allow both instructions, but then, depending on a target bit similar to `requiresStructuredCFG`, will declare that they require the SGEP instruction and will start enforcing it. Once the whole chain completed, the flag will be defaulted to true and removed, finishing the migration. --- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Options/Options.td | 7 +++ clang/lib/CodeGen/CGExpr.cpp | 32 +++++++++++ clang/lib/CodeGen/CGHLSLRuntime.cpp | 29 ++++++++-- clang/test/CodeGenHLSL/sgep/array_load.hlsl | 52 ++++++++++++++++++ clang/test/CodeGenHLSL/sgep/array_store.hlsl | 55 +++++++++++++++++++ clang/test/CodeGenHLSL/sgep/load_global.hlsl | 45 +++++++++++++++ .../test/CodeGenHLSL/sgep/object_method.hlsl | 30 ++++++++++ 8 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGenHLSL/sgep/array_load.hlsl create mode 100644 clang/test/CodeGenHLSL/sgep/array_store.hlsl create mode 100644 clang/test/CodeGenHLSL/sgep/load_global.hlsl create mode 100644 clang/test/CodeGenHLSL/sgep/object_method.hlsl diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index a86394aa44f6b..a922e599a55bd 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -248,6 +248,7 @@ LANGOPT(HLSLStrictAvailability, 1, 0, NotCompatible, "Strict availability diagnostic mode for HLSL built-in functions.") LANGOPT(HLSLSpvUseUnknownImageFormat, 1, 0, NotCompatible, "For storage images and texel buffers, sets the default format to 'Unknown' when not specified via the `vk::image_format` attribute. If this option is not used, the format is inferred from the resource's data type.") LANGOPT(HLSLSpvEnableMaximalReconvergence, 1, 0, NotCompatible, "Enables the MaximallyReconvergesKHR execution mode for this module. This ensures that control flow reconverges at well-defined merge points as defined by the Vulkan spec.") +LANGOPT(EmitStructuredGEP, 1, 0, NotCompatible, "Emit structured_gep instructions instead of GEP") LANGOPT(CUDAIsDevice , 1, 0, NotCompatible, "compiling for CUDA device") LANGOPT(CUDAHostDeviceConstexpr, 1, 1, NotCompatible, "treating unattributed constexpr functions as __host__ __device__") diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 43727236ed5a4..c033bce666ecc 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -9811,6 +9811,13 @@ def fhlsl_spv_enable_maximal_reconvergence "well-defined merge points as defined by the Vulkan spec.">, MarshallingInfoFlag<LangOpts<"HLSLSpvEnableMaximalReconvergence">>; +def fexperimental_emit_sgep + : Flag<["-"], "fexperimental-emit-sgep">, + Visibility<[CC1Option, DXCOption]>, + HelpText<"Emit structured GEP intrinsic instead of GEP instructions " + "(experimental).">, + MarshallingInfoFlag<LangOpts<"EmitStructuredGEP">>; + def no_wasm_opt : Flag<["--"], "no-wasm-opt">, Group<m_Group>, HelpText<"Disable the wasm-opt optimizer">, diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 76a3939cd28eb..7d441aebfb6c2 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4498,6 +4498,17 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, if (!E->getType()->isVariableArrayType()) { assert(isa<llvm::ArrayType>(Addr.getElementType()) && "Expected pointer to array"); + + if (getLangOpts().HLSL && getLangOpts().EmitStructuredGEP) { + llvm::Value *Ptr = Addr.emitRawPointer(*this); + if (auto *C = dyn_cast<llvm::Constant>(Ptr)) + return Address(C, Addr.getElementType(), Addr.getAlignment(), + Addr.isKnownNonNull()); + return Address( + Builder.CreateStructuredGEP(NewTy, Addr.getBasePointer(), {}), + Addr.getElementType(), Addr.getAlignment(), Addr.isKnownNonNull()); + } + Addr = Builder.CreateConstArrayGEP(Addr, 0, "arraydecay"); } @@ -4537,6 +4548,9 @@ static llvm::Value *emitArraySubscriptGEP(CodeGenFunction &CGF, bool signedIndices, SourceLocation loc, const llvm::Twine &name = "arrayidx") { + if (CGF.getLangOpts().HLSL && inbounds && CGF.getLangOpts().EmitStructuredGEP) + return CGF.Builder.CreateStructuredGEP(elemType, ptr, indices); + if (inbounds) { return CGF.EmitCheckedInBoundsGEP(elemType, ptr, indices, signedIndices, CodeGenFunction::NotSubtraction, loc, @@ -4548,10 +4562,18 @@ static llvm::Value *emitArraySubscriptGEP(CodeGenFunction &CGF, static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, ArrayRef<llvm::Value *> indices, + llvm::Type *arrayType, llvm::Type *elementType, bool inbounds, bool signedIndices, SourceLocation loc, CharUnits align, const llvm::Twine &name = "arrayidx") { + if (CGF.getLangOpts().HLSL && arrayType && inbounds && + CGF.getLangOpts().EmitStructuredGEP) + return RawAddress(CGF.Builder.CreateStructuredGEP(arrayType, + addr.emitRawPointer(CGF), + indices.drop_front()), + elementType, align); + if (inbounds) { return CGF.EmitCheckedInBoundsGEP(addr, indices, elementType, signedIndices, CodeGenFunction::NotSubtraction, loc, @@ -4668,6 +4690,8 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, if (!LastIndex || (!CGF.IsInPreservedAIRegion && !IsPreserveAIArrayBase(CGF, Base))) { addr = emitArraySubscriptGEP(CGF, addr, indices, + arrayType ? CGF.ConvertTypeForMem(*arrayType) + : nullptr, CGF.ConvertTypeForMem(eltType), inbounds, signedIndices, loc, eltAlign, name); return addr; @@ -5522,6 +5546,14 @@ static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base, unsigned idx = CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMFieldNo(field); + llvm::Type *StructType = + CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMType(); + + if (CGF.getLangOpts().HLSL && CGF.getLangOpts().EmitStructuredGEP) + return RawAddress( + CGF.Builder.CreateStructuredGEP(StructType, base.emitRawPointer(CGF), + {CGF.Builder.getSize(idx)}), + base.getElementType(), base.getAlignment()); if (!IsInBounds) return CGF.Builder.CreateConstGEP2_32(base, 0, idx, field->getName()); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index cff2824d29dc5..e52da1b1d845a 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -36,6 +36,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -1108,6 +1109,8 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV, // Make sure the global variable is buffer resource handle llvm::Type *HandleTy = GV->getValueType(); assert(HandleTy->isTargetExtTy() && "unexpected type of the buffer global"); + llvm::Type *UnderlyingType = + cast<TargetExtType>(HandleTy)->getTypeParameter(0); llvm::Value *CreateHandle = Builder.CreateIntrinsic( /*ReturnType=*/HandleTy, IntrID, Args, nullptr, @@ -1395,9 +1398,18 @@ std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr( Indices.push_back(Idx); Indices.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 0)); - llvm::Value *GEP = CGF.Builder.CreateGEP(LayoutTy, Addr.emitRawPointer(CGF), - Indices, "cbufferidx"); - Addr = Address(GEP, Addr.getElementType(), RowAlignedSize, KnownNonNull); + if (CGF.getLangOpts().EmitStructuredGEP) { + if (auto *AT = dyn_cast<llvm::ArrayType>(Addr.getElementType())) + LayoutTy = llvm::ArrayType::get(LayoutTy, AT->getNumElements()); + auto *GEP = cast<StructuredGEPInst>(CGF.Builder.CreateStructuredGEP( + LayoutTy, Addr.emitRawPointer(CGF), Indices, "cbufferidx")); + Addr = + Address(GEP, GEP->getResultElementType(), RowAlignedSize, KnownNonNull); + } else { + llvm::Value *GEP = CGF.Builder.CreateGEP(LayoutTy, Addr.emitRawPointer(CGF), + Indices, "cbufferidx"); + Addr = Address(GEP, Addr.getElementType(), RowAlignedSize, KnownNonNull); + } return CGF.MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo); } @@ -1577,9 +1589,14 @@ LValue CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF, llvm::Type *FieldLLVMTy = CGM.getTypes().ConvertTypeForMem(FieldType); CharUnits Align = CharUnits::fromQuantity( CGF.CGM.getDataLayout().getABITypeAlign(FieldLLVMTy)); - Address Addr(CGF.Builder.CreateStructGEP(LayoutTy, Base.getPointer(CGF), - FieldIdx, Field->getName()), - FieldLLVMTy, Align, KnownNonNull); + + Value *Ptr = CGF.getLangOpts().EmitStructuredGEP + ? CGF.Builder.CreateStructuredGEP( + LayoutTy, Base.getPointer(CGF), + llvm::ConstantInt::get(CGM.IntTy, FieldIdx)) + : CGF.Builder.CreateStructGEP(LayoutTy, Base.getPointer(CGF), + FieldIdx, Field->getName()); + Address Addr(Ptr, FieldLLVMTy, Align, KnownNonNull); LValue LV = LValue::MakeAddr(Addr, FieldType, CGM.getContext(), LValueBaseInfo(AlignmentSource::Type), diff --git a/clang/test/CodeGenHLSL/sgep/array_load.hlsl b/clang/test/CodeGenHLSL/sgep/array_load.hlsl new file mode 100644 index 0000000000000..26efa7cef57dc --- /dev/null +++ b/clang/test/CodeGenHLSL/sgep/array_load.hlsl @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIR + +void foo() { +// CHECK: %array = alloca [3 x i32], align 4 + uint array[3] = { 0, 1, 2 }; + +// CHECK-DXIL: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x i32]) %array, i32 2) +// CHECK-SPIR: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x i32]) %array, i64 2) +// CHECK: load i32, ptr %[[#PTR]], align 4 + uint tmp = array[2]; +} + +struct S { + uint a; + uint b; +}; + +void bar() { +// CHECK: %array = alloca [3 x %struct.S], align 1 + S array[3] = { { 0, 1 }, { 2, 3 }, { 3, 4 } }; + +// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i32 2) +// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i32 1) +// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i64 2) +// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i64 1) + +// CHECK: load i32, ptr %[[#B]], align 1 + uint tmp = array[2].b; +} + +struct S2 { + uint a; + S b; + uint c; +}; + +void baz() { +// CHECK: %array = alloca [2 x %struct.S2], align 1 + S2 array[2] = { { 0, { 1, 2 }, 3 }, { 4, { 5, 6 }, 7 } }; + +// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i32 1) +// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i32 1) +// CHECK-DXIL: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i32 0) + +// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i64 1) +// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i64 1) +// CHECK-SPIR: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i64 0) + +// CHECK: load i32, ptr %[[#C]], align 1 + uint tmp = array[1].b.a; +} diff --git a/clang/test/CodeGenHLSL/sgep/array_store.hlsl b/clang/test/CodeGenHLSL/sgep/array_store.hlsl new file mode 100644 index 0000000000000..a798bc47da50c --- /dev/null +++ b/clang/test/CodeGenHLSL/sgep/array_store.hlsl @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIR + +[shader("compute")] +[numthreads(1,1,1)] +void foo() { +// CHECK: %array = alloca [10 x i32], align 4 + uint array[10]; + +// CHECK-DXIL: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %array, i32 2) +// CHECK-SPIR: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %array, i64 2) +// CHECK: store i32 10, ptr %[[#PTR]], align 4 + array[2] = 10; +} + +struct S { + uint a; + uint b; +}; + +void bar() { +// CHECK: %array = alloca [3 x %struct.S], align 1 + S array[3] = { { 0, 1 }, { 2, 3 }, { 3, 4 } }; + +// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i32 2) +// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i32 1) + +// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i64 2) +// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i64 1) + +// CHECK: store i32 10, ptr %[[#B]], align 1 + array[2].b = 10; +} + +struct S2 { + uint a; + S b; + uint c; +}; + +void baz() { +// CHECK: %array = alloca [2 x %struct.S2], align 1 + S2 array[2] = { { 0, { 1, 2 }, 3 }, { 4, { 5, 6 }, 7 } }; + +// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i32 1) +// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i32 1) +// CHECK-DXIL: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i32 0) + +// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i64 1) +// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i64 1) +// CHECK-SPIR: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i64 0) + +// CHECK: store i32 10, ptr %[[#C]], align 1 + array[1].b.a = 10; +} diff --git a/clang/test/CodeGenHLSL/sgep/load_global.hlsl b/clang/test/CodeGenHLSL/sgep/load_global.hlsl new file mode 100644 index 0000000000000..18e57e04e64fc --- /dev/null +++ b/clang/test/CodeGenHLSL/sgep/load_global.hlsl @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK-DXIL +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s --check-prefixes=CHECK-SPIR + +struct S { + uint a; + uint b; + uint c; + uint d; +}; + +// CHECK-DXIL: @_ZL1s = external hidden addrspace(2) global %struct.S, align 1 +// CHECK-DXIL: @_ZL1a = external hidden addrspace(2) constant [4 x i32], align 4 +// CHECK-DXIL: @_ZL1b = external hidden addrspace(2) constant i32, align 4 + +// CHECK-SPIR: @_ZL1s = external hidden addrspace(12) global %struct.S, align 1 +// CHECK-SPIR: @_ZL1a = external hidden addrspace(12) constant [4 x i32], align 4 +// CHECK-SPIR: @_ZL1b = external hidden addrspace(12) constant i32, align 4 +const S s; +const uint a[4]; +const uint b; + +void foo() { + +// CHECK-DXIL: %[[#PTR:]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(%S) @_ZL1s, i32 1) +// CHECK-DXIL: %[[#]] = load i32, ptr addrspace(2) %[[#PTR]], align 4 + +// CHECK-SPIR: %[[#PTR:]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(%S) @_ZL1s, i32 1) +// CHECK-SPIR: %[[#]] = load i32, ptr addrspace(12) %[[#PTR]], align 4 + uint tmp = s.b; +} + +void bar() { +// CHECK-DXIL: %cbufferidx = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([4 x <{ i32, target("dx.Padding", 12) }>]) @_ZL1a, i32 2, i32 0) +// CHECK-DXIL: %[[#]] = load i32, ptr addrspace(2) %cbufferidx, align 16 + +// CHECK-SPIR: %cbufferidx = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([4 x <{ i32, target("spirv.Padding", 12) }>]) @_ZL1a, i64 2, i32 0) +// CHECK-SPIR: %[[#]] = load i32, ptr addrspace(12) %cbufferidx, align 16 + uint tmp = a[2]; +} + +void baz() { +// CHECK-DXIL: %[[#]] = load i32, ptr addrspace(2) @_ZL1b, align 4 +// CHECK-SPIR: %[[#]] = load i32, ptr addrspace(12) @_ZL1b, align 4 + uint tmp = b; +} diff --git a/clang/test/CodeGenHLSL/sgep/object_method.hlsl b/clang/test/CodeGenHLSL/sgep/object_method.hlsl new file mode 100644 index 0000000000000..f54f0c064bb31 --- /dev/null +++ b/clang/test/CodeGenHLSL/sgep/object_method.hlsl @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIR + +struct O { + int value = 0; + + int get() { + return value; + } +}; + +[shader("compute")] +[numthreads(1,1,1)] +void foo() { + O o; + +// CHECK: %o = alloca %struct.O, align 1 +// CHECK-DXIL: call noundef i32 @_ZN1O3getEv(ptr noundef nonnull align 1 dereferenceable(4) %o) +// CHECK-SPIR: call spir_func noundef i32 @_ZN1O3getEv(ptr noundef nonnull align 1 dereferenceable(4) %o) + uint tmp = o.get(); +} + + + +// CHECK-DXIL: define linkonce_odr hidden noundef i32 @_ZN1O3getEv(ptr noundef nonnull align 1 dereferenceable(4) %this) +// CHECK-SPIR: define linkonce_odr hidden spir_func noundef i32 @_ZN1O3getEv(ptr noundef nonnull align 1 dereferenceable(4) %this) +// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.O) %this1, i32 0) +// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.O) %this1, i64 0) +// CHECK: %[[#B:]] = load i32, ptr %[[#A]], align 1 +// CHECK: ret i32 %[[#B]] From 0fbbc98d3443e38a3cc52c324049bbb75c956c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Mon, 26 Jan 2026 13:51:00 +0100 Subject: [PATCH 02/10] pr-feedback: langopt phrasing --- clang/include/clang/Basic/LangOptions.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index a922e599a55bd..49b570ceff7b3 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -248,7 +248,7 @@ LANGOPT(HLSLStrictAvailability, 1, 0, NotCompatible, "Strict availability diagnostic mode for HLSL built-in functions.") LANGOPT(HLSLSpvUseUnknownImageFormat, 1, 0, NotCompatible, "For storage images and texel buffers, sets the default format to 'Unknown' when not specified via the `vk::image_format` attribute. If this option is not used, the format is inferred from the resource's data type.") LANGOPT(HLSLSpvEnableMaximalReconvergence, 1, 0, NotCompatible, "Enables the MaximallyReconvergesKHR execution mode for this module. This ensures that control flow reconverges at well-defined merge points as defined by the Vulkan spec.") -LANGOPT(EmitStructuredGEP, 1, 0, NotCompatible, "Emit structured_gep instructions instead of GEP") +LANGOPT(EmitStructuredGEP, 1, 0, NotCompatible, "Emit structured GEP intrinsics instead of GEP instructions") LANGOPT(CUDAIsDevice , 1, 0, NotCompatible, "compiling for CUDA device") LANGOPT(CUDAHostDeviceConstexpr, 1, 1, NotCompatible, "treating unattributed constexpr functions as __host__ __device__") From 895fa27e4c42220d9ba388cb5c1c9477048125d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Mon, 26 Jan 2026 14:22:15 +0100 Subject: [PATCH 03/10] pr-feedback: simplify SGEP case --- clang/lib/CodeGen/CGExpr.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 7d441aebfb6c2..8ea9cd1f863d2 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4499,16 +4499,8 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, assert(isa<llvm::ArrayType>(Addr.getElementType()) && "Expected pointer to array"); - if (getLangOpts().HLSL && getLangOpts().EmitStructuredGEP) { - llvm::Value *Ptr = Addr.emitRawPointer(*this); - if (auto *C = dyn_cast<llvm::Constant>(Ptr)) - return Address(C, Addr.getElementType(), Addr.getAlignment(), - Addr.isKnownNonNull()); - return Address( - Builder.CreateStructuredGEP(NewTy, Addr.getBasePointer(), {}), - Addr.getElementType(), Addr.getAlignment(), Addr.isKnownNonNull()); - } - + if (getLangOpts().HLSL && getLangOpts().EmitStructuredGEP) + return Addr; Addr = Builder.CreateConstArrayGEP(Addr, 0, "arraydecay"); } From 7bed4c12c72d902a4d0116ad02ab69ba5feefc6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Mon, 26 Jan 2026 15:28:29 +0100 Subject: [PATCH 04/10] remove unused variable --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index e52da1b1d845a..013c7ce63e5e4 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -1109,8 +1109,6 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV, // Make sure the global variable is buffer resource handle llvm::Type *HandleTy = GV->getValueType(); assert(HandleTy->isTargetExtTy() && "unexpected type of the buffer global"); - llvm::Type *UnderlyingType = - cast<TargetExtType>(HandleTy)->getTypeParameter(0); llvm::Value *CreateHandle = Builder.CreateIntrinsic( /*ReturnType=*/HandleTy, IntrID, Args, nullptr, From ea52409e3738a53f38aa623a9a0d8211581bffd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Mon, 26 Jan 2026 16:26:26 +0100 Subject: [PATCH 05/10] pr-feedback --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 013c7ce63e5e4..1d7d0089087f4 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -1397,8 +1397,9 @@ std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr( Indices.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 0)); if (CGF.getLangOpts().EmitStructuredGEP) { - if (auto *AT = dyn_cast<llvm::ArrayType>(Addr.getElementType())) - LayoutTy = llvm::ArrayType::get(LayoutTy, AT->getNumElements()); + LayoutTy = llvm::ArrayType::get( + LayoutTy, + cast<llvm::ArrayType>(Addr.getElementType())->getNumElements()); auto *GEP = cast<StructuredGEPInst>(CGF.Builder.CreateStructuredGEP( LayoutTy, Addr.emitRawPointer(CGF), Indices, "cbufferidx")); Addr = From 32b5c4d26c165cdf03b02c09784b0ac47ddb7eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Mon, 26 Jan 2026 16:31:31 +0100 Subject: [PATCH 06/10] test regex --- clang/test/CodeGenHLSL/sgep/array_load.hlsl | 25 ++++++----------- clang/test/CodeGenHLSL/sgep/array_store.hlsl | 28 +++++++------------ .../test/CodeGenHLSL/sgep/object_method.hlsl | 8 +++--- 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/clang/test/CodeGenHLSL/sgep/array_load.hlsl b/clang/test/CodeGenHLSL/sgep/array_load.hlsl index 26efa7cef57dc..fa2feca1ae961 100644 --- a/clang/test/CodeGenHLSL/sgep/array_load.hlsl +++ b/clang/test/CodeGenHLSL/sgep/array_load.hlsl @@ -1,12 +1,11 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIR +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s void foo() { // CHECK: %array = alloca [3 x i32], align 4 uint array[3] = { 0, 1, 2 }; -// CHECK-DXIL: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x i32]) %array, i32 2) -// CHECK-SPIR: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x i32]) %array, i64 2) +// CHECK: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x i32]) %array, {{i32|i64}} 2) // CHECK: load i32, ptr %[[#PTR]], align 4 uint tmp = array[2]; } @@ -20,11 +19,8 @@ void bar() { // CHECK: %array = alloca [3 x %struct.S], align 1 S array[3] = { { 0, 1 }, { 2, 3 }, { 3, 4 } }; -// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i32 2) -// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i32 1) -// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i64 2) -// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i64 1) - +// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, {{i32|i64}} 2) +// CHECK: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], {{i32|i64}} 1) // CHECK: load i32, ptr %[[#B]], align 1 uint tmp = array[2].b; } @@ -39,14 +35,9 @@ void baz() { // CHECK: %array = alloca [2 x %struct.S2], align 1 S2 array[2] = { { 0, { 1, 2 }, 3 }, { 4, { 5, 6 }, 7 } }; -// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i32 1) -// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i32 1) -// CHECK-DXIL: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i32 0) - -// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i64 1) -// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i64 1) -// CHECK-SPIR: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i64 0) - +// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, {{i32|i64}} 1) +// CHECK: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], {{i32|i64}} 1) +// CHECK: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], {{i32|i64}} 0) // CHECK: load i32, ptr %[[#C]], align 1 uint tmp = array[1].b.a; } diff --git a/clang/test/CodeGenHLSL/sgep/array_store.hlsl b/clang/test/CodeGenHLSL/sgep/array_store.hlsl index a798bc47da50c..f08d2b1f5d258 100644 --- a/clang/test/CodeGenHLSL/sgep/array_store.hlsl +++ b/clang/test/CodeGenHLSL/sgep/array_store.hlsl @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIR +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-emit-sgep -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -fexperimental-emit-sgep -o - %s | FileCheck %s [shader("compute")] [numthreads(1,1,1)] @@ -7,8 +7,7 @@ void foo() { // CHECK: %array = alloca [10 x i32], align 4 uint array[10]; -// CHECK-DXIL: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %array, i32 2) -// CHECK-SPIR: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %array, i64 2) +// CHECK: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %array, {{i32|i64}} 2) // CHECK: store i32 10, ptr %[[#PTR]], align 4 array[2] = 10; } @@ -22,13 +21,10 @@ void bar() { // CHECK: %array = alloca [3 x %struct.S], align 1 S array[3] = { { 0, 1 }, { 2, 3 }, { 3, 4 } }; -// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i32 2) -// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i32 1) - -// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, i64 2) -// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], i64 1) - +// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, {{i32|i64}} 2) +// CHECK: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#A]], {{i32|i64}} 1) // CHECK: store i32 10, ptr %[[#B]], align 1 + array[2].b = 10; } @@ -42,14 +38,10 @@ void baz() { // CHECK: %array = alloca [2 x %struct.S2], align 1 S2 array[2] = { { 0, { 1, 2 }, 3 }, { 4, { 5, 6 }, 7 } }; -// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i32 1) -// CHECK-DXIL: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i32 1) -// CHECK-DXIL: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i32 0) - -// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, i64 1) -// CHECK-SPIR: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], i64 1) -// CHECK-SPIR: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], i64 0) - +// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, {{i32|i64}} 1) +// CHECK: %[[#B:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S2) %[[#A]], {{i32|i64}} 1) +// CHECK: %[[#C:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %[[#B]], {{i32|i64}} 0) // CHECK: store i32 10, ptr %[[#C]], align 1 + array[1].b.a = 10; } diff --git a/clang/test/CodeGenHLSL/sgep/object_method.hlsl b/clang/test/CodeGenHLSL/sgep/object_method.hlsl index f54f0c064bb31..fae271e26d0f9 100644 --- a/clang/test/CodeGenHLSL/sgep/object_method.hlsl +++ b/clang/test/CodeGenHLSL/sgep/object_method.hlsl @@ -24,7 +24,7 @@ void foo() { // CHECK-DXIL: define linkonce_odr hidden noundef i32 @_ZN1O3getEv(ptr noundef nonnull align 1 dereferenceable(4) %this) // CHECK-SPIR: define linkonce_odr hidden spir_func noundef i32 @_ZN1O3getEv(ptr noundef nonnull align 1 dereferenceable(4) %this) -// CHECK-DXIL: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.O) %this1, i32 0) -// CHECK-SPIR: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.O) %this1, i64 0) -// CHECK: %[[#B:]] = load i32, ptr %[[#A]], align 1 -// CHECK: ret i32 %[[#B]] + +// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.O) %this1, {{i32|i64}} 0) +// CHECK: %[[#B:]] = load i32, ptr %[[#A]], align 1 +// CHECK: ret i32 %[[#B]] From 7a5071a2fee54ec0bc04c8de40aabfe924a5651b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Wed, 28 Jan 2026 15:11:58 +0100 Subject: [PATCH 07/10] remove HLSL check --- clang/lib/CodeGen/CGExpr.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 8ea9cd1f863d2..a0245d5770510 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4499,7 +4499,7 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, assert(isa<llvm::ArrayType>(Addr.getElementType()) && "Expected pointer to array"); - if (getLangOpts().HLSL && getLangOpts().EmitStructuredGEP) + if (getLangOpts().EmitStructuredGEP) return Addr; Addr = Builder.CreateConstArrayGEP(Addr, 0, "arraydecay"); } @@ -4540,7 +4540,7 @@ static llvm::Value *emitArraySubscriptGEP(CodeGenFunction &CGF, bool signedIndices, SourceLocation loc, const llvm::Twine &name = "arrayidx") { - if (CGF.getLangOpts().HLSL && inbounds && CGF.getLangOpts().EmitStructuredGEP) + if (inbounds && CGF.getLangOpts().EmitStructuredGEP) return CGF.Builder.CreateStructuredGEP(elemType, ptr, indices); if (inbounds) { @@ -4559,8 +4559,7 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, bool signedIndices, SourceLocation loc, CharUnits align, const llvm::Twine &name = "arrayidx") { - if (CGF.getLangOpts().HLSL && arrayType && inbounds && - CGF.getLangOpts().EmitStructuredGEP) + if (arrayType && inbounds && CGF.getLangOpts().EmitStructuredGEP) return RawAddress(CGF.Builder.CreateStructuredGEP(arrayType, addr.emitRawPointer(CGF), indices.drop_front()), @@ -5541,7 +5540,7 @@ static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base, llvm::Type *StructType = CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMType(); - if (CGF.getLangOpts().HLSL && CGF.getLangOpts().EmitStructuredGEP) + if (CGF.getLangOpts().EmitStructuredGEP) return RawAddress( CGF.Builder.CreateStructuredGEP(StructType, base.emitRawPointer(CGF), {CGF.Builder.getSize(idx)}), From 527f0284a8f4f24736b71c4c3f21a6acdb9c7181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Wed, 28 Jan 2026 15:43:28 +0100 Subject: [PATCH 08/10] remove optional on arrayType --- clang/lib/CodeGen/CGExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index a0245d5770510..c1528129f42e1 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4559,7 +4559,7 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, bool signedIndices, SourceLocation loc, CharUnits align, const llvm::Twine &name = "arrayidx") { - if (arrayType && inbounds && CGF.getLangOpts().EmitStructuredGEP) + if (inbounds && CGF.getLangOpts().EmitStructuredGEP) return RawAddress(CGF.Builder.CreateStructuredGEP(arrayType, addr.emitRawPointer(CGF), indices.drop_front()), From fdb3c6a4b956b878f52a45fdc87bf4dc45cff6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Wed, 11 Feb 2026 13:07:38 +0100 Subject: [PATCH 09/10] remove early exit on decay-to-pointer --- clang/lib/CodeGen/CGExpr.cpp | 10 +++++++--- clang/lib/CodeGen/CGHLSLRuntime.cpp | 21 ++++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index c1528129f42e1..30026fb6061fe 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4499,9 +4499,13 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, assert(isa<llvm::ArrayType>(Addr.getElementType()) && "Expected pointer to array"); - if (getLangOpts().EmitStructuredGEP) - return Addr; - Addr = Builder.CreateConstArrayGEP(Addr, 0, "arraydecay"); + if (getLangOpts().EmitStructuredGEP) { + auto *SGEP = + Builder.CreateStructuredGEP(NewTy, Addr.emitRawPointer(*this), {}); + Addr = Address(SGEP, NewTy, Addr.getAlignment(), Addr.isKnownNonNull()); + } else { + Addr = Builder.CreateConstArrayGEP(Addr, 0, "arraydecay"); + } } // The result of this decay conversion points to an array element within the diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 1d7d0089087f4..aa39a4885a05d 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -1385,18 +1385,23 @@ std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr( LValueBaseInfo EltBaseInfo; TBAAAccessInfo EltTBAAInfo; - Address Addr = - CGF.EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo); - llvm::Value *Idx = EmitIdxAfterBase(/*Promote*/ true); // Index into the object as-if we have an array of the padded element type, // and then dereference the element itself to avoid reading padding that may // be past the end of the in-memory object. SmallVector<llvm::Value *, 2> Indices; + llvm::Value *Idx = EmitIdxAfterBase(/*Promote*/ true); Indices.push_back(Idx); Indices.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 0)); if (CGF.getLangOpts().EmitStructuredGEP) { + // The fact that we emit an array-to-pointer decay might be an oversight, + // but for now, we simply ignore it (see #179951). + const CastExpr *CE = cast<CastExpr>(E->getBase()); + assert(CE->getCastKind() == CastKind::CK_ArrayToPointerDecay); + + LValue LV = CGF.EmitLValue(CE->getSubExpr()); + Address Addr = LV.getAddress(); LayoutTy = llvm::ArrayType::get( LayoutTy, cast<llvm::ArrayType>(Addr.getElementType())->getNumElements()); @@ -1404,12 +1409,14 @@ std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr( LayoutTy, Addr.emitRawPointer(CGF), Indices, "cbufferidx")); Addr = Address(GEP, GEP->getResultElementType(), RowAlignedSize, KnownNonNull); - } else { - llvm::Value *GEP = CGF.Builder.CreateGEP(LayoutTy, Addr.emitRawPointer(CGF), - Indices, "cbufferidx"); - Addr = Address(GEP, Addr.getElementType(), RowAlignedSize, KnownNonNull); + return CGF.MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo); } + Address Addr = + CGF.EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo); + llvm::Value *GEP = CGF.Builder.CreateGEP(LayoutTy, Addr.emitRawPointer(CGF), + Indices, "cbufferidx"); + Addr = Address(GEP, Addr.getElementType(), RowAlignedSize, KnownNonNull); return CGF.MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo); } From 2a6a02d101c547325a5531d4a8ef9938cccb925f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <[email protected]> Date: Thu, 12 Feb 2026 09:42:42 +0100 Subject: [PATCH 10/10] add comment --- clang/lib/CodeGen/CGExpr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 30026fb6061fe..1f93a6a51d3e0 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4500,6 +4500,8 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, "Expected pointer to array"); if (getLangOpts().EmitStructuredGEP) { + // Array-to-pointer decay for an SGEP is a no-op as we don't do any + // logical indexing. See #179951 for some additional context. auto *SGEP = Builder.CreateStructuredGEP(NewTy, Addr.emitRawPointer(*this), {}); Addr = Address(SGEP, NewTy, Addr.getAlignment(), Addr.isKnownNonNull()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
