[llvm] c4eda0c - [HLSL][DirectX] Lower llvm.dx.load.input and llvm.dx.store.output to DXIL (#208871)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 11:54:16 PDT 2026
Author: Dan Brown
Date: 2026-08-07T11:54:10-07:00
New Revision: c4eda0c444131b8f0cba2f33a126fb1632a25b3e
URL: https://github.com/llvm/llvm-project/commit/c4eda0c444131b8f0cba2f33a126fb1632a25b3e
DIFF: https://github.com/llvm/llvm-project/commit/c4eda0c444131b8f0cba2f33a126fb1632a25b3e.diff
LOG: [HLSL][DirectX] Lower llvm.dx.load.input and llvm.dx.store.output to DXIL (#208871)
Addresses #189766.
Co-authored-by: Finn Plummer <mail at inbelic.dev>
Assisted-by: Claude Sonnet 4
---------
Co-authored-by: Finn Plummer <mail at inbelic.dev>
Added:
llvm/test/CodeGen/DirectX/LoadInput.ll
llvm/test/CodeGen/DirectX/StoreOutput.ll
Modified:
clang/lib/CodeGen/CGHLSLRuntime.cpp
clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl
clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl
clang/test/CodeGenHLSL/semantics/semantic.struct.output.hlsl
clang/test/CodeGenHLSL/sret_output.hlsl
llvm/include/llvm/IR/IntrinsicsDirectX.td
llvm/lib/Target/DirectX/DXIL.td
llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f86cb00c204f8..a24e63f0b2b93 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1220,12 +1220,8 @@ void CGHLSLRuntime::emitDXILUserSemanticStore(llvm::IRBuilder<> &B,
std::optional<unsigned> Index) {
// DXIL packing rules etc shall be handled here.
// FIXME: generate proper sigpoint, index, col, row values.
- SmallVector<Value *> Args{B.getInt32(4),
- B.getInt32(0),
- B.getInt32(0),
- B.getInt8(0),
- llvm::PoisonValue::get(B.getInt32Ty()),
- Source};
+ SmallVector<Value *> Args{B.getInt32(4), B.getInt32(0), B.getInt32(0),
+ B.getInt8(0), Source};
llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_store_output;
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
index b118ee395f7de..0dbf307074e89 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
@@ -11,7 +11,7 @@ float4 main(float4 p : SV_Position) : A {
// CHECK-DXIL: %SV_Position0 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
- // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, <4 x float> %[[#TMP]])
return p;
}
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl
index caab744c1fa98..aee4ee183bf9d 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl
@@ -12,7 +12,7 @@ float4 main(float4 p : SV_Position) : SV_Position {
// CHECK-DXIL: %SV_Position0 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
- // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, <4 x float> %[[#TMP]])
return p;
}
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
index 1ab4df4f61bd7..2c7929a9e821d 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
@@ -9,7 +9,7 @@ float4 main(float4 p : SV_Position) : SV_Target {
// CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 4
// CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
- // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, <4 x float> %[[#TMP]])
return p;
}
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
index 18942699aaa2c..4e70ea037952c 100644
--- a/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/semantic.array.output.hlsl
@@ -19,10 +19,10 @@ S0 main1(float4 input : A) : B {
// CHECK: %[[#ST:]] = load %struct.S0, ptr %[[#ARG]]
// CHECK: %[[#TMP:]] = extractvalue %struct.S0 %[[#ST]], 0
// CHECK-SPIRV: store [2 x <4 x float>] %[[#TMP]], ptr addrspace(8) @B0, align 4
- // CHECK-DXIL: call void @llvm.dx.store.output.a2v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, [2 x <4 x float>] %[[#TMP]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.a2v4f32(i32 4, i32 0, i32 0, i8 0, [2 x <4 x float>] %[[#TMP]])
// CHECK: %[[#TMP:]] = extractvalue %struct.S0 %[[#ST]], 1
// CHECK-SPIRV: store <4 x float> %[[#TMP]], ptr addrspace(8) @B2, align 4
- // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, <4 x float> %[[#TMP]])
S0 output;
output.position[0] = input;
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
index 8bd8998cdceb5..57b5b777f4736 100644
--- a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
@@ -21,7 +21,7 @@ Output main(float4 p : SV_Position) {
// CHECK: %[[#FIELD:]] = extractvalue %struct.Output %[[#TMP]], 0
// CHECK-SPIRV: store <4 x float> %[[#FIELD]], ptr addrspace(8) @SV_Target0, align 4
- // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#FIELD]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, <4 x float> %[[#FIELD]])
Output o;
o.field = p;
return o;
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl
index 625d5dcb02ec4..96506c5bdeaa0 100644
--- a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl
@@ -9,7 +9,7 @@
// CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 4
// CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
- // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, <4 x float> %[[#TMP]])
return p;
}
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.struct.output.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.struct.output.hlsl
index 0f2444d21724a..b1b1d2159ebac 100644
--- a/clang/test/CodeGenHLSL/semantics/semantic.struct.output.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/semantic.struct.output.hlsl
@@ -43,10 +43,10 @@ Output main(Input input) {
// CHECK: %[[#TMP:]] = load %struct.Output, ptr %[[#OUTPUT]], align 4
// CHECK: %[[#VAL:]] = extractvalue %struct.Output %[[#TMP]], 0
// CHECK-SPIRV: store float %[[#VAL]], ptr addrspace(8) @A4, align 4
-// CHECK-DXIL: call void @llvm.dx.store.output.f32(i32 4, i32 0, i32 0, i8 0, i32 poison, float %[[#VAL]])
+// CHECK-DXIL: call void @llvm.dx.store.output.f32(i32 4, i32 0, i32 0, i8 0, float %[[#VAL]])
// CHECK: %[[#VAL:]] = extractvalue %struct.Output %[[#TMP]], 1
// CHECK-SPIRV: store float %[[#VAL]], ptr addrspace(8) @A2, align 4
-// CHECK-DXIL: call void @llvm.dx.store.output.f32(i32 4, i32 0, i32 0, i8 0, i32 poison, float %[[#VAL]])
+// CHECK-DXIL: call void @llvm.dx.store.output.f32(i32 4, i32 0, i32 0, i8 0, float %[[#VAL]])
// CHECK-SPIRV-DAG: ![[#METADATA_0]] = !{![[#METADATA_1:]]}
// CHECK-SPIRV-DAG: ![[#METADATA_2]] = !{![[#METADATA_3:]]}
diff --git a/clang/test/CodeGenHLSL/sret_output.hlsl b/clang/test/CodeGenHLSL/sret_output.hlsl
index e1aa0973fe5db..e2f806a2646f6 100644
--- a/clang/test/CodeGenHLSL/sret_output.hlsl
+++ b/clang/test/CodeGenHLSL/sret_output.hlsl
@@ -25,7 +25,7 @@ S vs_main() {
// CHECK-VK: call spir_func void @_Z7vs_mainv(ptr %[[#alloca]])
// CHECK: %[[#a:]] = load %struct.S, ptr %[[#alloca]], align 4
// CHECK: %[[#b:]] = extractvalue %struct.S %[[#a]], 0
-// CHECK-DX: call void @llvm.dx.store.output.f32(i32 4, i32 0, i32 0, i8 0, i32 poison, float %[[#b]])
+// CHECK-DX: call void @llvm.dx.store.output.f32(i32 4, i32 0, i32 0, i8 0, float %[[#b]])
// CHECK-VK: store float %3, ptr addrspace(8) @A4, align 4
// CHECK: ret void
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 090656ffb36c8..62489b58a0300 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -323,14 +323,18 @@ def int_dx_group_memory_barrier_with_group_sync
: DefaultAttrsIntrinsic<[], [], [IntrConvergent]>;
def int_dx_load_input
- : DefaultAttrsIntrinsic<[llvm_any_ty],
- [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i8_ty,
- llvm_i32_ty],
- [IntrConvergent]>;
+ : DefaultAttrsIntrinsic<
+ [llvm_any_ty],
+ [llvm_i32_ty /*sigpointId*/, llvm_i32_ty /*sigElementId*/,
+ llvm_i32_ty /*rowIndex*/, llvm_i8_ty /*colIndex*/,
+ llvm_i32_ty /*gsVertexOrPrimIndex*/],
+ [IntrConvergent]>;
def int_dx_store_output
- : DefaultAttrsIntrinsic<[],
- [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i8_ty,
- llvm_i32_ty, llvm_any_ty],
- [IntrConvergent]>;
+ : DefaultAttrsIntrinsic<
+ [],
+ [llvm_i32_ty /*sigpointId*/, llvm_i32_ty /*sigElementId*/,
+ llvm_i32_ty /*rowIndex*/, llvm_i8_ty /*colIndex*/,
+ llvm_any_ty /*value*/],
+ [IntrConvergent]>;
}
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 3d978c207f104..842c1b48efecf 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -412,6 +412,31 @@ class DXILOp<int opcode, DXILOpClass opclass> {
//
// This are sorted by ascending value of the DXIL Opcodes
+def LoadInput : DXILOp<4, loadInput> {
+ let Doc = "Loads a scalar value from a shader input register component.";
+ let intrinsics = [IntrinSelect<int_dx_load_input,
+ [IntrinArgIndex<1>, IntrinArgIndex<2>, IntrinArgIndex<3>,
+ IntrinArgIndex<4>]>];
+ // inputSigId, rowIndex, colIndex, gsVertexOrPrimIndex
+ let arguments = [Int32Ty, Int32Ty, Int8Ty, Int32Ty];
+ let result = OverloadTy;
+ let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy, Int16Ty, Int32Ty]>];
+ let stages = [Stages<DXIL1_0, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
+}
+
+def StoreOutput : DXILOp<5, storeOutput> {
+ let Doc = "Stores a scalar value to a shader output register component.";
+ let intrinsics = [IntrinSelect<int_dx_store_output,
+ [IntrinArgIndex<1>, IntrinArgIndex<2>, IntrinArgIndex<3>,
+ IntrinArgIndex<4>]>];
+ // outputSigId, rowIndex, colIndex, value
+ let arguments = [Int32Ty, Int32Ty, Int8Ty, OverloadTy];
+ let result = VoidTy;
+ let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy, Int16Ty, Int32Ty]>];
+ let stages = [Stages<DXIL1_0, [all_stages]>];
+}
+
def Abs : DXILOp<6, unary> {
let Doc = "Returns the absolute value of the input.";
let intrinsics = [IntrinSelect<int_fabs>];
diff --git a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
index a251288a6ae42..c5b8b4efee956 100644
--- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
+++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
@@ -234,6 +234,8 @@ static bool isIntrinsicExpansion(Function &F) {
case Intrinsic::matrix_transpose:
case Intrinsic::umul_with_overflow:
case Intrinsic::smul_with_overflow:
+ case Intrinsic::dx_load_input:
+ case Intrinsic::dx_store_output:
return true;
case Intrinsic::dx_resource_load_rawbuffer:
return resourceAccessNeeds64BitExpansion(
@@ -1212,6 +1214,84 @@ static Value *expandMatrixTranspose(CallInst *Orig) {
return Builder.CreateShuffleVector(Mat, Mask);
}
+// Scalarize a vector int_dx_store_output call into per-component scalar calls.
+// The DXIL StoreOutput op is per-component; vector intrinsics are split here
+// so that DXILOpLowering sees only scalar variants.
+static bool expandStoreOutput(CallInst *Orig) {
+ auto *VT = dyn_cast<FixedVectorType>(Orig->getArgOperand(4)->getType());
+ if (!VT)
+ return false; // already scalar, nothing to expand
+
+ IRBuilder<> Builder(Orig);
+ Module *M = Orig->getModule();
+ Type *Int8Ty = Builder.getInt8Ty();
+ Type *Int32Ty = Builder.getInt32Ty();
+ Type *ScalarTy = VT->getElementType();
+ unsigned NumElems = VT->getNumElements();
+
+ Value *SigpointId = Orig->getArgOperand(0);
+ Value *SigElementId = Orig->getArgOperand(1);
+ Value *RowIndex = Orig->getArgOperand(2);
+ Value *StartCol = Orig->getArgOperand(3); // i8
+ Value *Data = Orig->getArgOperand(4);
+ Value *StartColI32 = Builder.CreateZExt(StartCol, Int32Ty);
+
+ Function *ScalarFn = Intrinsic::getOrInsertDeclaration(
+ M, Intrinsic::dx_store_output, {ScalarTy});
+
+ for (unsigned I = 0; I < NumElems; ++I) {
+ Value *Scalar =
+ Builder.CreateExtractElement(Data, ConstantInt::get(Int32Ty, I));
+ Value *ColIdx =
+ Builder.CreateAdd(StartColI32, ConstantInt::get(Int32Ty, I));
+ Value *ColI8 = Builder.CreateTrunc(ColIdx, Int8Ty);
+ Builder.CreateCall(ScalarFn,
+ {SigpointId, SigElementId, RowIndex, ColI8, Scalar});
+ }
+
+ Orig->eraseFromParent();
+ return true;
+}
+
+// Scalarize a vector int_dx_load_input call into per-component scalar calls
+// and reassemble the vector. The DXIL LoadInput op is per-component.
+static Value *expandLoadInput(CallInst *Orig) {
+ auto *VT = dyn_cast<FixedVectorType>(Orig->getType());
+ if (!VT)
+ return nullptr; // already scalar, nothing to expand
+
+ IRBuilder<> Builder(Orig);
+ Module *M = Orig->getModule();
+ Type *Int8Ty = Builder.getInt8Ty();
+ Type *Int32Ty = Builder.getInt32Ty();
+ Type *ScalarTy = VT->getElementType();
+ unsigned NumElems = VT->getNumElements();
+
+ Value *SigpointId = Orig->getArgOperand(0);
+ Value *SigElementId = Orig->getArgOperand(1);
+ Value *RowIndex = Orig->getArgOperand(2);
+ Value *StartCol = Orig->getArgOperand(3); // i8
+ Value *GsVertexOrPrimIndex = Orig->getArgOperand(4);
+ Value *StartColI32 = Builder.CreateZExt(StartCol, Int32Ty);
+
+ Function *ScalarFn = Intrinsic::getOrInsertDeclaration(
+ M, Intrinsic::dx_load_input, {ScalarTy});
+
+ Value *Vec = PoisonValue::get(VT);
+ for (unsigned I = 0; I < NumElems; ++I) {
+ Value *ColIdx =
+ Builder.CreateAdd(StartColI32, ConstantInt::get(Int32Ty, I));
+ Value *ColI8 = Builder.CreateTrunc(ColIdx, Int8Ty);
+ Value *Scalar =
+ Builder.CreateCall(ScalarFn, {SigpointId, SigElementId, RowIndex, ColI8,
+ GsVertexOrPrimIndex});
+ Vec =
+ Builder.CreateInsertElement(Vec, Scalar, ConstantInt::get(Int32Ty, I));
+ }
+
+ return Vec;
+}
+
static bool expandIntrinsic(Function &F, CallInst *Orig) {
Value *Result = nullptr;
Intrinsic::ID IntrinsicId = F.getIntrinsicID();
@@ -1287,6 +1367,13 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
case Intrinsic::dx_radians:
Result = expandRadiansIntrinsic(Orig);
break;
+ case Intrinsic::dx_load_input:
+ Result = expandLoadInput(Orig);
+ break;
+ case Intrinsic::dx_store_output:
+ if (expandStoreOutput(Orig))
+ return true;
+ break;
case Intrinsic::dx_resource_load_rawbuffer:
if (expandBufferLoadIntrinsic(Orig, /*IsRaw*/ true))
return true;
diff --git a/llvm/test/CodeGen/DirectX/LoadInput.ll b/llvm/test/CodeGen/DirectX/LoadInput.ll
new file mode 100644
index 0000000000000..b29167d2dfb27
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/LoadInput.ll
@@ -0,0 +1,51 @@
+; RUN: opt -S -dxil-intrinsic-expansion -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.0-pixel"
+
+; Scalar float load: one LoadInput call, result forwarded directly.
+; CHECK-LABEL: define float @load_scalar_f32
+define float @load_scalar_f32() {
+ ; CHECK: [[V:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 0, i32 1, i8 2, i32 0)
+ ; CHECK-NEXT: ret float [[V]]
+ ; CHECK-NOT: llvm.dx.load.input
+ %v = call float @llvm.dx.load.input.f32(i32 99, i32 0, i32 1, i8 2, i32 0)
+ ret float %v
+}
+
+; Vector float4 load: four per-component LoadInput calls reassembled into a vector.
+; CHECK-LABEL: define <4 x float> @load_v4f32
+define <4 x float> @load_v4f32() {
+ ; CHECK: [[S0:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 1, i32 0, i8 0, i32 0)
+ ; CHECK-NEXT: insertelement <4 x float> {{.*}}, float [[S0]], i32 0
+ ; CHECK: [[S1:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 1, i32 0, i8 1, i32 0)
+ ; CHECK-NEXT: insertelement {{.*}}, float [[S1]], i32 1
+ ; CHECK: [[S2:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 1, i32 0, i8 2, i32 0)
+ ; CHECK-NEXT: insertelement {{.*}}, float [[S2]], i32 2
+ ; CHECK: [[S3:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 1, i32 0, i8 3, i32 0)
+ ; CHECK-NEXT: insertelement {{.*}}, float [[S3]], i32 3
+ ; CHECK-NOT: llvm.dx.load.input
+ %v = call <4 x float> @llvm.dx.load.input.v4f32(i32 99, i32 1, i32 0, i8 0, i32 0)
+ ret <4 x float> %v
+}
+
+; Vector float2 load with non-zero start column: col indices must be 2 and 3.
+; CHECK-LABEL: define <2 x float> @load_v2f32_col2
+define <2 x float> @load_v2f32_col2() {
+ ; CHECK: [[S0:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 2, i32 0, i8 2, i32 0)
+ ; CHECK-NEXT: insertelement <2 x float> {{.*}}, float [[S0]], i32 0
+ ; CHECK: [[S1:%.*]] = call float @dx.op.loadInput.f32(i32 4, i32 2, i32 0, i8 3, i32 0)
+ ; CHECK-NEXT: insertelement {{.*}}, float [[S1]], i32 1
+ ; CHECK-NOT: llvm.dx.load.input
+ %v = call <2 x float> @llvm.dx.load.input.v2f32(i32 99, i32 2, i32 0, i8 2, i32 0)
+ ret <2 x float> %v
+}
+
+; Scalar int load: one LoadInput call, result forwarded directly.
+; CHECK-LABEL: define i32 @load_scalar_i32
+define i32 @load_scalar_i32() {
+ ; CHECK: [[V:%.*]] = call i32 @dx.op.loadInput.i32(i32 4, i32 2, i32 0, i8 0, i32 0)
+ ; CHECK-NEXT: ret i32 [[V]]
+ ; CHECK-NOT: llvm.dx.load.input
+ %v = call i32 @llvm.dx.load.input.i32(i32 99, i32 2, i32 0, i8 0, i32 0)
+ ret i32 %v
+}
diff --git a/llvm/test/CodeGen/DirectX/StoreOutput.ll b/llvm/test/CodeGen/DirectX/StoreOutput.ll
new file mode 100644
index 0000000000000..04970a1488710
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/StoreOutput.ll
@@ -0,0 +1,49 @@
+; RUN: opt -S -dxil-intrinsic-expansion -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.0-pixel"
+
+; Scalar float store: one StoreOutput call, no residual intrinsic.
+; CHECK-LABEL: define void @store_scalar_f32
+define void @store_scalar_f32(float %val) {
+ ; CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 1, i8 2, float %val)
+ ; CHECK-NOT: llvm.dx.store.output
+ call void @llvm.dx.store.output.f32(i32 99, i32 0, i32 1, i8 2, float %val)
+ ret void
+}
+
+; Vector float4 store: four per-component StoreOutput calls, col indices 0..3.
+; CHECK-LABEL: define void @store_v4f32
+define void @store_v4f32(<4 x float> %val) {
+ ; CHECK: [[E0:%.*]] = extractelement <4 x float> %val, i32 0
+ ; CHECK-NEXT: call void @dx.op.storeOutput.f32(i32 5, i32 1, i32 0, i8 0, float [[E0]])
+ ; CHECK-NEXT: [[E1:%.*]] = extractelement <4 x float> %val, i32 1
+ ; CHECK-NEXT: call void @dx.op.storeOutput.f32(i32 5, i32 1, i32 0, i8 1, float [[E1]])
+ ; CHECK-NEXT: [[E2:%.*]] = extractelement <4 x float> %val, i32 2
+ ; CHECK-NEXT: call void @dx.op.storeOutput.f32(i32 5, i32 1, i32 0, i8 2, float [[E2]])
+ ; CHECK-NEXT: [[E3:%.*]] = extractelement <4 x float> %val, i32 3
+ ; CHECK-NEXT: call void @dx.op.storeOutput.f32(i32 5, i32 1, i32 0, i8 3, float [[E3]])
+ ; CHECK-NOT: llvm.dx.store.output
+ call void @llvm.dx.store.output.v4f32(i32 99, i32 1, i32 0, i8 0, <4 x float> %val)
+ ret void
+}
+
+; Vector float2 store with non-zero start column: col indices must be 2 and 3.
+; CHECK-LABEL: define void @store_v2f32_col2
+define void @store_v2f32_col2(<2 x float> %val) {
+ ; CHECK: [[E0:%.*]] = extractelement <2 x float> %val, i32 0
+ ; CHECK-NEXT: call void @dx.op.storeOutput.f32(i32 5, i32 2, i32 0, i8 2, float [[E0]])
+ ; CHECK-NEXT: [[E1:%.*]] = extractelement <2 x float> %val, i32 1
+ ; CHECK-NEXT: call void @dx.op.storeOutput.f32(i32 5, i32 2, i32 0, i8 3, float [[E1]])
+ ; CHECK-NOT: llvm.dx.store.output
+ call void @llvm.dx.store.output.v2f32(i32 99, i32 2, i32 0, i8 2, <2 x float> %val)
+ ret void
+}
+
+; Scalar int store: one StoreOutput call, no residual intrinsic.
+; CHECK-LABEL: define void @store_scalar_i32
+define void @store_scalar_i32(i32 %val) {
+ ; CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 2, i32 0, i8 0, i32 %val)
+ ; CHECK-NOT: llvm.dx.store.output
+ call void @llvm.dx.store.output.i32(i32 99, i32 2, i32 0, i8 0, i32 %val)
+ ret void
+}
More information about the llvm-commits
mailing list