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 <[email protected]> Assisted-by: Claude Sonnet 4 --------- Co-authored-by: Finn Plummer <[email protected]> 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 +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
