[clang] 18c3c86 - [HLSL][Clang] Start emitting @llvm.structured.alloca (#190157)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 20 03:09:42 PDT 2026
Author: Nathan Gauër
Date: 2026-04-20T10:09:36Z
New Revision: 18c3c86cd30fef80e30382c1eda54e05f530a3ca
URL: https://github.com/llvm/llvm-project/commit/18c3c86cd30fef80e30382c1eda54e05f530a3ca
DIFF: https://github.com/llvm/llvm-project/commit/18c3c86cd30fef80e30382c1eda54e05f530a3ca.diff
LOG: [HLSL][Clang] Start emitting @llvm.structured.alloca (#190157)
Allowing some pattterns in the FE to emit this new instruction to emit
logical pointers. Renamed the experimental-emit-sgep flag to reflect the
broader logic it gates.
This also updates the few frontend tests to reflect the newly emitted
alloca.
Next step is to handle the Mem2Reg/Reg2Mem.
Added:
Modified:
clang/include/clang/Basic/LangOptions.def
clang/include/clang/Options/Options.td
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CGHLSLRuntime.cpp
clang/test/CodeGenHLSL/sgep/array_load.hlsl
clang/test/CodeGenHLSL/sgep/array_store.hlsl
clang/test/CodeGenHLSL/sgep/load_global.hlsl
clang/test/CodeGenHLSL/sgep/object_method.hlsl
llvm/include/llvm/IR/IRBuilder.h
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 6bba142aaf428..596bce9e897f7 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 intrinsics instead of GEP instructions")
+LANGOPT(EmitLogicalPointer, 1, 0, NotCompatible, "Allow emitting structured GEP/alloca intrinsics instead of normal GEP/alloca instructions.")
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 cbdf0e7cf3e62..46e97256a9e77 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -10097,12 +10097,12 @@ 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">,
+def fexperimental_logical_pointer
+ : Flag<["-"], "fexperimental-logical-pointer">,
Visibility<[CC1Option, DXCOption]>,
- HelpText<"Emit structured GEP intrinsic instead of GEP instructions "
- "(experimental).">,
- MarshallingInfoFlag<LangOpts<"EmitStructuredGEP">>;
+ HelpText<"Allow emitting structured GEP/alloca intrinsics instead of "
+ "normal GEP/alloca instructions (experimental).">,
+ MarshallingInfoFlag<LangOpts<"EmitLogicalPointer">>;
def no_wasm_opt : Flag<["--"], "no-wasm-opt">,
Group<m_Group>,
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ab08f3a238ef6..64adc4770808a 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -107,7 +107,12 @@ RawAddress
CodeGenFunction::CreateTempAllocaWithoutCast(llvm::Type *Ty, CharUnits Align,
const Twine &Name,
llvm::Value *ArraySize) {
- auto Alloca = CreateTempAlloca(Ty, Name, ArraySize);
+ if (getLangOpts().EmitLogicalPointer) {
+ auto Alloca = Builder.CreateStructuredAlloca(Ty, Name);
+ return RawAddress(Alloca, Ty, Align, KnownNonNull);
+ }
+
+ auto *Alloca = CreateTempAlloca(Ty, Name, ArraySize);
Alloca->setAlignment(Align.getAsAlign());
return RawAddress(Alloca, Ty, Align, KnownNonNull);
}
@@ -4602,7 +4607,7 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E,
assert(isa<llvm::ArrayType>(Addr.getElementType()) &&
"Expected pointer to array");
- if (getLangOpts().EmitStructuredGEP) {
+ if (getLangOpts().EmitLogicalPointer) {
// 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 =
@@ -4649,7 +4654,7 @@ static llvm::Value *emitArraySubscriptGEP(CodeGenFunction &CGF,
bool signedIndices,
SourceLocation loc,
const llvm::Twine &name = "arrayidx") {
- if (inbounds && CGF.getLangOpts().EmitStructuredGEP)
+ if (inbounds && CGF.getLangOpts().EmitLogicalPointer)
return CGF.Builder.CreateStructuredGEP(elemType, ptr, indices);
if (inbounds) {
@@ -4668,7 +4673,7 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
bool signedIndices, SourceLocation loc,
CharUnits align,
const llvm::Twine &name = "arrayidx") {
- if (inbounds && CGF.getLangOpts().EmitStructuredGEP)
+ if (inbounds && CGF.getLangOpts().EmitLogicalPointer)
return RawAddress(CGF.Builder.CreateStructuredGEP(arrayType,
addr.emitRawPointer(CGF),
indices.drop_front()),
@@ -5661,7 +5666,7 @@ static Address emitRawAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
llvm::Type *StructType =
CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMType();
- if (CGF.getLangOpts().EmitStructuredGEP)
+ if (CGF.getLangOpts().EmitLogicalPointer)
return RawAddress(
CGF.Builder.CreateStructuredGEP(StructType, base.emitRawPointer(CGF),
{CGF.Builder.getSize(idx)}),
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 6182663111f5a..1e25172d18890 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -963,7 +963,10 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
if (Param.hasStructRetAttr()) {
SRetOffset = 1;
llvm::Type *VarType = Param.getParamStructRetType();
- llvm::Value *Var = B.CreateAlloca(VarType);
+ llvm::Value *Var =
+ CGM.getLangOpts().EmitLogicalPointer
+ ? cast<Instruction>(B.CreateStructuredAlloca(VarType))
+ : cast<Instruction>(B.CreateAlloca(VarType));
OutputSemantic.push_back(std::make_pair(Var, VarType));
Args.push_back(Var);
continue;
@@ -986,7 +989,11 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
if (!SemanticValue)
return;
if (Param.hasByValAttr()) {
- llvm::Value *Var = B.CreateAlloca(Param.getParamByValType());
+ llvm::Value *Var =
+ CGM.getLangOpts().EmitLogicalPointer
+ ? cast<Instruction>(
+ B.CreateStructuredAlloca(Param.getParamByValType()))
+ : cast<Instruction>(B.CreateAlloca(Param.getParamByValType()));
B.CreateStore(SemanticValue, Var);
SemanticValue = Var;
}
@@ -1431,7 +1438,7 @@ std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr(
Indices.push_back(Idx);
Indices.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 0));
- if (CGF.getLangOpts().EmitStructuredGEP) {
+ if (CGF.getLangOpts().EmitLogicalPointer) {
// 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());
@@ -1646,7 +1653,7 @@ LValue CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF,
CharUnits Align = CharUnits::fromQuantity(
CGF.CGM.getDataLayout().getABITypeAlign(FieldLLVMTy));
- Value *Ptr = CGF.getLangOpts().EmitStructuredGEP
+ Value *Ptr = CGF.getLangOpts().EmitLogicalPointer
? CGF.Builder.CreateStructuredGEP(
LayoutTy, Base.getPointer(CGF),
llvm::ConstantInt::get(CGM.IntTy, FieldIdx))
diff --git a/clang/test/CodeGenHLSL/sgep/array_load.hlsl b/clang/test/CodeGenHLSL/sgep/array_load.hlsl
index fa2feca1ae961..db3d5a1580023 100644
--- a/clang/test/CodeGenHLSL/sgep/array_load.hlsl
+++ b/clang/test/CodeGenHLSL/sgep/array_load.hlsl
@@ -1,8 +1,8 @@
-// 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
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-logical-pointer -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-logical-pointer -o - %s | FileCheck %s
void foo() {
-// CHECK: %array = alloca [3 x i32], align 4
+// CHECK: %array = call elementtype([3 x i32]) ptr @llvm.structured.alloca.p0()
uint array[3] = { 0, 1, 2 };
// CHECK: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x i32]) %array, {{i32|i64}} 2)
@@ -16,7 +16,7 @@ struct S {
};
void bar() {
-// CHECK: %array = alloca [3 x %struct.S], align 1
+// CHECK: %array = call elementtype([3 x %struct.S]) ptr @llvm.structured.alloca.p0()
S array[3] = { { 0, 1 }, { 2, 3 }, { 3, 4 } };
// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, {{i32|i64}} 2)
@@ -32,7 +32,7 @@ struct S2 {
};
void baz() {
-// CHECK: %array = alloca [2 x %struct.S2], align 1
+// CHECK: %array = call elementtype([2 x %struct.S2]) ptr @llvm.structured.alloca.p0()
S2 array[2] = { { 0, { 1, 2 }, 3 }, { 4, { 5, 6 }, 7 } };
// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, {{i32|i64}} 1)
diff --git a/clang/test/CodeGenHLSL/sgep/array_store.hlsl b/clang/test/CodeGenHLSL/sgep/array_store.hlsl
index f08d2b1f5d258..886bc49c0019e 100644
--- a/clang/test/CodeGenHLSL/sgep/array_store.hlsl
+++ b/clang/test/CodeGenHLSL/sgep/array_store.hlsl
@@ -1,10 +1,10 @@
-// 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
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-logical-pointer -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-logical-pointer -o - %s | FileCheck %s
[shader("compute")]
[numthreads(1,1,1)]
void foo() {
-// CHECK: %array = alloca [10 x i32], align 4
+// CHECK: %array = call elementtype([10 x i32]) ptr @llvm.structured.alloca.p0()
uint array[10];
// CHECK: %[[#PTR:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %array, {{i32|i64}} 2)
@@ -18,7 +18,7 @@ struct S {
};
void bar() {
-// CHECK: %array = alloca [3 x %struct.S], align 1
+// CHECK: %array = call elementtype([3 x %struct.S]) ptr @llvm.structured.alloca.p0()
S array[3] = { { 0, 1 }, { 2, 3 }, { 3, 4 } };
// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([3 x %struct.S]) %array, {{i32|i64}} 2)
@@ -35,7 +35,7 @@ struct S2 {
};
void baz() {
-// CHECK: %array = alloca [2 x %struct.S2], align 1
+// CHECK: %array = call elementtype([2 x %struct.S2]) ptr @llvm.structured.alloca.p0()
S2 array[2] = { { 0, { 1, 2 }, 3 }, { 4, { 5, 6 }, 7 } };
// CHECK: %[[#A:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x %struct.S2]) %array, {{i32|i64}} 1)
diff --git a/clang/test/CodeGenHLSL/sgep/load_global.hlsl b/clang/test/CodeGenHLSL/sgep/load_global.hlsl
index 18e57e04e64fc..b35583591fddf 100644
--- a/clang/test/CodeGenHLSL/sgep/load_global.hlsl
+++ b/clang/test/CodeGenHLSL/sgep/load_global.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-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
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -fexperimental-logical-pointer -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-logical-pointer -o - %s | FileCheck %s --check-prefixes=CHECK-SPIR
struct S {
uint a;
diff --git a/clang/test/CodeGenHLSL/sgep/object_method.hlsl b/clang/test/CodeGenHLSL/sgep/object_method.hlsl
index fae271e26d0f9..34b6c4a44ed65 100644
--- a/clang/test/CodeGenHLSL/sgep/object_method.hlsl
+++ b/clang/test/CodeGenHLSL/sgep/object_method.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-logical-pointer -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-logical-pointer -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIR
struct O {
int value = 0;
@@ -14,7 +14,7 @@ struct O {
void foo() {
O o;
-// CHECK: %o = alloca %struct.O, align 1
+// CHECK: %o = call elementtype(%struct.O) ptr @llvm.structured.alloca.p0()
// 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();
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index bdec42516b8cd..05f744ad71246 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -1873,6 +1873,16 @@ class IRBuilderBase {
return Insert(new AllocaInst(Ty, AddrSpace, ArraySize, AllocaAlign), Name);
}
+ CallInst *CreateStructuredAlloca(Type *BaseType, const Twine &Name = "") {
+ const DataLayout &DL = BB->getDataLayout();
+ PointerType *PtrTy = DL.getAllocaPtrType(Context);
+ CallInst *Output =
+ CreateIntrinsic(Intrinsic::structured_alloca, {PtrTy}, {}, {}, Name);
+ Output->addRetAttr(
+ Attribute::get(getContext(), Attribute::ElementType, BaseType));
+ return Output;
+ }
+
/// Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of
/// converting the string to 'bool' for the isVolatile parameter.
LoadInst *CreateLoad(Type *Ty, Value *Ptr, const char *Name) {
More information about the cfe-commits
mailing list