Browse Source

[spirv] Refactor Buffer and Texture load functions (#600)

[spirv] Refactor Buffer and Texture load functions

Also fixes 2 bugs:

1- RWBuffer must use OpImageRead rather than OpImageFetch.
2- OpImageFetch/Read take a parameter whose type is an image. We were
incorectly passing the *variable* (which is of type pointer-to-image).

This change also allows TextureXX<type> to be used with types other than
float4. Tests were added for such cases.
Ehsan 8 years ago
parent
commit
f3d5010b2e

+ 8 - 4
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -159,10 +159,14 @@ public:
                              std::pair<uint32_t, uint32_t> grad,
                              uint32_t constOffset, uint32_t varOffset);
 
-  /// \brief Creates SPIR-V instructions for fetching the given image.
-  uint32_t createImageFetch(uint32_t texelType, uint32_t image,
-                            uint32_t coordinate, uint32_t lod,
-                            uint32_t constOffset, uint32_t varOffset);
+  /// \brief Creates SPIR-V instructions for reading a texel from an image. If
+  /// doImageFetch is true, OpImageFetch is used. OpImageRead is used otherwise.
+  /// OpImageFetch should be used for sampled images. OpImageRead should be used
+  /// for images without a sampler.
+  uint32_t createImageFetchOrRead(bool doImageFetch, uint32_t texelType,
+                                  uint32_t image, uint32_t coordinate,
+                                  uint32_t lod, uint32_t constOffset,
+                                  uint32_t varOffset);
 
   /// \brief Creates SPIR-V instructions for writing to the given image.
   void createImageWrite(uint32_t imageId, uint32_t coordId, uint32_t texelId);

+ 8 - 5
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -294,10 +294,9 @@ void ModuleBuilder::createImageWrite(uint32_t imageId, uint32_t coordId,
   insertPoint->appendInstruction(std::move(constructSite));
 }
 
-uint32_t ModuleBuilder::createImageFetch(uint32_t texelType, uint32_t image,
-                                         uint32_t coordinate, uint32_t lod,
-                                         uint32_t constOffset,
-                                         uint32_t varOffset) {
+uint32_t ModuleBuilder::createImageFetchOrRead(
+    bool doImageFetch, uint32_t texelType, uint32_t image, uint32_t coordinate,
+    uint32_t lod, uint32_t constOffset, uint32_t varOffset) {
   assert(insertPoint && "null insert point");
 
   llvm::SmallVector<uint32_t, 2> params;
@@ -307,7 +306,11 @@ uint32_t ModuleBuilder::createImageFetch(uint32_t texelType, uint32_t image,
           &params));
 
   const uint32_t texelId = theContext.takeNextId();
-  instBuilder.opImageFetch(texelType, texelId, image, coordinate, mask);
+  if (doImageFetch)
+    instBuilder.opImageFetch(texelType, texelId, image, coordinate, mask);
+  else
+    instBuilder.opImageRead(texelType, texelId, image, coordinate, mask);
+
   for (const auto param : params)
     instBuilder.idRef(param);
   instBuilder.x();

+ 41 - 32
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -1330,13 +1330,31 @@ uint32_t SPIRVEmitter::doConditionalOperator(const ConditionalOperator *expr) {
   return theBuilder.createSelect(type, condition, trueBranch, falseBranch);
 }
 
-uint32_t SPIRVEmitter::processBufferLoad(const Expr *object,
-                                         const Expr *location) {
+uint32_t SPIRVEmitter::processBufferTextureLoad(const Expr *object,
+                                                const Expr *location,
+                                                uint32_t constOffset,
+                                                uint32_t varOffset) {
   // Loading for Buffer and RWBuffer translates to an OpImageFetch.
   // The result type of an OpImageFetch must be a vec4 of float or int.
   const auto type = object->getType();
-  const uint32_t objectId = doExpr(object);
+  assert(TypeTranslator::isBuffer(type) || TypeTranslator::isRWBuffer(type) ||
+         TypeTranslator::isTexture(type));
+  const bool doFetch =
+      TypeTranslator::isBuffer(type) || TypeTranslator::isTexture(type);
+  const uint32_t objectId = loadIfGLValue(object);
   const uint32_t locationId = doExpr(location);
+
+  // For Buffers/RWBuffers, the location is just an int, which should be used as
+  // the coordinate argument. Textures require an additional processing.
+  uint32_t coordinate = locationId, lod = 0;
+  if (TypeTranslator::isTexture(type)) {
+    // The location parameter is a vector that consists of both the coordinate
+    // and the mipmap level (via the last vector element). We need to split it
+    // here since the OpImageFetch SPIR-V instruction encodes them as separate
+    // arguments.
+    splitVecLastElement(location->getType(), locationId, &coordinate, &lod);
+  }
+
   const auto sampledType = hlsl::GetHLSLResourceResultType(type);
   QualType elemType = sampledType;
   uint32_t elemCount = 1;
@@ -1358,22 +1376,21 @@ uint32_t SPIRVEmitter::processBufferLoad(const Expr *object,
 
   // Always need to fetch 4 elements.
   const uint32_t fetchTypeId = theBuilder.getVecType(elemTypeId, 4u);
-  const uint32_t imageFetchResult =
-      theBuilder.createImageFetch(fetchTypeId, objectId, locationId, 0, 0, 0);
+  const uint32_t texel = theBuilder.createImageFetchOrRead(
+      doFetch, fetchTypeId, objectId, coordinate, lod, constOffset, varOffset);
 
   // For the case of buffer elements being vec4, there's no need for extraction
   // and composition.
   switch (elemCount) {
   case 1:
-    return theBuilder.createCompositeExtract(elemTypeId, imageFetchResult, {0});
+    return theBuilder.createCompositeExtract(elemTypeId, texel, {0});
   case 2:
-    return theBuilder.createVectorShuffle(resultTypeId, imageFetchResult,
-                                          imageFetchResult, {0, 1});
+    return theBuilder.createVectorShuffle(resultTypeId, texel, texel, {0, 1});
   case 3:
-    return theBuilder.createVectorShuffle(resultTypeId, imageFetchResult,
-                                          imageFetchResult, {0, 1, 2});
+    return theBuilder.createVectorShuffle(resultTypeId, texel, texel,
+                                          {0, 1, 2});
   case 4:
-    return imageFetchResult;
+    return texel;
   }
   llvm_unreachable("Element count of a vector must be 1, 2, 3, or 4.");
 }
@@ -1622,6 +1639,7 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
       }
 
       const auto *object = expr->getImplicitObjectArgument();
+      const auto *location = expr->getArg(0);
       const auto objectType = object->getType();
 
       if (typeTranslator.isRWByteAddressBuffer(objectType) ||
@@ -1633,26 +1651,17 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
 
       if (TypeTranslator::isBuffer(objectType) ||
           TypeTranslator::isRWBuffer(objectType))
-        return processBufferLoad(expr->getImplicitObjectArgument(),
-                                 expr->getArg(0));
-
-      const uint32_t image = loadIfGLValue(object);
-
-      // The location parameter is a vector that consists of both the coordinate
-      // and the mipmap level (via the last vector element). We need to split it
-      // here since the OpImageFetch SPIR-V instruction encodes them as separate
-      // arguments.
-      const uint32_t location = doExpr(expr->getArg(0));
-      uint32_t coordinate = 0, lod = 0;
-      splitVecLastElement(expr->getArg(0)->getType(), location, &coordinate,
-                          &lod);
-
-      // .Load() has a second optional paramter for offset.
-      uint32_t constOffset = 0, varOffset = 0;
-      handleOffset(expr, 1, &constOffset, &varOffset);
-
-      return theBuilder.createImageFetch(retType, image, coordinate, lod,
-                                         constOffset, varOffset);
+        return processBufferTextureLoad(object, location);
+
+      if (TypeTranslator::isTexture(objectType)) {
+        // .Load() has a second optional paramter for offset.
+        uint32_t constOffset = 0, varOffset = 0;
+        handleOffset(expr, 1, &constOffset, &varOffset);
+        return processBufferTextureLoad(object, location, constOffset,
+                                        varOffset);
+      }
+      emitError("Load() is not implemented for the given object type.");
+      return 0;
     }
     case IntrinsicOp::MOP_Load2: {
       return processByteAddressBufferLoadStore(expr, 2, /*doStore*/ false);
@@ -1693,7 +1702,7 @@ uint32_t SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
     const Expr *indexExpr = nullptr;
 
     if (isBufferIndexing(expr, &baseExpr, &indexExpr)) {
-      return processBufferLoad(baseExpr, indexExpr);
+      return processBufferTextureLoad(baseExpr, indexExpr);
     }
   }
 

+ 6 - 4
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -419,10 +419,12 @@ private:
   uint32_t processByteAddressBufferLoadStore(const CXXMemberCallExpr *,
                                              uint32_t numWords, bool doStore);
 
-  /// \brief Loads one element from the given Buffer/RWBuffer object at the
-  /// given location. The type of the loaded element matches the type in the
-  /// declaration for the (RW)Buffer object.
-  uint32_t processBufferLoad(const Expr *object, const Expr *address);
+  /// \brief Loads one element from the given Buffer/RWBuffer/Texture object at
+  /// the given location. The type of the loaded element matches the type in the
+  /// declaration for the Buffer/Texture object.
+  uint32_t processBufferTextureLoad(const Expr *object, const Expr *address,
+                                    uint32_t constOffset = 0,
+                                    uint32_t varOffst = 0);
 
   /// \brief Generates an OpAccessChain instruction for the given
   /// (RW)StructuredBuffer.Load() method call.

+ 13 - 0
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -225,6 +225,19 @@ bool TypeTranslator::isBuffer(QualType type) {
   return false;
 }
 
+bool TypeTranslator::isTexture(QualType type) {
+  if (const auto *rt = type->getAs<RecordType>()) {
+    const auto name = rt->getDecl()->getName();
+    if (name == "Texture1D" || name == "Texture1DArray" ||
+        name == "Texture2D" || name == "Texture2DArray" ||
+        name == "Texture2DMS" || name == "Texture2DMSArray" ||
+        name == "TextureCube" || name == "TextureCubeArray" ||
+        name == "Texture3D")
+      return true;
+  }
+  return false;
+}
+
 bool TypeTranslator::isVectorType(QualType type, QualType *elemType,
                                   uint32_t *elemCount) {
   bool isVec = false;

+ 3 - 0
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -66,6 +66,9 @@ public:
   /// \brief Returns true if the given type is the HLSL RWBuffer type.
   static bool isRWBuffer(QualType type);
 
+  /// \brief Returns true if the given type is an HLSL Texture type.
+  static bool isTexture(QualType);
+
   /// \brief Returns true if the given type will be translated into a SPIR-V
   /// scalar type. This includes normal scalar types, vectors of size 1, and
   /// 1x1 matrices. If scalarType is not nullptr, writes the scalar type to

+ 24 - 12
tools/clang/test/CodeGenSPIRV/buffer.load.hlsl

@@ -16,51 +16,63 @@ RWBuffer<float4> float4buf;
 void main() {
   int address;
 
-// CHECK:      [[f1:%\d+]] = OpImageFetch %v4int %intbuf {{%\d+}} None
+// CHECK:      [[img1:%\d+]] = OpLoad %type_buffer_image %intbuf
+// CHECK:      [[f1:%\d+]] = OpImageFetch %v4int [[img1]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpCompositeExtract %int [[f1]] 0
   int int1 = intbuf.Load(address);
 
-// CHECK:      [[f2:%\d+]] = OpImageFetch %v4uint %uintbuf {{%\d+}} None
+// CHECK:      [[img2:%\d+]] = OpLoad %type_buffer_image_0 %uintbuf
+// CHECK:      [[f2:%\d+]] = OpImageFetch %v4uint [[img2]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpCompositeExtract %uint [[f2]] 0
   uint uint1 = uintbuf.Load(address);
 
-// CHECK:      [[f3:%\d+]] = OpImageFetch %v4float %floatbuf {{%\d+}} None
+// CHECK:      [[img3:%\d+]] = OpLoad %type_buffer_image_1 %floatbuf
+// CHECK:      [[f3:%\d+]] = OpImageFetch %v4float [[img3]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpCompositeExtract %float [[f3]] 0
   float float1 = floatbuf.Load(address);
 
-// CHECK:      [[f4:%\d+]] = OpImageFetch %v4int %int2buf {{%\d+}} None
+// CHECK:      [[img4:%\d+]] = OpLoad %type_buffer_image_2 %int2buf
+// CHECK:      [[f4:%\d+]] = OpImageRead %v4int [[img4]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2int [[f4]] [[f4]] 0 1
   int2 int2 = int2buf.Load(address);
 
-// CHECK:      [[f5:%\d+]] = OpImageFetch %v4uint %uint2buf {{%\d+}} None
+// CHECK:      [[img5:%\d+]] = OpLoad %type_buffer_image_3 %uint2buf
+// CHECK:      [[f5:%\d+]] = OpImageRead %v4uint [[img5]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2uint [[f5]] [[f5]] 0 1
   uint2 uint2 = uint2buf.Load(address);
 
-// CHECK:      [[f6:%\d+]] = OpImageFetch %v4float %float2buf {{%\d+}} None
+// CHECK:      [[img6:%\d+]] = OpLoad %type_buffer_image_4 %float2buf
+// CHECK:      [[f6:%\d+]] = OpImageRead %v4float [[img6]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2float [[f6]] [[f6]] 0 1
   float2 float2 = float2buf.Load(address);
 
-// CHECK:      [[f7:%\d+]] = OpImageFetch %v4int %int3buf {{%\d+}} None
+// CHECK:      [[img7:%\d+]] = OpLoad %type_buffer_image_5 %int3buf
+// CHECK:      [[f7:%\d+]] = OpImageFetch %v4int [[img7]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3int [[f7]] [[f7]] 0 1 2
   int3 int3 = int3buf.Load(address);
 
-// CHECK:      [[f8:%\d+]] = OpImageFetch %v4uint %uint3buf {{%\d+}} None
+// CHECK:      [[img8:%\d+]] = OpLoad %type_buffer_image_6 %uint3buf
+// CHECK:      [[f8:%\d+]] = OpImageFetch %v4uint [[img8]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3uint [[f8]] [[f8]] 0 1 2
   uint3 uint3 = uint3buf.Load(address);
 
-// CHECK:      [[f9:%\d+]] = OpImageFetch %v4float %float3buf {{%\d+}} None
+// CHECK:      [[img9:%\d+]] = OpLoad %type_buffer_image_7 %float3buf
+// CHECK:      [[f9:%\d+]] = OpImageFetch %v4float [[img9]] {{%\d+}} None
 // CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3float [[f9]] [[f9]] 0 1 2
   float3 float3 = float3buf.Load(address);
 
-// CHECK:      {{%\d+}} = OpImageFetch %v4int %int4buf {{%\d+}} None
+// CHECK:      [[img10:%\d+]] = OpLoad %type_buffer_image_8 %int4buf
+// CHECK:      {{%\d+}} = OpImageRead %v4int [[img10]] {{%\d+}} None
 // CHECK-NEXT: OpStore %int4 {{%\d+}}
   int4 int4 = int4buf.Load(address);
 
-// CHECK:      {{%\d+}} = OpImageFetch %v4uint %uint4buf {{%\d+}} None
+// CHECK:      [[img11:%\d+]] = OpLoad %type_buffer_image_9 %uint4buf
+// CHECK:      {{%\d+}} = OpImageRead %v4uint [[img11]] {{%\d+}} None
 // CHECK-NEXT: OpStore %uint4 {{%\d+}}
   uint4 uint4 = uint4buf.Load(address);
 
-// CHECK:      {{%\d+}} = OpImageFetch %v4float %float4buf {{%\d+}} None
+// CHECK:      [[img12:%\d+]] = OpLoad %type_buffer_image_10 %float4buf
+// CHECK:      {{%\d+}} = OpImageRead %v4float [[img12]] {{%\d+}} None
 // CHECK-NEXT: OpStore %float4 {{%\d+}}
   float4 float4 = float4buf.Load(address);
 }

+ 24 - 12
tools/clang/test/CodeGenSPIRV/op.buffer.access.hlsl

@@ -16,60 +16,72 @@ RWBuffer<float4> float4buf;
 void main() {
   int address;
 
-// CHECK:      [[f1:%\d+]] = OpImageFetch %v4int %intbuf {{%\d+}} None
+// CHECK:      [[img1:%\d+]] = OpLoad %type_buffer_image %intbuf
+// CHECK:      [[f1:%\d+]] = OpImageFetch %v4int [[img1]] {{%\d+}} None
 // CHECK-NEXT: [[r1:%\d+]] = OpCompositeExtract %int [[f1]] 0
 // CHECK-NEXT: OpStore %int1 [[r1]]
   int int1 = intbuf[address];
 
-// CHECK:      [[f2:%\d+]] = OpImageFetch %v4uint %uintbuf {{%\d+}} None
+// CHECK:      [[img2:%\d+]] = OpLoad %type_buffer_image_0 %uintbuf
+// CHECK:      [[f2:%\d+]] = OpImageFetch %v4uint [[img2]] {{%\d+}} None
 // CHECK-NEXT: [[r2:%\d+]] = OpCompositeExtract %uint [[f2]] 0
 // CHECK-NEXT: OpStore %uint1 [[r2]]
   uint uint1 = uintbuf[address];
 
-// CHECK:      [[f3:%\d+]] = OpImageFetch %v4float %floatbuf {{%\d+}} None
+// CHECK:      [[img3:%\d+]] = OpLoad %type_buffer_image_1 %floatbuf
+// CHECK:      [[f3:%\d+]] = OpImageFetch %v4float [[img3]] {{%\d+}} None
 // CHECK-NEXT: [[r3:%\d+]] = OpCompositeExtract %float [[f3]] 0
 // CHECK-NEXT: OpStore %float1 [[r3]]
   float float1 = floatbuf[address];
 
-// CHECK:      [[f4:%\d+]] = OpImageFetch %v4int %int2buf {{%\d+}} None
+// CHECK:      [[img4:%\d+]] = OpLoad %type_buffer_image_2 %int2buf
+// CHECK:      [[f4:%\d+]] = OpImageRead %v4int [[img4]] {{%\d+}} None
 // CHECK-NEXT: [[r4:%\d+]] = OpVectorShuffle %v2int [[f4]] [[f4]] 0 1
 // CHECK-NEXT: OpStore %int2 [[r4]]
   int2 int2 = int2buf[address];
 
-// CHECK:      [[f5:%\d+]] = OpImageFetch %v4uint %uint2buf {{%\d+}} None
+// CHECK:      [[img5:%\d+]] = OpLoad %type_buffer_image_3 %uint2buf
+// CHECK:      [[f5:%\d+]] = OpImageRead %v4uint [[img5]] {{%\d+}} None
 // CHECK-NEXT: [[r5:%\d+]] = OpVectorShuffle %v2uint [[f5]] [[f5]] 0 1
 // CHECK-NEXT: OpStore %uint2 [[r5]]
   uint2 uint2 = uint2buf[address];
 
-// CHECK:      [[f6:%\d+]] = OpImageFetch %v4float %float2buf {{%\d+}} None
+// CHECK:      [[img6:%\d+]] = OpLoad %type_buffer_image_4 %float2buf
+// CHECK:      [[f6:%\d+]] = OpImageRead %v4float [[img6]] {{%\d+}} None
 // CHECK-NEXT: [[r6:%\d+]] = OpVectorShuffle %v2float [[f6]] [[f6]] 0 1
 // CHECK-NEXT: OpStore %float2 [[r6]]
   float2 float2 = float2buf[address];
 
-// CHECK:      [[f7:%\d+]] = OpImageFetch %v4int %int3buf {{%\d+}} None
+// CHECK:      [[img7:%\d+]] = OpLoad %type_buffer_image_5 %int3buf
+// CHECK:      [[f7:%\d+]] = OpImageFetch %v4int [[img7]] {{%\d+}} None
 // CHECK-NEXT: [[r7:%\d+]] = OpVectorShuffle %v3int [[f7]] [[f7]] 0 1 2
 // CHECK-NEXT: OpStore %int3 [[r7]]
   int3 int3 = int3buf[address];
 
-// CHECK:      [[f8:%\d+]] = OpImageFetch %v4uint %uint3buf {{%\d+}} None
+// CHECK:      [[img8:%\d+]] = OpLoad %type_buffer_image_6 %uint3buf
+// CHECK:      [[f8:%\d+]] = OpImageFetch %v4uint [[img8]] {{%\d+}} None
 // CHECK-NEXT: [[r8:%\d+]] = OpVectorShuffle %v3uint [[f8]] [[f8]] 0 1 2
 // CHECK-NEXT: OpStore %uint3 [[r8]]
   uint3 uint3 = uint3buf[address];
 
-// CHECK:      [[f9:%\d+]] = OpImageFetch %v4float %float3buf {{%\d+}} None
+// CHECK:      [[img9:%\d+]] = OpLoad %type_buffer_image_7 %float3buf
+// CHECK:      [[f9:%\d+]] = OpImageFetch %v4float [[img9]] {{%\d+}} None
 // CHECK-NEXT: [[r9:%\d+]] = OpVectorShuffle %v3float [[f9]] [[f9]] 0 1 2
 // CHECK-NEXT: OpStore %float3 [[r9]]
   float3 float3 = float3buf[address];
 
-// CHECK:      [[r10:%\d+]] = OpImageFetch %v4int %int4buf {{%\d+}} None
+// CHECK:      [[img10:%\d+]] = OpLoad %type_buffer_image_8 %int4buf
+// CHECK:      [[r10:%\d+]] = OpImageRead %v4int [[img10]] {{%\d+}} None
 // CHECK-NEXT: OpStore %int4 [[r10]]
   int4 int4 = int4buf[address];
 
-// CHECK:      [[r11:%\d+]] = OpImageFetch %v4uint %uint4buf {{%\d+}} None
+// CHECK:      [[img11:%\d+]] = OpLoad %type_buffer_image_9 %uint4buf
+// CHECK:      [[r11:%\d+]] = OpImageRead %v4uint [[img11]] {{%\d+}} None
 // CHECK-NEXT: OpStore %uint4 [[r11]]
   uint4 uint4 = uint4buf[address];
 
-// CHECK:      [[r12:%\d+]] = OpImageFetch %v4float %float4buf {{%\d+}} None
+// CHECK:      [[img12:%\d+]] = OpLoad %type_buffer_image_10 %float4buf
+// CHECK:      [[r12:%\d+]] = OpImageRead %v4float [[img12]] {{%\d+}} None
 // CHECK-NEXT: OpStore %float4 [[r12]]
   float4 float4 = float4buf[address];
 }

+ 19 - 3
tools/clang/test/CodeGenSPIRV/texture.load.hlsl

@@ -5,18 +5,22 @@ Texture2D <float4> t2 : register(t2);
 Texture3D <float4> t3 : register(t3);
 // .Load() does not support TextureCube.
 
+Texture1D <float> t4 : register(t4);
+Texture2D <int2>  t5 : register(t5);
+Texture3D <uint3> t6 : register(t6);
+
 // CHECK: OpCapability ImageGatherExtended
 
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
-// CHECK: [[v4ic:%\d+]] = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
 // CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
+// CHECK: [[v4ic:%\d+]] = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
 
 float4 main(int3 location: A, int offset: B) : SV_Target {
 
-// CHECK:         [[t1:%\d+]] = OpLoad %type_1d_image %t1
+// CHECK:     [[offset:%\d+]] = OpLoad %int %offset
+// CHECK-NEXT:    [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK-NEXT: [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
 // CHECK-NEXT:   [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
-// CHECK-NEXT:[[offset:%\d+]] = OpLoad %int %offset
 // CHECK-NEXT:       {{%\d+}} = OpImageFetch %v4float [[t1]] [[coord]] Lod|Offset [[lod]] [[offset]]
     float4 val1 = t1.Load(int2(1, 2), offset);
 
@@ -33,5 +37,17 @@ float4 main(int3 location: A, int offset: B) : SV_Target {
 // CHECK-NEXT:       {{%\d+}} = OpImageFetch %v4float [[t3]] [[coord]] Lod|ConstOffset [[lod]] [[v3ic]]
     float4 val3 = t3.Load(int4(1, 2, 3, 4), 3);
 
+// CHECK:      [[f4:%\d+]] = OpImageFetch %v4float {{%\d+}} {{%\d+}} Lod|Offset {{%\d+}} {{%\d+}}
+// CHECK-NEXT:    {{%\d+}} = OpCompositeExtract %float [[f4]] 0
+    float val4 = t4.Load(int2(1,2), offset);
+
+// CHECK:      [[f5:%\d+]] = OpImageFetch %v4int {{%\d+}} {{%\d+}} Lod|ConstOffset {{%\d+}} {{%\d+}}
+// CHECK-NEXT:    {{%\d+}} = OpVectorShuffle %v2int [[f5]] [[f5]] 0 1
+    int2  val5 = t5.Load(location, int2(1,2));
+
+// CHECK:      [[f6:%\d+]] = OpImageFetch %v4uint {{%\d+}} {{%\d+}} Lod|ConstOffset {{%\d+}} {{%\d+}}
+// CHECK-NEXT:    {{%\d+}} = OpVectorShuffle %v3uint [[f6]] [[f6]] 0 1 2
+    uint3 val6 = t6.Load(int4(1, 2, 3, 4), 3);
+
     return 1.0;
 }