Browse Source

[spirv] Support Load methods that take Status arg. (#905)

Ehsan 7 years ago
parent
commit
1d4509f35d

+ 61 - 25
docs/SPIR-V.rst

@@ -1611,6 +1611,8 @@ Since Buffers are represented as ``OpTypeImage`` with ``Sampled`` set to 1
 operation. The return value of ``OpImageFetch`` is always a four-component
 vector; so proper additional instructions are generated to truncate the vector
 and return the desired number of elements.
+If an output unsigned integer ``status`` argument is present, ``OpImageSparseFetch``
+is used instead. The resulting SPIR-V ``Residency Code`` will be written to ``status``.
 
 ``operator[]``
 ++++++++++++++
@@ -1628,7 +1630,8 @@ Since Buffers are represented as ``OpTypeImage`` with dimension of ``Buffer``,
 +++++++++++
 Since RWBuffers are represented as ``OpTypeImage`` with ``Sampled`` set to 2
 (meaning to be used without a sampler), ``OpImageRead`` is used to perform this
-operation.
+operation. If an output unsigned integer ``status`` argument is present, ``OpImageSparseRead``
+is used instead. The resulting SPIR-V ``Residency Code`` will be written to ``status``.
 
 ``operator[]``
 ++++++++++++++
@@ -1780,8 +1783,8 @@ for that texture type.
 Common texture methods
 ~~~~~~~~~~~~~~~~~~~~~~
 
-``.Sample(sampler, location[, offset])``
-++++++++++++++++++++++++++++++++++++++++
+``.Sample(sampler, location[, offset][, clamp][, Status])``
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
 
@@ -1790,12 +1793,15 @@ since texture types are represented as ``OpTypeImage``. An ``OpSampledImage`` is
 created based on the ``sampler`` passed to the function. The resulting sampled
 image and the ``location`` passed to the function are used as arguments to
 ``OpImageSampleImplicitLod``, with the optional ``offset`` tranlated into
-addtional SPIR-V image operands ``ConstOffset`` or ``Offset`` on it.
+addtional SPIR-V image operands ``ConstOffset`` or ``Offset`` on it. The optional
+``clamp`` argument will be translated to the ``MinLod`` image operand.
 
-The overload with the status parameter are not supported.
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseSampleImplicitLod`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
 
-``.SampleLevel(sampler, location, lod[, offset])``
-++++++++++++++++++++++++++++++++++++++++++++++++++
+``.SampleLevel(sampler, location, lod[, offset][, Status])``
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
 
@@ -1807,21 +1813,26 @@ is attached to the instruction as an SPIR-V image operands ``Lod``. The optional
 ``offset`` is also tranlated into addtional SPIR-V image operands ``ConstOffset``
 or ``Offset`` on it.
 
-The overload with the status parameter are not supported.
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseSampleExplicitLod`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
 
-``.SampleGrad(sampler, location, ddx, ddy[, offset])``
-++++++++++++++++++++++++++++++++++++++++++++++++++++++
+``.SampleGrad(sampler, location, ddx, ddy[, offset][, clamp][, Status])``
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
 
 Similarly to ``.SampleLevel``, the ``ddx`` and ``ddy`` parameter are attached to
 the ``OpImageSampleExplicitLod`` instruction as an SPIR-V image operands
-``Grad``.
+``Grad``. The optional ``clamp`` argument will be translated into the ``MinLod``
+image operand.
 
-The overload with the status parameter are not supported.
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseSampleExplicitLod`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
 
-``.SampleBias(sampler, location, bias[, offset])``
-++++++++++++++++++++++++++++++++++++++++++++++++++
+``.SampleBias(sampler, location, bias[, offset][, clamp][, Status])``
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
 
@@ -1829,18 +1840,24 @@ The translation is similar to ``.Sample()``, with the ``bias`` parameter
 attached to the ``OpImageSampleImplicitLod`` instruction as an SPIR-V image
 operands ``Bias``.
 
-The overload with the status parameter are not supported.
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseSampleImplicitLod`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
 
-``.SampleCmp(sampler, location, comparator[, offset])``
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++
+``.SampleCmp(sampler, location, comparator[, offset][, clamp][, Status])``
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Not available to ``Texture3D``, ``Texture2DMS``, and ``Texture2DMSArray``.
 
 The translation is similar to ``.Sample()``, but the
 ``OpImageSampleDrefImplicitLod`` instruction are used.
 
-``.SampleCmpLevelZero(sampler, location, comparator[, offset])``
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseSampleDrefImplicitLod`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
+
+``.SampleCmpLevelZero(sampler, location, comparator[, offset][, Status])``
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Not available to ``Texture3D``, ``Texture2DMS``, and ``Texture2DMSArray``.
 
@@ -1848,6 +1865,10 @@ The translation is similar to ``.Sample()``, but the
 ``OpImageSampleDrefExplicitLod`` instruction are used, with the additional
 ``Lod`` image operands set to 0.0.
 
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseSampleDrefExplicitLod`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
+
 ``.Gather()``
 +++++++++++++
 
@@ -1857,7 +1878,9 @@ Available to ``Texture2D``, ``Texture2DArray``, ``TextureCube``, and
 The translation is similar to ``.Sample()``, but the ``OpImageGather``
 instruction is used, with component setting to 0.
 
-The overload with the status parameter are not supported.
+If an output unsigned integer ``status`` argument is present,
+``OpImageSparseGather`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
 
 ``.GatherRed()``, ``.GatherGreen()``, ``.GatherBlue()``, ``.GatherAlpha()``
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -1873,7 +1896,9 @@ There are a few overloads for these functions:
 - For those overloads taking 4 offset parameters, those offset parameters will
   be conveyed as an additional ``ConstOffsets`` image operands to the
   instruction. So those offset parameters must all be constant values.
-- Those overloads with the status parameter are not supported.
+- For those overloads with the ``status`` parameter, ``OpImageSparseGather``
+  is used instead, and the resulting SPIR-V ``Residency Code`` will be
+  written to ``status``.
 
 ``.GatherCmp()``
 ++++++++++++++++
@@ -1884,7 +1909,10 @@ Available to ``Texture2D``, ``Texture2DArray``, ``TextureCube``, and
 The translation is similar to ``.Sample()``, but the ``OpImageDrefGather``
 instruction is used.
 
-The overload with the status parameter are not supported.
+For the overload with the output unsigned integer ``status`` argument,
+``OpImageSparseDrefGather`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
+
 
 ``.GatherCmpRed()``
 +++++++++++++++++++
@@ -1894,8 +1922,6 @@ Available to ``Texture2D``, ``Texture2DArray``, ``TextureCube``, and
 
 The translation is the same as ``.GatherCmp()``.
 
-The overload with the status parameter are not supported.
-
 ``.Load(location[, sampleIndex][, offset])``
 ++++++++++++++++++++++++++++++++++++++++++++
 
@@ -1908,7 +1934,9 @@ The return value of ``OpImageFetch`` is always a four-component vector; so
 proper additional instructions are generated to truncate the vector and return
 the desired number of elements.
 
-The overload with the status parameter are not supported.
+For the overload with the output unsigned integer ``status`` argument,
+``OpImageSparseFetch`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
 
 ``operator[]``
 ++++++++++++++
@@ -2034,6 +2062,10 @@ Since read-write texture types are represented as ``OpTypeImage`` with
 ``Sampled`` set to 2 (meaning to be used without a sampler), ``OpImageRead`` is
 used to perform this operation.
 
+For the overload with the output unsigned integer ``status`` argument,
+``OpImageSparseRead`` is used instead. The resulting SPIR-V
+``Residency Code`` will be written to ``status``.
+
 ``operator[]``
 ++++++++++++++
 Using ``operator[]`` for reading is handled similarly as ``.Load()``, while for
@@ -2299,3 +2331,7 @@ either because of no Vulkan equivalents at the moment, or because of deprecation
   sample currently being processed.) The compiler will emit an error.
 * ``SV_InnerCoverage`` semantic does not have a Vulkan equivalent. The compiler
   will emit an error.
+* Since ``StructuredBuffer``, ``RWStructuredBuffer``, ``ByteAddressBuffer``, and
+  ``RWByteAddressBuffer`` are not represented as image types in SPIR-V, using the
+  output unsigned integer ``status`` argument in their ``Load*`` methods is not
+  supported. Using these methods with the ``status`` argument will cause a compiler error.

+ 8 - 0
tools/clang/include/clang/SPIRV/InstBuilder.h

@@ -865,6 +865,14 @@ public:
                 llvm::Optional<spv::ImageOperandsMask> image_operands,
                 bool is_explicit, bool is_sparse);
 
+  // All-in-one method for creating different types of
+  // OpImageRead*/OpImageFetch*.
+  InstBuilder &
+  opImageFetchRead(uint32_t result_type, uint32_t result_id, uint32_t image,
+                   uint32_t coordinate,
+                   llvm::Optional<spv::ImageOperandsMask> image_operands,
+                   bool is_fetch, bool is_sparse);
+
   // Methods for supplying additional parameters.
   InstBuilder &fPFastMathMode(spv::FPFastMathModeMask);
   InstBuilder &fPRoundingMode(spv::FPRoundingMode);

+ 6 - 1
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -191,11 +191,16 @@ public:
   /// 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.
+  ///
+  /// If residencyCodeId is not zero, the sparse version of the instructions
+  /// will be used, and the SPIR-V instruction for storing the resulting
+  /// residency code will also be emitted.
   uint32_t createImageFetchOrRead(bool doImageFetch, uint32_t texelType,
                                   QualType imageType, uint32_t image,
                                   uint32_t coordinate, uint32_t lod,
                                   uint32_t constOffset, uint32_t varOffset,
-                                  uint32_t constOffsets, uint32_t sample);
+                                  uint32_t constOffsets, uint32_t sample,
+                                  uint32_t residencyCodeId);
 
   /// \brief Creates SPIR-V instructions for writing to the given image.
   void createImageWrite(QualType imageType, uint32_t imageId, uint32_t coordId,

+ 22 - 0
tools/clang/lib/SPIRV/InstBuilderManual.cpp

@@ -112,6 +112,28 @@ InstBuilder &InstBuilder::opImageSample(
   return *this;
 }
 
+InstBuilder &InstBuilder::opImageFetchRead(
+    uint32_t result_type, uint32_t result_id, uint32_t image,
+    uint32_t coordinate, llvm::Optional<spv::ImageOperandsMask> image_operands,
+    bool is_fetch, bool is_sparse) {
+  spv::Op op =
+      is_fetch
+          ? (is_sparse ? spv::Op::OpImageSparseFetch : spv::Op::OpImageFetch)
+          : (is_sparse ? spv::Op::OpImageSparseRead : spv::Op::OpImageRead);
+
+  TheInst.emplace_back(static_cast<uint32_t>(op));
+  TheInst.emplace_back(result_type);
+  TheInst.emplace_back(result_id);
+  TheInst.emplace_back(image);
+  TheInst.emplace_back(coordinate);
+  if (image_operands.hasValue()) {
+    const auto &val = image_operands.getValue();
+    encodeImageOperands(val);
+  }
+
+  return *this;
+}
+
 void InstBuilder::encodeString(std::string value) {
   const auto &words = string::encodeSPIRVString(value);
   TheInst.insert(TheInst.end(), words.begin(), words.end());

+ 21 - 7
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -429,30 +429,44 @@ void ModuleBuilder::createImageWrite(QualType imageType, uint32_t imageId,
 uint32_t ModuleBuilder::createImageFetchOrRead(
     bool doImageFetch, uint32_t texelType, QualType imageType, uint32_t image,
     uint32_t coordinate, uint32_t lod, uint32_t constOffset, uint32_t varOffset,
-    uint32_t constOffsets, uint32_t sample) {
+    uint32_t constOffsets, uint32_t sample, uint32_t residencyCodeId) {
   assert(insertPoint && "null insert point");
 
-  // TODO: Update ImageFetch/ImageRead to accept minLod if necessary.
   llvm::SmallVector<uint32_t, 2> params;
   const auto mask =
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
           /*bias*/ 0, lod, std::make_pair(0, 0), constOffset, varOffset,
           constOffsets, sample, /*minLod*/ 0, &params));
 
-  const uint32_t texelId = theContext.takeNextId();
-  if (doImageFetch) {
-    instBuilder.opImageFetch(texelType, texelId, image, coordinate, mask);
-  } else {
+  const bool isSparse = (residencyCodeId != 0);
+  uint32_t retType = texelType;
+  if (isSparse) {
+    requireCapability(spv::Capability::SparseResidency);
+    retType = getSparseResidencyStructType(texelType);
+  }
+
+  if (!doImageFetch) {
     requireCapability(
         TypeTranslator::getCapabilityForStorageImageReadWrite(imageType));
-    instBuilder.opImageRead(texelType, texelId, image, coordinate, mask);
   }
 
+  uint32_t texelId = theContext.takeNextId();
+  instBuilder.opImageFetchRead(retType, texelId, image, coordinate, mask,
+                               doImageFetch, isSparse);
+
   for (const auto param : params)
     instBuilder.idRef(param);
   instBuilder.x();
   insertPoint->appendInstruction(std::move(constructSite));
 
+  if (isSparse) {
+    // Write the Residency Code
+    const auto status = createCompositeExtract(getUint32Type(), texelId, {0});
+    createStore(residencyCodeId, status);
+    // Extract the real result from the struct
+    texelId = createCompositeExtract(texelType, texelId, {1});
+  }
+
   return texelId;
 }
 

+ 64 - 36
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -2350,11 +2350,9 @@ uint32_t SPIRVEmitter::processTextureGatherCmp(const CXXMemberCallExpr *expr) {
       /*sampleNumber*/ 0, status);
 }
 
-SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(const Expr *object,
-                                                     const uint32_t locationId,
-                                                     uint32_t constOffset,
-                                                     uint32_t varOffset,
-                                                     uint32_t lod) {
+SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
+    const Expr *object, const uint32_t locationId, uint32_t constOffset,
+    uint32_t varOffset, uint32_t lod, uint32_t residencyCode) {
   // 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();
@@ -2393,10 +2391,9 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(const Expr *object,
 
   // OpImageFetch and OpImageRead can only fetch a vector of 4 elements.
   const uint32_t texelTypeId = theBuilder.getVecType(elemTypeId, 4u);
-  const uint32_t texel =
-      theBuilder.createImageFetchOrRead(doFetch, texelTypeId, type, objectId,
-                                        locationId, lod, constOffset, varOffset,
-                                        /*constOffsets*/ 0, sampleNumber);
+  const uint32_t texel = theBuilder.createImageFetchOrRead(
+      doFetch, texelTypeId, type, objectId, locationId, lod, constOffset,
+      varOffset, /*constOffsets*/ 0, sampleNumber, residencyCode);
 
   uint32_t retVal = texel;
   // If the result type is a vec1, vec2, or vec3, some extra processing
@@ -2436,7 +2433,7 @@ SpirvEvalInfo SPIRVEmitter::processByteAddressBufferLoadStore(
            typeTranslator.isByteAddressBuffer(type));
     if (expr->getNumArgs() == 2) {
       emitError(
-          "(RW)ByteAddressBuffer::Load(in address, out status) unimplemented",
+          "(RW)ByteAddressBuffer::Load(in address, out status) not supported",
           expr->getExprLoc());
       return 0;
     }
@@ -2512,8 +2509,9 @@ SpirvEvalInfo SPIRVEmitter::processByteAddressBufferLoadStore(
 SpirvEvalInfo
 SPIRVEmitter::processStructuredBufferLoad(const CXXMemberCallExpr *expr) {
   if (expr->getNumArgs() == 2) {
-    emitError("(RW)StructuredBuffer::Load(int, int) unimplemented",
-              expr->getExprLoc());
+    emitError(
+        "(RW)StructuredBuffer::Load(in location, out status) not supported",
+        expr->getExprLoc());
     return 0;
   }
 
@@ -2644,19 +2642,6 @@ void SPIRVEmitter::handleOffsetInMethodCall(const CXXMemberCallExpr *expr,
     *varOffset = doExpr(expr->getArg(index));
 };
 
-void SPIRVEmitter::handleOptionalOffsetInMethodCall(
-    const CXXMemberCallExpr *expr, uint32_t index, uint32_t *constOffset,
-    uint32_t *varOffset) {
-  *constOffset = *varOffset = 0; // Initialize both first
-
-  if (expr->getNumArgs() == index + 1) { // Has offset argument
-    if (*constOffset = tryToEvaluateAsConst(expr->getArg(index)))
-      return; // Constant offset
-    else
-      *varOffset = doExpr(expr->getArg(index));
-  }
-}
-
 SpirvEvalInfo
 SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
                                          hlsl::IntrinsicOp opcode) {
@@ -3074,12 +3059,38 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
 SpirvEvalInfo
 SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
   // Signature:
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray, Texture3D:
+  // ret Object.Load(int Location
+  //                 [, int Offset]
+  //                 [, uint status]);
+  //
+  // For Texture2DMS and Texture2DMSArray, there is one additional argument:
   // ret Object.Load(int Location
   //                 [, int SampleIndex]
-  //                 [, int Offset]);
+  //                 [, int Offset]
+  //                 [, uint status]);
+  //
+  // For (RW)Buffer, RWTexture1D, RWTexture1DArray, RWTexture2D,
+  // RWTexture2DArray, RWTexture3D: 
+  // ret Object.Load (int Location
+  //                  [, uint status]);
+  //
+  // Note: (RW)ByteAddressBuffer and (RW)StructuredBuffer types also have Load
+  // methods that take an additional Status argument. However, since these types
+  // are not represented as OpTypeImage in SPIR-V, we don't have a way of
+  // figuring out the Residency Code for them. Therefore having the Status
+  // argument for these types is not supported.
+  //
+  // For (RW)ByteAddressBuffer:
+  // ret Object.{Load,Load2,Load3,Load4} (int Location
+  //                                      [, uint status]);
+  //
+  // For (RW)StructuredBuffer:
+  // ret Object.Load (int Location
+  //                  [, uint status]);
+  //
 
   const auto *object = expr->getImplicitObjectArgument();
-  const auto *location = expr->getArg(0);
   const auto objectType = object->getType();
 
   if (typeTranslator.isRWByteAddressBuffer(objectType) ||
@@ -3089,10 +3100,23 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
   if (TypeTranslator::isStructuredBuffer(objectType))
     return processStructuredBufferLoad(expr);
 
+  const auto numArgs = expr->getNumArgs();
+  const auto *location = expr->getArg(0);
+  const bool isTextureMS = TypeTranslator::isTextureMS(objectType);
+  const bool hasStatusArg =
+      expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
+  const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
+
   if (TypeTranslator::isBuffer(objectType) ||
-      TypeTranslator::isRWBuffer(objectType) ||
-      TypeTranslator::isRWTexture(objectType))
-    return processBufferTextureLoad(object, doExpr(location));
+    TypeTranslator::isRWBuffer(objectType) ||
+    TypeTranslator::isRWTexture(objectType))
+    return processBufferTextureLoad(object, doExpr(location), /*constOffset*/ 0,
+                                    /*varOffset*/ 0, /*lod*/ 0,
+                                    /*residencyCode*/ status);
+
+  // Subtract 1 for status (if it exists), and 1 for sampleIndex (if it exists),
+  // and 1 for location.
+  const bool hasOffsetArg = numArgs - hasStatusArg - isTextureMS - 1 > 0;
 
   if (TypeTranslator::isTexture(objectType)) {
     // .Load() has a second optional paramter for offset.
@@ -3100,12 +3124,13 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
     uint32_t constOffset = 0, varOffset = 0;
     uint32_t coordinate = locationId, lod = 0;
 
-    if (TypeTranslator::isTextureMS(objectType)) {
+    if (isTextureMS) {
       // SampleIndex is only available when the Object is of Texture2DMS or
       // Texture2DMSArray types. Under those cases, Offset will be the third
       // parameter (index 2).
       lod = doExpr(expr->getArg(1));
-      handleOptionalOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
+      if(hasOffsetArg)
+        handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
     } else {
       // For Texture Load() functions, the location parameter is a vector
       // that consists of both the coordinate and the mipmap level (via the
@@ -3114,11 +3139,12 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
       splitVecLastElement(location->getType(), locationId, &coordinate, &lod);
       // For textures other than Texture2DMS(Array), offset should be the
       // second parameter (index 1).
-      handleOptionalOffsetInMethodCall(expr, 1, &constOffset, &varOffset);
+      if(hasOffsetArg)
+        handleOffsetInMethodCall(expr, 1, &constOffset, &varOffset);
     }
 
     return processBufferTextureLoad(object, coordinate, constOffset, varOffset,
-                                    lod);
+                                    lod, status);
   }
   emitError("Load() of the given object type unimplemented",
             object->getExprLoc());
@@ -3158,13 +3184,15 @@ SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
                                ? theBuilder.getConstantUint32(0)
                                : 0;
       return processBufferTextureLoad(baseExpr, doExpr(indexExpr),
-                                      /*constOffset*/ 0, /*varOffset*/ 0, lod);
+                                      /*constOffset*/ 0, /*varOffset*/ 0, lod,
+                                      /*residencyCode*/ 0);
     }
     // .mips[][] or .sample[][] must use the correct slice.
     if (isTextureMipsSampleIndexing(expr, &baseExpr, &indexExpr, &lodExpr)) {
       const uint32_t lod = doExpr(lodExpr);
       return processBufferTextureLoad(baseExpr, doExpr(indexExpr),
-                                      /*constOffset*/ 0, /*varOffset*/ 0, lod);
+                                      /*constOffset*/ 0, /*varOffset*/ 0, lod,
+                                      /*residencyCode*/ 0);
     }
   }
 

+ 5 - 11
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -576,14 +576,6 @@ private:
   void processSwitchStmtUsingIfStmts(const SwitchStmt *switchStmt);
 
 private:
-  /// Handles the optional offset argument in the given method call at the given
-  /// argument index.
-  /// If there exists an offset argument, writes the <result-id> to either
-  /// *constOffset or *varOffset, depending on the constantness of the offset.
-  void handleOptionalOffsetInMethodCall(const CXXMemberCallExpr *expr,
-                                        uint32_t index, uint32_t *constOffset,
-                                        uint32_t *varOffset);
-
   /// Handles the offset argument in the given method call at the given argument
   /// index. Panics if the argument at the given index does not exist. Writes
   /// the <result-id> to either *constOffset or *varOffset, depending on the
@@ -598,10 +590,12 @@ private:
   /// \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.
+  /// If residencyCodeId is not zero,  the SPIR-V instruction for storing the
+  /// resulting residency code will also be emitted.
   SpirvEvalInfo processBufferTextureLoad(const Expr *object, uint32_t location,
-                                         uint32_t constOffset = 0,
-                                         uint32_t varOffst = 0,
-                                         uint32_t lod = 0);
+                                         uint32_t constOffset,
+                                         uint32_t varOffset, uint32_t lod,
+                                         uint32_t residencyCode);
 
   /// \brief Processes .Sample() and .Gather() method calls for texture objects.
   uint32_t processTextureSampleGather(const CXXMemberCallExpr *expr,

+ 42 - 15
tools/clang/test/CodeGenSPIRV/method.buffer.load.hlsl

@@ -15,66 +15,93 @@ RWBuffer<int4> int4buf;
 RWBuffer<uint4> uint4buf;
 RWBuffer<float4> float4buf;
 
+// CHECK: OpCapability SparseResidency
+
+// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float
+
 void main() {
   int address;
 
 // 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);
+  int i1 = intbuf.Load(address);
 
 // 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);
+  uint u1 = uintbuf.Load(address);
 
 // 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);
+  float f1 = floatbuf.Load(address);
 
 // 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);
+  int2 i2 = int2buf.Load(address);
 
 // 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);
+  uint2 u2 = uint2buf.Load(address);
 
 // 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);
+  float2 f2 = float2buf.Load(address);
 
 // 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);
+  int3 i3 = int3buf.Load(address);
 
 // 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);
+  uint3 u3 = uint3buf.Load(address);
 
 // 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);
+  float3 f3 = float3buf.Load(address);
 
 // 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-NEXT: OpStore %i4 {{%\d+}}
+  int4 i4 = int4buf.Load(address);
 
 // 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-NEXT: OpStore %u4 {{%\d+}}
+  uint4 u4 = uint4buf.Load(address);
 
 // 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);
+// CHECK-NEXT: OpStore %f4 {{%\d+}}
+  float4 f4 = float4buf.Load(address);
+
+///////////////////////////////
+// Using the Status argument //  
+///////////////////////////////
+  uint status;
+
+// CHECK:              [[img3:%\d+]] = OpLoad %type_buffer_image_1 %floatbuf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[img3]] {{%\d+}} None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:       [[result:%\d+]] = OpCompositeExtract %float [[v4result]] 0
+// CHECK-NEXT:                         OpStore %r1 [[result]]
+  float  r1 = floatbuf.Load(address, status);  // Test for Buffer
+
+// CHECK:              [[img6:%\d+]] = OpLoad %type_buffer_image_4 %float2buf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct [[img6]] {{%\d+}} None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:       [[result:%\d+]] = OpVectorShuffle %v2float [[v4result]] [[v4result]] 0 1
+// CHECK-NEXT:                         OpStore %r2 [[result]]
+  float2 r2 = float2buf.Load(address, status);  // Test for RWBuffer
 }

+ 51 - 0
tools/clang/test/CodeGenSPIRV/method.rwtexture.load.hlsl

@@ -6,6 +6,12 @@ RWTexture3D<float3> float3buf;
 RWTexture1DArray<float4> float4buf;
 RWTexture2DArray<int3> int3buf;
 
+// CHECK: OpCapability SparseResidency
+
+// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4int
+// CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4uint
+// CHECK: %SparseResidencyStruct_1 = OpTypeStruct %uint %v4float
+
 void main() {
 
 // CHECK:      [[img1:%\d+]] = OpLoad %type_1d_image %intbuf
@@ -36,4 +42,49 @@ void main() {
 // CHECK-NEXT: [[r5:%\d+]] = OpVectorShuffle %v3int [[ret5]] [[ret5]] 0 1 2
 // CHECK-NEXT: OpStore %e [[r5]]
   int3 e   = int3buf.Load(0);
+
+  uint status;
+// CHECK:              [[img1:%\d+]] = OpLoad %type_1d_image %intbuf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct [[img1]] %int_0 None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4int [[structResult]] 1
+// CHECK-NEXT:       [[result:%\d+]] = OpCompositeExtract %int [[v4result]] 0
+// CHECK-NEXT:                         OpStore %a2 [[result]]
+  int    a2 = intbuf.Load(0, status);
+
+// CHECK:              [[img2:%\d+]] = OpLoad %type_2d_image %uint2buf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct_0 [[img2]] {{%\d+}} None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4uint [[structResult]] 1
+// CHECK-NEXT:       [[result:%\d+]] = OpVectorShuffle %v2uint [[v4result]] [[v4result]] 0 1
+// CHECK-NEXT:                         OpStore %b2 [[result]]
+  uint2  b2 = uint2buf.Load(0, status);
+
+// CHECK:              [[img3:%\d+]] = OpLoad %type_3d_image %float3buf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct_1 [[img3]] {{%\d+}} None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:       [[result:%\d+]] = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
+// CHECK-NEXT:                         OpStore %c2 [[result]]
+  float3 c2 = float3buf.Load(0, status);
+
+// CHECK:              [[img4:%\d+]] = OpLoad %type_1d_image_array %float4buf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct_1 [[img4]] {{%\d+}} None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:                         OpStore %d2 [[v4result]]
+  float4 d2 = float4buf.Load(0, status);
+
+// CHECK:              [[img5:%\d+]] = OpLoad %type_2d_image_array %int3buf
+// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct [[img5]] {{%\d+}} None
+// CHECK-NEXT:       [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                         OpStore %status [[status]]
+// CHECK-NEXT:     [[v4result:%\d+]] = OpCompositeExtract %v4int [[structResult]] 1
+// CHECK-NEXT:       [[result:%\d+]] = OpVectorShuffle %v3int [[v4result]] [[v4result]] 0 1 2
+// CHECK-NEXT:                         OpStore %e2 [[result]]
+  int3   e2 = int3buf.Load(0, status);
 }

+ 27 - 4
tools/clang/test/CodeGenSPIRV/texture.array.load.hlsl

@@ -4,7 +4,12 @@ Texture1DArray <float4> t1 : register(t1);
 Texture2DArray <float4> t2 : register(t2);
 // .Load() does not support TextureCubeArray.
 
+// CHECK: OpCapability SparseResidency
+
+// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float
+
 // CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_1 %int_2 %int_3
+// CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
 
 float4 main(int4 location: A) : SV_Target {
 
@@ -21,9 +26,27 @@ float4 main(int4 location: A) : SV_Target {
 // CHECK-NEXT:       {{%\d+}} = OpImageFetch %v4float [[t2]] [[coord]] Lod [[lod]]
     float4 val2 = t2.Load(location);
 
+    uint status;
+// CHECK:            [[coord:%\d+]] = OpVectorShuffle %v2int [[v3ic]] [[v3ic]] 0 1
+// CHECK-NEXT:         [[lod:%\d+]] = OpCompositeExtract %int [[v3ic]] 2
+// CHECK-NEXT:          [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t1]] [[coord]] Lod|ConstOffset [[lod]] %int_10
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:      [[result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:                        OpStore %val3 [[result]]
+    float4 val3 = t1.Load(int3(1, 2, 3), 10, status);
+
+// CHECK:              [[loc:%\d+]] = OpLoad %v4int %location
+// CHECK-NEXT:       [[coord:%\d+]] = OpVectorShuffle %v3int [[loc]] [[loc]] 0 1 2
+// CHECK-NEXT:         [[lod:%\d+]] = OpCompositeExtract %int [[loc]] 3
+// CHECK-NEXT:          [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t2]] [[coord]] Lod|ConstOffset [[lod]] [[v2ic]]
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:      [[result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:                        OpStore %val4 [[result]]
+    float4 val4 = t2.Load(location, int2(1, 2), status);
+    
     return 1.0;
 }
-
-
-
-

+ 68 - 0
tools/clang/test/CodeGenSPIRV/texture.load.hlsl

@@ -13,12 +13,18 @@ Texture2DMS     <float>  t7 : register(t7);
 Texture2DMSArray<float3> t8 : register(t8);
 
 // CHECK: OpCapability ImageGatherExtended
+// CHECK: OpCapability SparseResidency
+
+// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float
+// CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4int
+// CHECK: %SparseResidencyStruct_1 = OpTypeStruct %uint %v4uint
 
 // 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
 
 float4 main(int3 location: A, int offset: B) : SV_Target {
+    uint status;
 
 // CHECK:      [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
 // CHECK-NEXT:   [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
@@ -88,5 +94,67 @@ float4 main(int3 location: A, int offset: B) : SV_Target {
 // CHECK-NEXT:     {{%\d+}} = OpVectorShuffle %v3float [[f81]] [[f81]] 0 1 2
     val8 = t8.Load(pos3, sampleIndex, int2(1,2));
 
+/////////////////////////////////
+/// Using the Status argument ///
+/////////////////////////////////
+
+// CHECK:            [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
+// CHECK-NEXT:         [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
+// CHECK-NEXT:      [[offset:%\d+]] = OpLoad %int %offset
+// CHECK-NEXT:          [[t4:%\d+]] = OpLoad %type_1d_image %t4
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t4]] [[coord]] Lod|Offset [[lod]] [[offset]]
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:    [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:      [[result:%\d+]] = OpCompositeExtract %float [[v4result]] 0
+// CHECK-NEXT:                        OpStore %val14 [[result]]
+    float  val14 = t4.Load(int2(1,2), offset, status);
+
+// CHECK:              [[loc:%\d+]] = OpLoad %v3int %location
+// CHECK-NEXT:       [[coord:%\d+]] = OpVectorShuffle %v2int [[loc]] [[loc]] 0 1
+// CHECK-NEXT:         [[lod:%\d+]] = OpCompositeExtract %int [[loc]] 2
+// CHECK-NEXT:          [[t5:%\d+]] = OpLoad %type_2d_image_0 %t5
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct_0 [[t5]] [[coord]] Lod|ConstOffset [[lod]] [[v2ic]]
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:    [[v4result:%\d+]] = OpCompositeExtract %v4int [[structResult]] 1
+// CHECK-NEXT:      [[result:%\d+]] = OpVectorShuffle %v2int [[v4result]] [[v4result]] 0 1
+// CHECK-NEXT:                        OpStore %val15 [[result]]
+    int2   val15 = t5.Load(location, int2(1,2), status);
+
+// CHECK:            [[coord:%\d+]] = OpVectorShuffle %v3int [[v4ic]] [[v4ic]] 0 1 2
+// CHECK-NEXT:         [[lod:%\d+]] = OpCompositeExtract %int [[v4ic]] 3
+// CHECK-NEXT:          [[t6:%\d+]] = OpLoad %type_3d_image_0 %t6
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct_1 [[t6]] [[coord]] Lod|ConstOffset [[lod]] [[v3ic]]
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:    [[v4result:%\d+]] = OpCompositeExtract %v4uint [[structResult]] 1
+// CHECK-NEXT:      [[result:%\d+]] = OpVectorShuffle %v3uint [[v4result]] [[v4result]] 0 1 2
+// CHECK-NEXT:                        OpStore %val16 [[result]]
+    uint3  val16 = t6.Load(int4(1, 2, 3, 4), 3, status);
+
+// CHECK:             [[pos1:%\d+]] = OpLoad %v2int %pos2
+// CHECK-NEXT:         [[si1:%\d+]] = OpLoad %int %sampleIndex
+// CHECK-NEXT:     [[offset2:%\d+]] = OpLoad %v2int %offset2
+// CHECK-NEXT:         [[t71:%\d+]] = OpLoad %type_2d_image_1 %t7
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t71]] [[pos1]] Offset|Sample [[offset2]] [[si1]]
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:    [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:      [[result:%\d+]] = OpCompositeExtract %float [[v4result]] 0
+// CHECK-NEXT:                        OpStore %val17 [[result]]
+    float  val17 = t7.Load(pos2, sampleIndex, offset2, status);
+
+// CHECK:             [[pos3:%\d+]] = OpLoad %v3int %pos3
+// CHECK-NEXT:         [[si3:%\d+]] = OpLoad %int %sampleIndex
+// CHECK-NEXT:         [[t81:%\d+]] = OpLoad %type_2d_image_array %t8
+// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t81]] [[pos3]] ConstOffset|Sample [[v2ic]] [[si3]]
+// CHECK-NEXT:      [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
+// CHECK-NEXT:                        OpStore %status [[status]]
+// CHECK-NEXT:    [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK-NEXT:      [[result:%\d+]] = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
+// CHECK-NEXT:                        OpStore %val18 [[result]]
+    float3 val18 = t8.Load(pos3, sampleIndex, int2(1,2), status);
+    
     return 1.0;
 }