Author: Deric C.
Date: 2026-08-12T15:46:51-07:00
New Revision: 5721918aff27f9af61a0e4cf8e0ff0c333710cd4

URL: 
https://github.com/llvm/llvm-project/commit/5721918aff27f9af61a0e4cf8e0ff0c333710cd4
DIFF: 
https://github.com/llvm/llvm-project/commit/5721918aff27f9af61a0e4cf8e0ff0c333710cd4.diff

LOG: [HLSL][DirectX] Implement lowering of texture stores (#212364)

Fixes https://github.com/llvm/llvm-project/issues/194930

This PR implements the DirectX lowering of texture stores via the
subscript operator (`.operator[]`).

Also adds missing frontend tests exercising stores to textures via the
subscript operator.

Also fixes an issue with `lowerTextureLoad` incorrectly supplying a mip
level to texture loads on UAV textures (except multisampled UAV
textures) which caused validation errors.

An UndefValue is used in the lowering because DXIL is based on an older
version of LLVM that requires the use of undef.

Assisted by: Claude Opus 5

Added: 
    clang/test/CodeGenHLSL/resources/RWTextures-Subscript.hlsl
    clang/test/SemaHLSL/Resources/RWTextures-Subscript.hlsl
    llvm/test/CodeGen/DirectX/ResourceAccess/store_texture.ll
    llvm/test/CodeGen/DirectX/TextureStore.ll

Modified: 
    clang/test/SemaHLSL/Resources/Textures-Subscript.hlsl
    llvm/docs/DirectX/DXILResources.rst
    llvm/include/llvm/IR/IntrinsicsDirectX.td
    llvm/lib/Target/DirectX/DXIL.td
    llvm/lib/Target/DirectX/DXILOpLowering.cpp
    llvm/lib/Target/DirectX/DXILResourceAccess.cpp
    llvm/test/CodeGen/DirectX/TextureLoad.ll

Removed: 
    


################################################################################
diff  --git a/clang/test/CodeGenHLSL/resources/RWTextures-Subscript.hlsl 
b/clang/test/CodeGenHLSL/resources/RWTextures-Subscript.hlsl
new file mode 100644
index 0000000000000..7562bedadba02
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/RWTextures-Subscript.hlsl
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -Wno-sign-conversion 
-DTEXTURE=RWTexture2D -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s 
-DTEXTURE=RWTexture2D -DCOORD_DIM=2 --check-prefixes=CHECK,DXIL -DDXIL_TY=2
+// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -Wno-sign-conversion 
-DTEXTURE=RWTexture2D -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s 
-DTEXTURE=RWTexture2D -DCOORD_DIM=2 --check-prefixes=CHECK,SPIRV -DARRAYED=0
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -Wno-sign-conversion 
-DTEXTURE=RWTexture2DArray -DCOORD_TYPE=uint3 -o - %s | llvm-cxxfilt | 
FileCheck %s -DTEXTURE=RWTexture2DArray -DCOORD_DIM=3 
--check-prefixes=CHECK,DXIL -DDXIL_TY=7
+// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -Wno-sign-conversion 
-DTEXTURE=RWTexture2DArray -DCOORD_TYPE=uint3 -o - %s | llvm-cxxfilt | 
FileCheck %s -DTEXTURE=RWTexture2DArray -DCOORD_DIM=3 
--check-prefixes=CHECK,SPIRV -DARRAYED=1
+
+// `Tex[coord] = value` on a writable texture lowers to a `store` through the
+// pointer from `resource.getpointer`, which the backends turn into
+// `dx.op.textureStore` / `OpImageWrite`.
+
+TEXTURE<float4> Tex : register(u0);
+TEXTURE<float> Tex2 : register(u1);
+TEXTURE<int3> Tex3 : register(u2);
+
+[numthreads(1,1,1)]
+void main(COORD_TYPE DTid : SV_DispatchThreadID) {
+  Tex[DTid] = float4(1, 2, 3, 4);
+  Tex[DTid].y = 5.0;
+  Tex2[DTid] = 6.0;
+  Tex3[DTid] = int3(7, 8, 9);
+}
+
+// CHECK: define hidden {{.*}}void @main(unsigned int 
vector[[[COORD_DIM]]])(<[[COORD_DIM]] x i32> noundef %[[DTID:.*]])
+// CHECK: %[[DTID_ADDR:.*]] = alloca <[[COORD_DIM]] x i32>
+// CHECK: store <[[COORD_DIM]] x i32> %[[DTID]], ptr %[[DTID_ADDR]]
+
+// Store a whole texel.
+// CHECK: %[[DTID_VAL:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]]
+// CHECK: %[[CALL1:.*]] = call noundef {{.*}}ptr{{.*}} 
@hlsl::[[TEXTURE]]<float vector[4]>::operator[](unsigned int 
vector[[[COORD_DIM]]]) const(ptr {{.*}} @Tex, <[[COORD_DIM]] x i32> noundef 
%[[DTID_VAL]])
+// CHECK: store <4 x float> <float 1.000000e+00, float 2.000000e+00, float 
3.000000e+00, float 4.000000e+00>, ptr{{.*}} %[[CALL1]]
+
+// Store a single component: a GEP off the texel pointer, which the DXIL
+// backend later expands into a read-modify-write of the whole texel.
+// CHECK: %[[DTID_VAL2:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]]
+// CHECK: %[[CALL2:.*]] = call noundef {{.*}}ptr{{.*}} 
@hlsl::[[TEXTURE]]<float vector[4]>::operator[](unsigned int 
vector[[[COORD_DIM]]]) const(ptr {{.*}} @Tex, <[[COORD_DIM]] x i32> noundef 
%[[DTID_VAL2]])
+// CHECK: %[[ELEM:.*]] = getelementptr <4 x float>, ptr{{.*}} %[[CALL2]], i32 
0, i32 1
+// CHECK: store float 5.000000e+00, ptr{{.*}} %[[ELEM]]
+
+// Store to a scalar texture.
+// CHECK: %[[DTID_VAL3:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]]
+// CHECK: %[[CALL3:.*]] = call noundef {{.*}}ptr{{.*}} 
@hlsl::[[TEXTURE]]<float>::operator[](unsigned int vector[[[COORD_DIM]]]) 
const(ptr {{.*}} @Tex2, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL3]])
+// CHECK: store float 6.000000e+00, ptr{{.*}} %[[CALL3]]
+
+// Store to an integer texture.
+// CHECK: %[[DTID_VAL4:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]]
+// CHECK: %[[CALL4:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<int 
vector[3]>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr {{.*}} 
@Tex3, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL4]])
+// CHECK: store <3 x i32> <i32 7, i32 8, i32 9>, ptr{{.*}} %[[CALL4]]
+
+// Check the operator[] body
+// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} 
@hlsl::[[TEXTURE]]<float vector[4]>::operator[](unsigned int 
vector[[[COORD_DIM]]]) const(ptr {{.*}} %[[THIS:.*]], <[[COORD_DIM]] x i32> 
noundef %[[INDEX:.*]])
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr
+// CHECK: %[[INDEX_ADDR:.*]] = alloca <[[COORD_DIM]] x i32>
+// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]]
+// CHECK: store <[[COORD_DIM]] x i32> %[[INDEX]], ptr %[[INDEX_ADDR]]
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// CHECK: %[[HANDLE_PTR:.*]] = getelementptr {{.*}} 
%"class.hlsl::[[TEXTURE]]", ptr %[[THIS1]], i32 0, i32 0
+// DXIL: %[[HANDLE:.*]] = load target("dx.Texture", <4 x float>, 1, 0, 0, 
[[DXIL_TY]]), ptr %[[HANDLE_PTR]]
+// SPIRV: %[[HANDLE:.*]] = load target("spirv.Image", float, 1, 2, 
[[ARRAYED]], 0, 2, 1), ptr %[[HANDLE_PTR]]
+// CHECK: %[[INDEX_VAL:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[INDEX_ADDR]]
+// DXIL: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.{{.*}}(target("dx.Texture", <4 x float>, 1, 0, 
0, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]])
+// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.Image", float, 1, 2, 
[[ARRAYED]], 0, 2, 1) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]])
+// CHECK: ret ptr {{.*}}%[[PTR]]

diff  --git a/clang/test/SemaHLSL/Resources/RWTextures-Subscript.hlsl 
b/clang/test/SemaHLSL/Resources/RWTextures-Subscript.hlsl
new file mode 100644
index 0000000000000..c58706d474236
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/RWTextures-Subscript.hlsl
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-finclude-default-header -DTEXTURE=RWTexture2D -DINDEX=uint2 
-DINDEX_INIT="uint2(1, 2)" -DARRAYED=0 -verify -o - %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-finclude-default-header -DTEXTURE=RWTexture2DArray -DINDEX=uint3 
-DINDEX_INIT="uint3(1, 2, 0)" -DARRAYED=1 -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl 
-finclude-default-header -DTEXTURE=RWTexture2D -DINDEX=uint2 
-DINDEX_INIT="uint2(1, 2)" -DARRAYED=0 -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl 
-finclude-default-header -DTEXTURE=RWTexture2DArray -DINDEX=uint3 
-DINDEX_INIT="uint3(1, 2, 0)" -DARRAYED=1 -verify -o - %s
+
+// Writable textures return a non-const reference from operator[], so unlike 
the
+// read-only textures in Textures-Subscript.hlsl they can be assigned through.
+
+TEXTURE<float4> Tex;
+TEXTURE<float> Tex2;
+TEXTURE<int3> Tex3;
+
+struct S { int a; };
+
+void main() {
+  INDEX valid_index = INDEX_INIT;
+
+  // Storing a whole texel is fine.
+  Tex[valid_index] = float4(1, 2, 3, 4);
+
+  // So is storing a single component...
+  Tex[valid_index].y = 5.0;
+
+  // ...a swizzle...
+  Tex[valid_index].xy = float2(6, 7);
+
+  // ...and a read-modify-write.
+  Tex[valid_index] += float4(1, 1, 1, 1);
+
+  // Scalar and integer element types work the same way.
+  Tex2[valid_index] = 8.0;
+  Tex3[valid_index] = int3(9, 10, 11);
+
+  // Reading back through the subscript.
+  float4 val = Tex[valid_index];
+
+  // The index has to be convertible to the coordinate vector.
+  S s = { 1 };
+  // expected-error-re@+2 {{no viable overloaded operator[] for type 
'RWTexture{{.*}}<float4>'}}
+  // expected-note-re@*:* {{candidate function not viable: no known conversion 
from 'S' to 'vector<unsigned int, {{[0-9]}}>'}}
+  Tex[s] = float4(1, 2, 3, 4);
+
+#if ARRAYED
+  // Array textures require a 3-component index.
+  uint2 too_few = uint2(1, 2);
+  // expected-error-re@+2 {{no viable overloaded operator[] for type 
'RWTexture{{.*}}<float4>'}}
+  // expected-note@*:* {{candidate function not viable: no known conversion 
from 'uint2' (aka 'vector<uint, 2>') to 'vector<unsigned int, 3>'}}
+  Tex[too_few] = float4(1, 2, 3, 4);
+#endif
+}

diff  --git a/clang/test/SemaHLSL/Resources/Textures-Subscript.hlsl 
b/clang/test/SemaHLSL/Resources/Textures-Subscript.hlsl
index f93c332ad04cb..1b4236c9093f9 100644
--- a/clang/test/SemaHLSL/Resources/Textures-Subscript.hlsl
+++ b/clang/test/SemaHLSL/Resources/Textures-Subscript.hlsl
@@ -36,4 +36,18 @@ void main() {
   // expected-note@*:* {{candidate function not viable: no known conversion 
from 'uint2' (aka 'vector<uint, 2>') to 'vector<unsigned int, 3>'}}
   float4 val5 = Tex[too_few];
 #endif
+
+  // A read-only texture's operator[] returns a const reference, so it cannot 
be
+  // assigned through. The writable counterpart is covered by
+  // RWTextures-Subscript.hlsl.
+  // expected-note@*:* 3 {{function 'operator[]' which returns const-qualified 
type 'vector<float, 4> const hlsl_device &' declared here}}
+
+  // expected-error@+1 {{cannot assign to return value because function 
'operator[]' returns a const value}}
+  Tex[valid_index] = float4(1, 2, 3, 4);
+
+  // expected-error@+1 {{cannot assign to return value because function 
'operator[]' returns a const value}}
+  Tex[valid_index].y = 5.0;
+
+  // expected-error@+1 {{cannot assign to return value because function 
'operator[]' returns a const value}}
+  Tex[valid_index] += float4(1, 1, 1, 1);
 }

diff  --git a/llvm/docs/DirectX/DXILResources.rst 
b/llvm/docs/DirectX/DXILResources.rst
index 3c922c888b40f..1f969841009a9 100644
--- a/llvm/docs/DirectX/DXILResources.rst
+++ b/llvm/docs/DirectX/DXILResources.rst
@@ -553,6 +553,51 @@ Examples:
    call void @llvm.dx.resource.store.typedbuffer.tdx.Buffer_v2f64_1_0_0t(
        target("dx.TypedBuffer", f64, 1, 0) %buf, i32 %index, <2 x f64> %data)
 
+For Textures, the coordinates are a scalar for 1D textures and a vector of 2 or
+3 elements for the higher dimensional and array textures. Like TypedBuffer, a
+store writes a whole texel, so partial writes have to be expressed as a
+read-modify-write of the full value.
+
+Examples:
+
+.. list-table:: ``@llvm.dx.resource.store.texture``
+   :header-rows: 1
+
+   * - Argument
+     -
+     - Type
+     - Description
+   * - Return value
+     -
+     - ``void``
+     -
+   * - ``%texture``
+     - 0
+     - ``target(dx.Texture, ...)``
+     - The texture to store into
+   * - ``%coords``
+     - 1
+     - ``i32`` or a 2- or 3-element vector of ``i32``
+     - Coordinates into the texture
+   * - ``%data``
+     - 2
+     - Scalar or vector of the type of the texture
+     - The data to store
+
+Examples:
+
+.. code-block:: llvm
+
+   call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_1t.i32.v4f32(
+       target("dx.Texture", <4 x float>, 1, 0, 0, 1) %tex,
+       i32 %coord, <4 x float> %data)
+   call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_2t.v2i32.v4f32(
+       target("dx.Texture", <4 x float>, 1, 0, 0, 2) %tex,
+       <2 x i32> %coords, <4 x float> %data)
+   call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_4t.v3i32.v4f32(
+       target("dx.Texture", <4 x float>, 1, 0, 0, 4) %tex,
+       <3 x i32> %coords, <4 x float> %data)
+
 For RawBuffer, we need two indices and we accept scalars and vectors of 4 or
 fewer elements. Note that we do allow vectors of 4 64-bit elements here.
 

diff  --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 033de14304fe6..9c9b2032035e3 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -145,6 +145,10 @@ def int_dx_resource_load_level
                              llvm_any_ty],
                             [IntrReadMem]>;
 
+def int_dx_resource_store_texture
+    : DefaultAttrsIntrinsic<[], [llvm_any_ty, llvm_any_ty, llvm_any_ty],
+                            [IntrWriteMem]>;
+
 def int_dx_resource_calculate_lod
     : DefaultAttrsIntrinsic<[llvm_float_ty],
                             [llvm_any_ty, llvm_any_ty, llvm_any_ty],

diff  --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 5f13f9aba41f2..bbf7cddb5c17c 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -1003,6 +1003,18 @@ def TextureLoad : DXILOp<66, textureLoad> {
   let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
 }
 
+def TextureStore : DXILOp<67, textureStore> {
+  let Doc = "writes to an RWTexture";
+  // Handle, Coord0, Coord1, Coord2, Val0, Val1, Val2, Val3, Mask
+  let arguments = [
+    HandleTy, Int32Ty, Int32Ty, Int32Ty, OverloadTy, OverloadTy, OverloadTy,
+    OverloadTy, Int8Ty
+  ];
+  let result = VoidTy;
+  let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy, Int16Ty, Int32Ty]>];
+  let stages = [Stages<DXIL1_0, [all_stages]>];
+}
+
 def BufferLoad : DXILOp<68, bufferLoad> {
   let Doc = "reads from a TypedBuffer";
   // Handle, Coord0, Coord1

diff  --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp 
b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 587543ef740d4..61dc9e533ba0f 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -36,6 +36,12 @@
 using namespace llvm;
 using namespace llvm::dxil;
 
+/// Write mask covering all four components of a UAV element. Typed UAV stores
+/// (textures and typed buffers) must always use this mask - the DXIL validator
+/// rejects anything narrower. Only raw and / structured buffer stores may use 
a
+/// partial mask.
+static constexpr uint8_t TypedUAVStoreWriteMask = 0xF;
+
 namespace {
 class OpLowerer {
   Module &M;
@@ -633,6 +639,16 @@ class OpLowerer {
       Value *MipLevel = CI->getArgOperand(2);
       Value *Offsets = CI->getArgOperand(3);
 
+      // A UAV descriptor binds a single mip slice, so there is no mip to 
select
+      // in the case of a UAV. Multisampled UAVs are the exception: the slot
+      // carries a sample index and stays live.
+      auto *HandleTy = cast<TargetExtType>(CI->getArgOperand(0)->getType());
+      dxil::ResourceTypeInfo &RTI = DRTM[HandleTy];
+      dxil::ResourceKind Kind = RTI.getResourceKind();
+      if (RTI.isUAV() && Kind != dxil::ResourceKind::Texture2DMS &&
+          Kind != dxil::ResourceKind::Texture2DMSArray)
+        MipLevel = UndefValue::get(Int32Ty);
+
       Type *OldTy = CI->getType();
       Type *NewRetTy = OpBuilder.getResRetType(OldTy->getScalarType());
 
@@ -879,6 +895,66 @@ class OpLowerer {
     return false;
   }
 
+  /// Splits the value operand of a resource store into its (at most four)
+  /// scalar components. Slots beyond the length of `Data` are filled with
+  /// `undef` when `FillWithUndef` is set (raw and structured buffers), or with
+  /// the first component otherwise (typed UAVs, which must write all four
+  /// components - repeating the first one matches DXC).
+  static std::array<Value *, 4> splitStoreData(IRBuilder<> &IRB, Value *Data,
+                                               uint64_t NumElements,
+                                               bool FillWithUndef) {
+    Type *DataTy = Data->getType();
+    Type *ScalarTy = DataTy->getScalarType();
+
+    std::array<Value *, 4> DataElements{nullptr, nullptr, nullptr, nullptr};
+    if (DataTy == ScalarTy)
+      DataElements[0] = Data;
+    else {
+      // Since we're post-scalarizer, if we see a vector here it's likely
+      // constructed solely for the argument of the store. Just use the scalar
+      // values from before they're inserted into the temporary.
+      auto *IEI = dyn_cast<InsertElementInst>(Data);
+      while (IEI) {
+        auto *IndexOp = dyn_cast<ConstantInt>(IEI->getOperand(2));
+        if (!IndexOp)
+          break;
+        size_t IndexVal = IndexOp->getZExtValue();
+        assert(IndexVal < 4 && "Too many elements for resource store");
+        DataElements[IndexVal] = IEI->getOperand(1);
+        IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0));
+      }
+    }
+
+    // If for some reason we weren't able to forward the arguments from the
+    // scalarizer artifact, then we may need to actually extract elements from
+    // the vector.
+    for (uint64_t I = 0, E = NumElements; I < E; ++I)
+      if (DataElements[I] == nullptr)
+        DataElements[I] = IRB.CreateExtractElement(
+            Data, ConstantInt::get(IRB.getInt32Ty(), I));
+
+    // For any elements beyond the length of the vector, we should fill it up
+    // with undef - however, for typed UAVs we repeat the first element to
+    // match DXC.
+    for (uint64_t I = NumElements, E = 4; I < E; ++I)
+      if (DataElements[I] == nullptr)
+        DataElements[I] =
+            FillWithUndef ? UndefValue::get(ScalarTy) : DataElements[0];
+
+    return DataElements;
+  }
+
+  /// Erase the chain of `insertelement`s that only existed to build up the
+  /// value operand of a store we've just replaced.
+  static void eraseDeadInsertElementChain(Value *Data) {
+    auto *IEI = dyn_cast<InsertElementInst>(Data);
+    while (IEI && IEI->use_empty()) {
+      InsertElementInst *Tmp = IEI;
+      IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0));
+      Tmp->eraseFromParent();
+    }
+  }
+
   [[nodiscard]] bool lowerBufferStore(Function &F, bool IsRaw) {
     const DataLayout &DL = F.getDataLayout();
     IRBuilder<> &IRB = OpBuilder.getIRB();
@@ -906,8 +982,8 @@ class OpLowerer {
 
       uint64_t NumElements =
           DL.getTypeSizeInBits(DataTy) / DL.getTypeSizeInBits(ScalarTy);
-      Value *Mask =
-          ConstantInt::get(Int8Ty, IsRaw ? ~(~0U << NumElements) : 15U);
+      Value *Mask = ConstantInt::get(Int8Ty, IsRaw ? ~(~0U << NumElements)
+                                                   : TypedUAVStoreWriteMask);
 
       // TODO: check that we only have vector or scalar...
       if (NumElements > 4)
@@ -915,39 +991,8 @@ class OpLowerer {
             "Buffer store data must have at most 4 elements",
             inconvertibleErrorCode());
 
-      std::array<Value *, 4> DataElements{nullptr, nullptr, nullptr, nullptr};
-      if (DataTy == ScalarTy)
-        DataElements[0] = Data;
-      else {
-        // Since we're post-scalarizer, if we see a vector here it's likely
-        // constructed solely for the argument of the store. Just use the 
scalar
-        // values from before they're inserted into the temporary.
-        auto *IEI = dyn_cast<InsertElementInst>(Data);
-        while (IEI) {
-          auto *IndexOp = dyn_cast<ConstantInt>(IEI->getOperand(2));
-          if (!IndexOp)
-            break;
-          size_t IndexVal = IndexOp->getZExtValue();
-          assert(IndexVal < 4 && "Too many elements for buffer store");
-          DataElements[IndexVal] = IEI->getOperand(1);
-          IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0));
-        }
-      }
-
-      // If for some reason we weren't able to forward the arguments from the
-      // scalarizer artifact, then we may need to actually extract elements 
from
-      // the vector.
-      for (int I = 0, E = NumElements; I < E; ++I)
-        if (DataElements[I] == nullptr)
-          DataElements[I] =
-              IRB.CreateExtractElement(Data, ConstantInt::get(Int32Ty, I));
-
-      // For any elements beyond the length of the vector, we should fill it up
-      // with undef - however, for typed buffers we repeat the first element to
-      // match DXC.
-      for (int I = NumElements, E = 4; I < E; ++I)
-        if (DataElements[I] == nullptr)
-          DataElements[I] = IsRaw ? UndefValue::get(ScalarTy) : 
DataElements[0];
+      std::array<Value *, 4> DataElements =
+          splitStoreData(IRB, Data, NumElements, /*FillWithUndef=*/IsRaw);
 
       dxil::OpCode Op = OpCode::BufferStore;
       SmallVector<Value *, 9> Args{
@@ -965,13 +1010,55 @@ class OpLowerer {
         return E;
 
       CI->eraseFromParent();
-      // Clean up any leftover `insertelement`s
-      auto *IEI = dyn_cast<InsertElementInst>(Data);
-      while (IEI && IEI->use_empty()) {
-        InsertElementInst *Tmp = IEI;
-        IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0));
-        Tmp->eraseFromParent();
-      }
+      eraseDeadInsertElementChain(Data);
+
+      return Error::success();
+    });
+  }
+
+  [[nodiscard]] bool lowerTextureStore(Function &F) {
+    const DataLayout &DL = F.getDataLayout();
+    IRBuilder<> &IRB = OpBuilder.getIRB();
+    Type *Int8Ty = IRB.getInt8Ty();
+    Type *Int32Ty = IRB.getInt32Ty();
+
+    return replaceFunction(F, [&](CallInst *CI) -> Error {
+      IRB.SetInsertPoint(CI);
+
+      Value *Handle =
+          createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
+      Value *Coords = CI->getArgOperand(1);
+      Value *Data = CI->getArgOperand(2);
+
+      Type *DataTy = Data->getType();
+      Type *ScalarTy = DataTy->getScalarType();
+      uint64_t NumElements =
+          DL.getTypeSizeInBits(DataTy) / DL.getTypeSizeInBits(ScalarTy);
+      if (NumElements > 4)
+        return make_error<StringError>(
+            "Texture store data must have at most 4 elements",
+            inconvertibleErrorCode());
+
+      Value *Mask = ConstantInt::get(Int8Ty, TypedUAVStoreWriteMask);
+      std::array<Value *, 4> DataElements =
+          splitStoreData(IRB, Data, NumElements, /*FillWithUndef=*/false);
+
+      Value *Undef = UndefValue::get(Int32Ty);
+      std::array<Value *, 9> Args{
+          Handle,          Undef,           Undef,
+          Undef,           DataElements[0], DataElements[1],
+          DataElements[2], DataElements[3], Mask};
+
+      // Copy the coordinates into Args.
+      extractElementsIntoArgs(IRB, Args, 1, Coords, 3);
+
+      Expected<CallInst *> OpCall =
+          OpBuilder.tryCreateOp(OpCode::TextureStore, Args, CI->getName());
+      if (Error E = OpCall.takeError())
+        return E;
+
+      CI->eraseFromParent();
+      eraseDeadInsertElementChain(Data);
 
       return Error::success();
     });
@@ -1233,6 +1320,9 @@ class OpLowerer {
       case Intrinsic::dx_resource_store_typedbuffer:
         HasErrors |= lowerBufferStore(F, /*IsRaw=*/false);
         break;
+      case Intrinsic::dx_resource_store_texture:
+        HasErrors |= lowerTextureStore(F);
+        break;
       case Intrinsic::dx_resource_load_rawbuffer:
         HasErrors |= lowerRawBufferLoad(F);
         break;

diff  --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp 
b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 3f9405464aa74..92574552af8ed 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -145,6 +145,58 @@ static void createTypedBufferStore(IntrinsicInst *II, 
StoreInst *SI,
   SI->replaceAllUsesWith(Inst);
 }
 
+/// Build a zero-initialized offset operand matching the shape of the given
+/// coordinate operand. Accesses through `operator[]` never have offsets.
+static Value *getNullOffsetsFor(IRBuilder<> &Builder, Value *Coords) {
+  Type *CoordTy = Coords->getType();
+  Type *OffsetTy;
+  if (auto *VecTy = dyn_cast<FixedVectorType>(CoordTy))
+    OffsetTy =
+        FixedVectorType::get(Builder.getInt32Ty(), VecTy->getNumElements());
+  else
+    OffsetTy = Builder.getInt32Ty();
+  return Constant::getNullValue(OffsetTy);
+}
+
+static void createTextureStore(IntrinsicInst *II, StoreInst *SI,
+                               dxil::ResourceTypeInfo &RTI) {
+  const DataLayout &DL = SI->getDataLayout();
+  IRBuilder<> Builder(SI);
+  Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
+  Type *ScalarType = ContainedType->getScalarType();
+
+  Value *Handle = II->getOperand(0);
+  Value *Coords = II->getOperand(1);
+
+  Value *V = SI->getValueOperand();
+  if (V->getType() == ContainedType) {
+    // V is already the right type.
+    assert(SI->getPointerOperand() == II &&
+           "Store of whole element has mismatched address to store to");
+  } else if (V->getType() == ScalarType) {
+    // We're storing a scalar, so we need to load the current value and only
+    // replace the relevant part. For operator[] the mip level and the offsets
+    // are always zero; DXILOpLowering drops the mip level for UAVs.
+    Value *MipLevel = Builder.getInt32(0);
+    Value *Offsets = getNullOffsetsFor(Builder, Coords);
+    auto *Load = Builder.CreateIntrinsic(ContainedType,
+                                         Intrinsic::dx_resource_load_level,
+                                         {Handle, Coords, MipLevel, Offsets});
+
+    uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
+    Value *Offset =
+        traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
+    V = Builder.CreateInsertElement(Load, V, Offset);
+  } else {
+    llvm_unreachable("Store to texture resource has invalid type");
+  }
+
+  auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
+                                       Intrinsic::dx_resource_store_texture,
+                                       {Handle, Coords, V});
+  SI->replaceAllUsesWith(Inst);
+}
+
 static void emitRawStore(IRBuilder<> &Builder, Value *Buffer, Value *Index,
                          Value *Offset, Value *V, dxil::ResourceTypeInfo &RTI) 
{
   // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
@@ -209,16 +261,18 @@ static void createStoreIntrinsic(IntrinsicInst *II, 
StoreInst *SI,
     return createRawStores(II, SI, RTI);
   case dxil::ResourceKind::Texture1D:
   case dxil::ResourceKind::Texture2D:
-  case dxil::ResourceKind::Texture2DMS:
   case dxil::ResourceKind::Texture3D:
-  case dxil::ResourceKind::TextureCube:
   case dxil::ResourceKind::Texture1DArray:
   case dxil::ResourceKind::Texture2DArray:
+    return createTextureStore(II, SI, RTI);
+  case dxil::ResourceKind::Texture2DMS:
   case dxil::ResourceKind::Texture2DMSArray:
+  case dxil::ResourceKind::TextureCube:
   case dxil::ResourceKind::TextureCubeArray:
   case dxil::ResourceKind::FeedbackTexture2D:
   case dxil::ResourceKind::FeedbackTexture2DArray:
-    reportFatalUsageError("DXIL Store not implemented for texture resources");
+    reportFatalUsageError(
+        "DXIL Store not implemented for this texture resource kind");
     return;
   case dxil::ResourceKind::CBuffer:
   case dxil::ResourceKind::Sampler:
@@ -390,14 +444,7 @@ static void createTextureLoad(IntrinsicInst *II, LoadInst 
*LI,
   Value *MipLevel = Builder.getInt32(0);
 
   // For operator[], offsets are zero.
-  Type *CoordTy = Coords->getType();
-  Type *OffsetTy;
-  if (auto *VecTy = dyn_cast<FixedVectorType>(CoordTy))
-    OffsetTy =
-        FixedVectorType::get(Builder.getInt32Ty(), VecTy->getNumElements());
-  else
-    OffsetTy = Builder.getInt32Ty();
-  Value *Offsets = Constant::getNullValue(OffsetTy);
+  Value *Offsets = getNullOffsetsFor(Builder, Coords);
 
   Value *V =
       Builder.CreateIntrinsic(ContainedType, Intrinsic::dx_resource_load_level,

diff  --git a/llvm/test/CodeGen/DirectX/ResourceAccess/store_texture.ll 
b/llvm/test/CodeGen/DirectX/ResourceAccess/store_texture.ll
new file mode 100644
index 0000000000000..515869935e3ee
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/store_texture.ll
@@ -0,0 +1,153 @@
+; RUN: opt -S -dxil-resource-access %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK-LABEL: define void @store_texture1d_float4
+define void @store_texture1d_float4(<4 x float> %data, i32 %coord) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 1)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 1) %texture, i32 %coord)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_1t.i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 1) %texture, i32 %coord, <4 x float> %data)
+  store <4 x float> %data, ptr %ptr
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2d_float4
+define void @store_texture2d_float4(<4 x float> %data, <2 x i32> %coords, i32 
%elemindex) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> 
%coords)
+
+  ; Store the whole value
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_2t.v2i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, <4 x float> %data)
+  store <4 x float> %data, ptr %ptr
+
+  ; Store just the .x component
+  %scalar = extractelement <4 x float> %data, i32 0
+  ; CHECK: %[[LOAD:.*]] = call <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_1_0_0_2t.v2i32.i32.v2i32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, i32 0, <2 x i32> 
zeroinitializer)
+  ; CHECK: %[[INSERT:.*]] = insertelement <4 x float> %[[LOAD]], float 
%scalar, i32 0
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_2t.v2i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, <4 x float> %[[INSERT]])
+  store float %scalar, ptr %ptr
+
+  ; Store just the .y component
+  ; CHECK: %[[LOAD:.*]] = call <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_1_0_0_2t.v2i32.i32.v2i32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, i32 0, <2 x i32> 
zeroinitializer)
+  ; CHECK: %[[INSERT:.*]] = insertelement <4 x float> %[[LOAD]], float 
%scalar, i32 1
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_2t.v2i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, <4 x float> %[[INSERT]])
+  %y_ptr = getelementptr inbounds i8, ptr %ptr, i32 4
+  store float %scalar, ptr %y_ptr
+
+  ; Store to one of the elements dynamically
+  ; CHECK: %[[LOAD:.*]] = call <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_1_0_0_2t.v2i32.i32.v2i32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, i32 0, <2 x i32> 
zeroinitializer)
+  ; CHECK: %[[INSERT:.*]] = insertelement <4 x float> %[[LOAD]], float 
%scalar, i32 %elemindex
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_2t.v2i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 2) %texture, <2 x i32> %coords, <4 x float> %[[INSERT]])
+  %dynamic = getelementptr inbounds float, ptr %ptr, i32 %elemindex
+  store float %scalar, ptr %dynamic
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2d_float
+define void @store_texture2d_float(float %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", float, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", float, 1, 0, 0, 2) %texture, <2 x i32> %coords)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_f32_1_0_0_2t.v2i32.f32(target("dx.Texture",
 float, 1, 0, 0, 2) %texture, <2 x i32> %coords, float %data)
+  store float %data, ptr %ptr
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2d_int3
+define void @store_texture2d_int3(<3 x i32> %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", <3 x i32>, 1, 0, 1, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <3 x i32>, 1, 0, 1, 2) %texture, <2 x i32> %coords)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v3i32_1_0_1_2t.v2i32.v3i32(target("dx.Texture",
 <3 x i32>, 1, 0, 1, 2) %texture, <2 x i32> %coords, <3 x i32> %data)
+  store <3 x i32> %data, ptr %ptr
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture1darray_float4
+define void @store_texture1darray_float4(<4 x float> %data, <2 x i32> %coords) 
{
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 6)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 6) %texture, <2 x i32> 
%coords)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_6t.v2i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 6) %texture, <2 x i32> %coords, <4 x float> %data)
+  store <4 x float> %data, ptr %ptr
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2darray_float4
+define void @store_texture2darray_float4(<4 x float> %data, <3 x i32> %coords) 
{
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 7)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 4, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 7) %texture, <3 x i32> 
%coords)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f32_1_0_0_7t.v3i32.v4f32(target("dx.Texture",
 <4 x float>, 1, 0, 0, 7) %texture, <3 x i32> %coords, <4 x float> %data)
+  store <4 x float> %data, ptr %ptr
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture3d_float3
+define void @store_texture3d_float3(<3 x float> %data, <3 x i32> %coords) {
+  %texture = call target("dx.Texture", <3 x float>, 1, 0, 0, 4)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <3 x float>, 1, 0, 0, 4) %texture, <3 x i32> 
%coords)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v3f32_1_0_0_4t.v3i32.v3f32(target("dx.Texture",
 <3 x float>, 1, 0, 0, 4) %texture, <3 x i32> %coords, <3 x float> %data)
+  store <3 x float> %data, ptr %ptr
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2d_half4
+define void @store_texture2d_half4(<4 x half> %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x half>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 6, i32 1, i32 0, ptr null)
+
+  ; CHECK-NOT: @llvm.dx.resource.getpointer
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x half>, 1, 0, 0, 2) %texture, <2 x i32> %coords)
+
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f16_1_0_0_2t.v2i32.v4f16(target("dx.Texture",
 <4 x half>, 1, 0, 0, 2) %texture, <2 x i32> %coords, <4 x half> %data)
+  store <4 x half> %data, ptr %ptr
+
+  ; Store just the .z component
+  ; CHECK: %[[LOAD:.*]] = call <4 x half> 
@llvm.dx.resource.load.level.v4f16.tdx.Texture_v4f16_1_0_0_2t.v2i32.i32.v2i32(target("dx.Texture",
 <4 x half>, 1, 0, 0, 2) %texture, <2 x i32> %coords, i32 0, <2 x i32> 
zeroinitializer)
+  ; CHECK: %[[INSERT:.*]] = insertelement <4 x half> %[[LOAD]], half %scalar, 
i32 2
+  ; CHECK: call void 
@llvm.dx.resource.store.texture.tdx.Texture_v4f16_1_0_0_2t.v2i32.v4f16(target("dx.Texture",
 <4 x half>, 1, 0, 0, 2) %texture, <2 x i32> %coords, <4 x half> %[[INSERT]])
+  %scalar = extractelement <4 x half> %data, i32 2
+  %z_ptr = getelementptr inbounds i8, ptr %ptr, i32 4
+  store half %scalar, ptr %z_ptr
+
+  ret void
+}

diff  --git a/llvm/test/CodeGen/DirectX/TextureLoad.ll 
b/llvm/test/CodeGen/DirectX/TextureLoad.ll
index aff0aa9e6f793..cf6c460ac2105 100644
--- a/llvm/test/CodeGen/DirectX/TextureLoad.ll
+++ b/llvm/test/CodeGen/DirectX/TextureLoad.ll
@@ -221,3 +221,62 @@ define void 
@load_texture2darray_float4_with_level_and_offset(<3 x i32> %coords)
   call void @use_float4(<4 x float> %data)
   ret void
 }
+
+; A UAV descriptor binds a single mip slice, so there is no mip to select
+; in the case of a UAV. Multisampled UAVs are the exception: the slot
+; carries a sample index and stays live.
+
+; CHECK-LABEL: define void @load_rwtexture2d_float4_implicit_mip(
+define void @load_rwtexture2d_float4_implicit_mip(<2 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_1_0_0_2t(
+          i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: %[[LOAD:.*]] = call %dx.types.ResRet.f32 @dx.op.textureLoad.f32(i32 
66, %dx.types.Handle %{{.*}}, i32 undef, i32 %[[COORD0]], i32 %[[COORD1]], i32 
undef, i32 undef, i32 undef, i32 undef)
+  %data = call <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_1_0_0_2t.v2i32.i32.v2i32(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 2) %texture,
+      <2 x i32> %coords, i32 0, <2 x i32> zeroinitializer)
+
+  ; CHECK: extractvalue %dx.types.ResRet.f32 %[[LOAD]], 0
+  call void @use_float4(<4 x float> %data)
+  ret void
+}
+
+; CHECK-LABEL: define void @load_rwtexture2d_float4_explicit_lod(
+define void @load_rwtexture2d_float4_explicit_lod(<2 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_1_0_0_2t(
+          i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: %[[LOAD:.*]] = call %dx.types.ResRet.f32 @dx.op.textureLoad.f32(i32 
66, %dx.types.Handle %{{.*}}, i32 undef, i32 %[[COORD0]], i32 %[[COORD1]], i32 
undef, i32 undef, i32 undef, i32 undef)
+  %data = call <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_1_0_0_2t.v2i32.i32.v2i32(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 2) %texture,
+      <2 x i32> %coords, i32 3, <2 x i32> zeroinitializer)
+
+  ; CHECK: extractvalue %dx.types.ResRet.f32 %[[LOAD]], 0
+  call void @use_float4(<4 x float> %data)
+  ret void
+}
+
+; CHECK-LABEL: define void @load_rwtexture2darray_float4(
+define void @load_rwtexture2darray_float4(<3 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 7)
+      @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_1_0_0_7t(
+          i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[COORD0:.*]] = extractelement <3 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <3 x i32> %coords, i64 1
+  ; CHECK: %[[COORD2:.*]] = extractelement <3 x i32> %coords, i64 2
+  ; CHECK: %[[LOAD:.*]] = call %dx.types.ResRet.f32 @dx.op.textureLoad.f32(i32 
66, %dx.types.Handle %{{.*}}, i32 undef, i32 %[[COORD0]], i32 %[[COORD1]], i32 
%[[COORD2]], i32 undef, i32 undef, i32 undef)
+  %data = call <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_1_0_0_7t.v3i32.i32.v2i32(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 7) %texture,
+      <3 x i32> %coords, i32 0, <2 x i32> zeroinitializer)
+
+  ; CHECK: extractvalue %dx.types.ResRet.f32 %[[LOAD]], 0
+  call void @use_float4(<4 x float> %data)
+  ret void
+}

diff  --git a/llvm/test/CodeGen/DirectX/TextureStore.ll 
b/llvm/test/CodeGen/DirectX/TextureStore.ll
new file mode 100644
index 0000000000000..c37b3f2baccd9
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/TextureStore.ll
@@ -0,0 +1,176 @@
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK-LABEL: define void @store_texture1d_float4(
+define void @store_texture1d_float4(<4 x float> %data, i32 %coord) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 1)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <4 x float> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <4 x float> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <4 x float> %data, i32 2
+  ; CHECK: %[[DATA3:.*]] = extractelement <4 x float> %data, i32 3
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %coord, i32 undef, i32 undef, float %[[DATA0]], float %[[DATA1]], float 
%[[DATA2]], float %[[DATA3]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 1) %texture,
+      i32 %coord, <4 x float> %data)
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2d_float4(
+define void @store_texture2d_float4(<4 x float> %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <4 x float> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <4 x float> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <4 x float> %data, i32 2
+  ; CHECK: %[[DATA3:.*]] = extractelement <4 x float> %data, i32 3
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 undef, float %[[DATA0]], float 
%[[DATA1]], float %[[DATA2]], float %[[DATA3]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 2) %texture,
+      <2 x i32> %coords, <4 x float> %data)
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture3d_float4(
+define void @store_texture3d_float4(<4 x float> %data, <3 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 4)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <4 x float> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <4 x float> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <4 x float> %data, i32 2
+  ; CHECK: %[[DATA3:.*]] = extractelement <4 x float> %data, i32 3
+  ; CHECK: %[[COORD0:.*]] = extractelement <3 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <3 x i32> %coords, i64 1
+  ; CHECK: %[[COORD2:.*]] = extractelement <3 x i32> %coords, i64 2
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 %[[COORD2]], float %[[DATA0]], float 
%[[DATA1]], float %[[DATA2]], float %[[DATA3]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 4) %texture,
+      <3 x i32> %coords, <4 x float> %data)
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture1darray_float4(
+define void @store_texture1darray_float4(<4 x float> %data, <2 x i32> %coords) 
{
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 6)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <4 x float> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <4 x float> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <4 x float> %data, i32 2
+  ; CHECK: %[[DATA3:.*]] = extractelement <4 x float> %data, i32 3
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 undef, float %[[DATA0]], float 
%[[DATA1]], float %[[DATA2]], float %[[DATA3]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 6) %texture,
+      <2 x i32> %coords, <4 x float> %data)
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2darray_float4(
+define void @store_texture2darray_float4(<4 x float> %data, <3 x i32> %coords) 
{
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 7)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 4, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <4 x float> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <4 x float> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <4 x float> %data, i32 2
+  ; CHECK: %[[DATA3:.*]] = extractelement <4 x float> %data, i32 3
+  ; CHECK: %[[COORD0:.*]] = extractelement <3 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <3 x i32> %coords, i64 1
+  ; CHECK: %[[COORD2:.*]] = extractelement <3 x i32> %coords, i64 2
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 %[[COORD2]], float %[[DATA0]], float 
%[[DATA1]], float %[[DATA2]], float %[[DATA3]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 7) %texture,
+      <3 x i32> %coords, <4 x float> %data)
+
+  ret void
+}
+
+; A scalar texture still has to write all four components, so the value is
+; repeated to fill out the store.
+; CHECK-LABEL: define void @store_texture2d_float(
+define void @store_texture2d_float(float %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", float, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 undef, float %data, float %data, float 
%data, float %data, i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", float, 1, 0, 0, 2) %texture,
+      <2 x i32> %coords, float %data)
+
+  ret void
+}
+
+; A three component texture repeats the first element to fill out the store.
+; CHECK-LABEL: define void @store_texture2d_int3(
+define void @store_texture2d_int3(<3 x i32> %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", <3 x i32>, 1, 0, 1, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 6, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <3 x i32> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <3 x i32> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <3 x i32> %data, i32 2
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call void @dx.op.textureStore.i32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 undef, i32 %[[DATA0]], i32 %[[DATA1]], 
i32 %[[DATA2]], i32 %[[DATA0]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <3 x i32>, 1, 0, 1, 2) %texture,
+      <2 x i32> %coords, <3 x i32> %data)
+
+  ret void
+}
+
+; CHECK-LABEL: define void @store_texture2d_half4(
+define void @store_texture2d_half4(<4 x half> %data, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x half>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 7, i32 1, i32 0, ptr null)
+
+  ; CHECK: %[[DATA0:.*]] = extractelement <4 x half> %data, i32 0
+  ; CHECK: %[[DATA1:.*]] = extractelement <4 x half> %data, i32 1
+  ; CHECK: %[[DATA2:.*]] = extractelement <4 x half> %data, i32 2
+  ; CHECK: %[[DATA3:.*]] = extractelement <4 x half> %data, i32 3
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call void @dx.op.textureStore.f16(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 undef, half %[[DATA0]], half %[[DATA1]], 
half %[[DATA2]], half %[[DATA3]], i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x half>, 1, 0, 0, 2) %texture,
+      <2 x i32> %coords, <4 x half> %data)
+
+  ret void
+}
+
+; The scalarizer leaves behind an insertelement chain that we can forward
+; directly into the store arguments.
+; CHECK-LABEL: define void @store_texture2d_scalarized(
+define void @store_texture2d_scalarized(float %x, float %y, float %z, float 
%w, <2 x i32> %coords) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 8, i32 1, i32 0, ptr null)
+
+  %vec.0 = insertelement <4 x float> poison, float %x, i32 0
+  %vec.1 = insertelement <4 x float> %vec.0, float %y, i32 1
+  %vec.2 = insertelement <4 x float> %vec.1, float %z, i32 2
+  %vec.3 = insertelement <4 x float> %vec.2, float %w, i32 3
+
+  ; CHECK-NOT: insertelement
+  ; CHECK: %[[COORD0:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[COORD1:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %{{.*}}, 
i32 %[[COORD0]], i32 %[[COORD1]], i32 undef, float %x, float %y, float %z, 
float %w, i8 15)
+  call void @llvm.dx.resource.store.texture(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 2) %texture,
+      <2 x i32> %coords, <4 x float> %vec.3)
+
+  ret void
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to