Pārlūkot izejas kodu

[spirv] Support non-constant offset in texture methods (#579)

Non-constant offset should be translated into the Offset image
operands instead of ConstOffset.
Lei Zhang 8 gadi atpakaļ
vecāks
revīzija
9e67da0c2f

+ 12 - 3
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -157,17 +157,18 @@ public:
                              uint32_t image, uint32_t sampler,
                              uint32_t image, uint32_t sampler,
                              uint32_t coordinate, uint32_t bias, uint32_t lod,
                              uint32_t coordinate, uint32_t bias, uint32_t lod,
                              std::pair<uint32_t, uint32_t> grad,
                              std::pair<uint32_t, uint32_t> grad,
-                             uint32_t offset);
+                             uint32_t constOffset, uint32_t varOffset);
 
 
   /// \brief Creates SPIR-V instructions for fetching the given image.
   /// \brief Creates SPIR-V instructions for fetching the given image.
   uint32_t createImageFetch(uint32_t texelType, uint32_t image,
   uint32_t createImageFetch(uint32_t texelType, uint32_t image,
-                            uint32_t coordinate, uint32_t lod, uint32_t offset);
+                            uint32_t coordinate, uint32_t lod,
+                            uint32_t constOffset, uint32_t varOffset);
 
 
   /// \brief Creates SPIR-V instructions for sampling the given image.
   /// \brief Creates SPIR-V instructions for sampling the given image.
   uint32_t createImageGather(uint32_t texelType, uint32_t imageType,
   uint32_t createImageGather(uint32_t texelType, uint32_t imageType,
                              uint32_t image, uint32_t sampler,
                              uint32_t image, uint32_t sampler,
                              uint32_t coordinate, uint32_t component,
                              uint32_t coordinate, uint32_t component,
-                             uint32_t offset);
+                             uint32_t constOffset, uint32_t varOffset);
 
 
   /// \brief Creates a select operation with the given values for true and false
   /// \brief Creates a select operation with the given values for true and false
   /// cases and returns the <result-id> for the result.
   /// cases and returns the <result-id> for the result.
@@ -314,6 +315,14 @@ private:
   /// \brief Returns the basic block with the given <label-id>.
   /// \brief Returns the basic block with the given <label-id>.
   BasicBlock *getBasicBlock(uint32_t label);
   BasicBlock *getBasicBlock(uint32_t label);
 
 
+  /// \brief Returns the composed ImageOperandsMask from non-zero parameters
+  /// and pushes non-zero parameters to *orderedParams in the expected order.
+  spv::ImageOperandsMask
+  composeImageOperandsMask(uint32_t bias, uint32_t lod,
+                           const std::pair<uint32_t, uint32_t> &grad,
+                           uint32_t constOffset, uint32_t varOffset,
+                           llvm::SmallVectorImpl<uint32_t> *orderedParams);
+
   SPIRVContext &theContext; ///< The SPIR-V context.
   SPIRVContext &theContext; ///< The SPIR-V context.
   SPIRVModule theModule;    ///< The module under building.
   SPIRVModule theModule;    ///< The module under building.
 
 

+ 3 - 2
tools/clang/include/clang/SPIRV/Structure.h

@@ -30,6 +30,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringRef.h"
 
 
 namespace clang {
 namespace clang {
@@ -327,7 +328,7 @@ private:
 
 
 private:
 private:
   Header header; ///< SPIR-V module header.
   Header header; ///< SPIR-V module header.
-  std::vector<spv::Capability> capabilities;
+  llvm::SetVector<spv::Capability> capabilities;
   std::vector<std::string> extensions;
   std::vector<std::string> extensions;
   llvm::MapVector<const char *, uint32_t> extInstSets;
   llvm::MapVector<const char *, uint32_t> extInstSets;
   // Addressing and memory model must exist for a valid SPIR-V module.
   // Addressing and memory model must exist for a valid SPIR-V module.
@@ -443,7 +444,7 @@ SPIRVModule::SPIRVModule()
 void SPIRVModule::setBound(uint32_t newBound) { header.bound = newBound; }
 void SPIRVModule::setBound(uint32_t newBound) { header.bound = newBound; }
 
 
 void SPIRVModule::addCapability(spv::Capability cap) {
 void SPIRVModule::addCapability(spv::Capability cap) {
-  capabilities.push_back(cap);
+  capabilities.insert(cap);
 }
 }
 
 
 void SPIRVModule::addExtension(std::string ext) {
 void SPIRVModule::addExtension(std::string ext) {

+ 22 - 10
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -210,10 +210,10 @@ uint32_t ModuleBuilder::createBinaryOp(spv::Op op, uint32_t resultType,
   return id;
   return id;
 }
 }
 
 
-namespace {
-spv::ImageOperandsMask composeImageOperandsMask(
+spv::ImageOperandsMask ModuleBuilder::composeImageOperandsMask(
     uint32_t bias, uint32_t lod, const std::pair<uint32_t, uint32_t> &grad,
     uint32_t bias, uint32_t lod, const std::pair<uint32_t, uint32_t> &grad,
-    uint32_t constOffset, llvm::SmallVectorImpl<uint32_t> *orderedParams) {
+    uint32_t constOffset, uint32_t varOffset,
+    llvm::SmallVectorImpl<uint32_t> *orderedParams) {
   using spv::ImageOperandsMask;
   using spv::ImageOperandsMask;
   // SPIR-V Image Operands from least significant bit to most significant bit
   // SPIR-V Image Operands from least significant bit to most significant bit
   // Bias, Lod, Grad, ConstOffset, Offset, ConstOffsets, Sample, MinLod
   // Bias, Lod, Grad, ConstOffset, Offset, ConstOffsets, Sample, MinLod
@@ -242,16 +242,22 @@ spv::ImageOperandsMask composeImageOperandsMask(
     orderedParams->push_back(constOffset);
     orderedParams->push_back(constOffset);
   }
   }
 
 
+  if (varOffset) {
+    mask = mask | ImageOperandsMask::Offset;
+    requireCapability(spv::Capability::ImageGatherExtended);
+    orderedParams->push_back(varOffset);
+  }
+
   return mask;
   return mask;
 }
 }
-} // anonymous namespace
 
 
 uint32_t ModuleBuilder::createImageSample(uint32_t texelType,
 uint32_t ModuleBuilder::createImageSample(uint32_t texelType,
                                           uint32_t imageType, uint32_t image,
                                           uint32_t imageType, uint32_t image,
                                           uint32_t sampler, uint32_t coordinate,
                                           uint32_t sampler, uint32_t coordinate,
                                           uint32_t bias, uint32_t lod,
                                           uint32_t bias, uint32_t lod,
                                           std::pair<uint32_t, uint32_t> grad,
                                           std::pair<uint32_t, uint32_t> grad,
-                                          uint32_t offset) {
+                                          uint32_t constOffset,
+                                          uint32_t varOffset) {
   assert(insertPoint && "null insert point");
   assert(insertPoint && "null insert point");
 
 
   // An OpSampledImage is required to do the image sampling.
   // An OpSampledImage is required to do the image sampling.
@@ -262,7 +268,8 @@ uint32_t ModuleBuilder::createImageSample(uint32_t texelType,
 
 
   const uint32_t texelId = theContext.takeNextId();
   const uint32_t texelId = theContext.takeNextId();
   llvm::SmallVector<uint32_t, 4> params;
   llvm::SmallVector<uint32_t, 4> params;
-  const auto mask = composeImageOperandsMask(bias, lod, grad, offset, &params);
+  const auto mask = composeImageOperandsMask(bias, lod, grad, constOffset,
+                                             varOffset, &params);
   // The Lod and Grad image operands requires explicit-lod instructions.
   // The Lod and Grad image operands requires explicit-lod instructions.
   if (lod || (grad.first && grad.second)) {
   if (lod || (grad.first && grad.second)) {
     instBuilder.opImageSampleExplicitLod(texelType, texelId, sampledImgId,
     instBuilder.opImageSampleExplicitLod(texelType, texelId, sampledImgId,
@@ -282,13 +289,15 @@ uint32_t ModuleBuilder::createImageSample(uint32_t texelType,
 
 
 uint32_t ModuleBuilder::createImageFetch(uint32_t texelType, uint32_t image,
 uint32_t ModuleBuilder::createImageFetch(uint32_t texelType, uint32_t image,
                                          uint32_t coordinate, uint32_t lod,
                                          uint32_t coordinate, uint32_t lod,
-                                         uint32_t offset) {
+                                         uint32_t constOffset,
+                                         uint32_t varOffset) {
   assert(insertPoint && "null insert point");
   assert(insertPoint && "null insert point");
 
 
   llvm::SmallVector<uint32_t, 2> params;
   llvm::SmallVector<uint32_t, 2> params;
   const auto mask =
   const auto mask =
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
-          /*bias*/ 0, lod, std::make_pair(0, 0), offset, &params));
+          /*bias*/ 0, lod, std::make_pair(0, 0), constOffset, varOffset,
+          &params));
 
 
   const uint32_t texelId = theContext.takeNextId();
   const uint32_t texelId = theContext.takeNextId();
   instBuilder.opImageFetch(texelType, texelId, image, coordinate, mask);
   instBuilder.opImageFetch(texelType, texelId, image, coordinate, mask);
@@ -303,7 +312,9 @@ uint32_t ModuleBuilder::createImageFetch(uint32_t texelType, uint32_t image,
 uint32_t ModuleBuilder::createImageGather(uint32_t texelType,
 uint32_t ModuleBuilder::createImageGather(uint32_t texelType,
                                           uint32_t imageType, uint32_t image,
                                           uint32_t imageType, uint32_t image,
                                           uint32_t sampler, uint32_t coordinate,
                                           uint32_t sampler, uint32_t coordinate,
-                                          uint32_t component, uint32_t offset) {
+                                          uint32_t component,
+                                          uint32_t constOffset,
+                                          uint32_t varOffset) {
   assert(insertPoint && "null insert point");
   assert(insertPoint && "null insert point");
 
 
   // An OpSampledImage is required to do the image sampling.
   // An OpSampledImage is required to do the image sampling.
@@ -316,7 +327,8 @@ uint32_t ModuleBuilder::createImageGather(uint32_t texelType,
 
 
   const auto mask =
   const auto mask =
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
-          /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0), offset, &params));
+          /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0), constOffset, varOffset,
+          &params));
   const uint32_t texelId = theContext.takeNextId();
   const uint32_t texelId = theContext.takeNextId();
   instBuilder.opImageGather(texelType, texelId, sampledImgId, coordinate,
   instBuilder.opImageGather(texelType, texelId, sampledImgId, coordinate,
                             component, mask);
                             component, mask);

+ 55 - 48
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -441,13 +441,8 @@ void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
     // instruction."
     // instruction."
     llvm::Optional<uint32_t> constInit;
     llvm::Optional<uint32_t> constInit;
     if (decl->hasInit()) {
     if (decl->hasInit()) {
-      // First try to evaluate the initializer as a constant expression
-      Expr::EvalResult evalResult;
-      if (decl->getInit()->EvaluateAsRValue(evalResult, astContext) &&
-          !evalResult.HasSideEffects) {
-        constInit = llvm::Optional<uint32_t>(
-            translateAPValue(evalResult.Val, decl->getType()));
-      }
+      if (const uint32_t id = tryToEvaluateAsConst(decl->getInit()))
+        constInit = llvm::Optional<uint32_t>(id);
     } else if (isFileScopeVar) {
     } else if (isFileScopeVar) {
       // For static variables, if no initializers are provided, we should
       // For static variables, if no initializers are provided, we should
       // initialize them to zero values.
       // initialize them to zero values.
@@ -1295,10 +1290,21 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
   const FunctionDecl *callee = expr->getDirectCallee();
   const FunctionDecl *callee = expr->getDirectCallee();
   const auto retType = typeTranslator.translateType(callee->getReturnType());
   const auto retType = typeTranslator.translateType(callee->getReturnType());
 
 
-  // TODO: For the following method calls, offset is not always constant.
-  // In SPIR-V, there are two image operands, ConstOffset and Offset.
-  // We are translating all into ConstOffset right now, which is incorrect
-  // for non-constant offset.
+  // Handles the offset argument. If there exists an offset argument, writes the
+  // <result-id> to either *constOffset or *varOffset, depending on the
+  // constantness of the offset.
+  const auto handleOffset = [this](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));
+    }
+  };
 
 
   llvm::StringRef group;
   llvm::StringRef group;
   uint32_t opcode = static_cast<uint32_t>(IntrinsicOp::Num_Intrinsics);
   uint32_t opcode = static_cast<uint32_t>(IntrinsicOp::Num_Intrinsics);
@@ -1323,20 +1329,19 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
       const uint32_t image = loadIfGLValue(imageExpr);
       const uint32_t image = loadIfGLValue(imageExpr);
       const uint32_t sampler = doExpr(expr->getArg(0));
       const uint32_t sampler = doExpr(expr->getArg(0));
       const uint32_t coordinate = doExpr(expr->getArg(1));
       const uint32_t coordinate = doExpr(expr->getArg(1));
-      // .Sample()/.Gather() has a third optional paramter for offset, which
-      // should be translated into SPIR-V ConstOffset image operands.
-      const uint32_t offset =
-          expr->getNumArgs() == 3 ? doExpr(expr->getArg(2)) : 0;
+      // .Sample()/.Gather() has a third optional paramter for offset.
+      uint32_t constOffset = 0, varOffset = 0;
+      handleOffset(expr, 2, &constOffset, &varOffset);
 
 
       if (opcode == static_cast<uint32_t>(IntrinsicOp::MOP_Sample)) {
       if (opcode == static_cast<uint32_t>(IntrinsicOp::MOP_Sample)) {
-        return theBuilder.createImageSample(retType, imageType, image, sampler,
-                                            coordinate, /*bias*/ 0, /*lod*/ 0,
-                                            std::make_pair(0, 0), offset);
+        return theBuilder.createImageSample(
+            retType, imageType, image, sampler, coordinate, /*bias*/ 0,
+            /*lod*/ 0, std::make_pair(0, 0), constOffset, varOffset);
       } else {
       } else {
         return theBuilder.createImageGather(
         return theBuilder.createImageGather(
             retType, imageType, image, sampler, coordinate,
             retType, imageType, image, sampler, coordinate,
             // .Gather() doc says we return four components of red data.
             // .Gather() doc says we return four components of red data.
-            theBuilder.getConstantInt32(0), offset);
+            theBuilder.getConstantInt32(0), constOffset, varOffset);
       }
       }
     }
     }
     case IntrinsicOp::MOP_SampleBias:
     case IntrinsicOp::MOP_SampleBias:
@@ -1366,14 +1371,13 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
       } else {
       } else {
         lod = doExpr(expr->getArg(2));
         lod = doExpr(expr->getArg(2));
       }
       }
-      // .Bias()/.SampleLevel() has a fourth optional paramter for offset,
-      // which should be translated into SPIR-V ConstOffset image operands.
-      const uint32_t offset =
-          expr->getNumArgs() == 4 ? doExpr(expr->getArg(3)) : 0;
-
-      return theBuilder.createImageSample(retType, imageType, image, sampler,
-                                          coordinate, bias, lod,
-                                          std::make_pair(0, 0), offset);
+      // .Bias()/.SampleLevel() has a fourth optional paramter for offset.
+      uint32_t constOffset = 0, varOffset = 0;
+      handleOffset(expr, 3, &constOffset, &varOffset);
+
+      return theBuilder.createImageSample(
+          retType, imageType, image, sampler, coordinate, bias, lod,
+          std::make_pair(0, 0), constOffset, varOffset);
     }
     }
     case IntrinsicOp::MOP_SampleGrad: {
     case IntrinsicOp::MOP_SampleGrad: {
       // Signature:
       // Signature:
@@ -1393,14 +1397,13 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
       const uint32_t coordinate = doExpr(expr->getArg(1));
       const uint32_t coordinate = doExpr(expr->getArg(1));
       const uint32_t ddx = doExpr(expr->getArg(2));
       const uint32_t ddx = doExpr(expr->getArg(2));
       const uint32_t ddy = doExpr(expr->getArg(3));
       const uint32_t ddy = doExpr(expr->getArg(3));
-      // .SampleGrad() has a fifth optional paramter for offset, which
-      // should be translated into SPIR-V ConstOffset image operands.
-      const uint32_t offset =
-          expr->getNumArgs() == 5 ? doExpr(expr->getArg(4)) : 0;
-
-      return theBuilder.createImageSample(retType, imageType, image, sampler,
-                                          coordinate, /*bias*/ 0, /*lod*/ 0,
-                                          std::make_pair(ddx, ddy), offset);
+      // .SampleGrad() has a fifth optional paramter for offset.
+      uint32_t constOffset = 0, varOffset = 0;
+      handleOffset(expr, 4, &constOffset, &varOffset);
+
+      return theBuilder.createImageSample(
+          retType, imageType, image, sampler, coordinate, /*bias*/ 0, /*lod*/ 0,
+          std::make_pair(ddx, ddy), constOffset, varOffset);
     }
     }
     case IntrinsicOp::MOP_Load: {
     case IntrinsicOp::MOP_Load: {
       // Signature:
       // Signature:
@@ -1428,11 +1431,12 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
       splitVecLastElement(expr->getArg(0)->getType(), location, &coordinate,
       splitVecLastElement(expr->getArg(0)->getType(), location, &coordinate,
                           &lod);
                           &lod);
 
 
-      const uint32_t offset =
-          expr->getNumArgs() == 2 ? doExpr(expr->getArg(1)) : 0;
+      // .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,
       return theBuilder.createImageFetch(retType, image, coordinate, lod,
-                                         offset);
+                                         constOffset, varOffset);
     }
     }
     default:
     default:
       emitError("HLSL intrinsic member call unimplemented: %0")
       emitError("HLSL intrinsic member call unimplemented: %0")
@@ -1638,12 +1642,8 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
 }
 }
 
 
 uint32_t SPIRVEmitter::doInitListExpr(const InitListExpr *expr) {
 uint32_t SPIRVEmitter::doInitListExpr(const InitListExpr *expr) {
-  // First try to evaluate the expression as constant expression
-  Expr::EvalResult evalResult;
-  if (expr->EvaluateAsRValue(evalResult, astContext) &&
-      !evalResult.HasSideEffects) {
-    return translateAPValue(evalResult.Val, expr->getType());
-  }
+  if (const uint32_t id = tryToEvaluateAsConst(expr))
+    return id;
 
 
   return InitListHandler(*this).process(expr);
   return InitListHandler(*this).process(expr);
 }
 }
@@ -2103,11 +2103,8 @@ uint32_t SPIRVEmitter::createVectorSplat(const Expr *scalarExpr, uint32_t size,
 
 
   // Try to evaluate the element as constant first. If successful, then we
   // Try to evaluate the element as constant first. If successful, then we
   // can generate constant instructions for this vector splat.
   // can generate constant instructions for this vector splat.
-  Expr::EvalResult evalResult;
-  if (scalarExpr->EvaluateAsRValue(evalResult, astContext) &&
-      !evalResult.HasSideEffects) {
+  if (scalarVal = tryToEvaluateAsConst(scalarExpr)) {
     isConstVal = true;
     isConstVal = true;
-    scalarVal = translateAPValue(evalResult.Val, scalarExpr->getType());
   } else {
   } else {
     scalarVal = doExpr(scalarExpr);
     scalarVal = doExpr(scalarExpr);
   }
   }
@@ -3281,6 +3278,16 @@ uint32_t SPIRVEmitter::translateAPFloat(const llvm::APFloat &floatValue,
   return 0;
   return 0;
 }
 }
 
 
+uint32_t SPIRVEmitter::tryToEvaluateAsConst(const Expr *expr) {
+  Expr::EvalResult evalResult;
+  if (expr->EvaluateAsRValue(evalResult, astContext) &&
+      !evalResult.HasSideEffects) {
+    return translateAPValue(evalResult.Val, expr->getType());
+  }
+
+  return 0;
+}
+
 spv::ExecutionModel
 spv::ExecutionModel
 SPIRVEmitter::getSpirvShaderStage(const hlsl::ShaderModel &model) {
 SPIRVEmitter::getSpirvShaderStage(const hlsl::ShaderModel &model) {
   // DXIL Models are:
   // DXIL Models are:

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

@@ -301,11 +301,15 @@ private:
   uint32_t translateAPFloat(const llvm::APFloat &floatValue,
   uint32_t translateAPFloat(const llvm::APFloat &floatValue,
                             QualType targetType);
                             QualType targetType);
 
 
+  /// Tries to evaluate the given Expr as a constant and returns the <result-id>
+  /// if success. Otherwise, returns 0.
+  uint32_t tryToEvaluateAsConst(const Expr *expr);
+
+private:
   /// Translates the given HLSL loop attribute into SPIR-V loop control mask.
   /// Translates the given HLSL loop attribute into SPIR-V loop control mask.
   /// Emits an error if the given attribute is not a loop attribute.
   /// Emits an error if the given attribute is not a loop attribute.
   spv::LoopControlMask translateLoopAttribute(const Attr &);
   spv::LoopControlMask translateLoopAttribute(const Attr &);
 
 
-private:
   static spv::ExecutionModel
   static spv::ExecutionModel
   getSpirvShaderStage(const hlsl::ShaderModel &model);
   getSpirvShaderStage(const hlsl::ShaderModel &model);
 
 

+ 6 - 3
tools/clang/test/CodeGenSPIRV/texture.array.gather.hlsl

@@ -6,16 +6,19 @@ Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t4 : register(t4);
 TextureCubeArray <float4> t4 : register(t4);
 // .Gather() does not support Texture1DArray.
 // .Gather() does not support Texture1DArray.
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_0_4
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_0_4
 
 
-float4 main(float3 location: A) : SV_Target {
+float4 main(float3 location: A, int2 offset: B) : SV_Target {
 
 
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:        [[loc:%\d+]] = OpLoad %v3float %location
 // CHECK-NEXT:        [[loc:%\d+]] = OpLoad %v3float %location
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t2]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t2]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageGather %v4float [[sampledImg]] [[loc]] %int_0
-    float4 val2 = t2.Gather(gSampler, location);
+// CHECK-NEXT:            {{%\d+}} = OpImageGather %v4float [[sampledImg]] [[loc]] %int_0 Offset [[offset]]
+    float4 val2 = t2.Gather(gSampler, location, offset);
 
 
 // CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image_array %t4
 // CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image_array %t4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 6 - 3
tools/clang/test/CodeGenSPIRV/texture.array.sample-bias.hlsl

@@ -8,6 +8,8 @@ Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
 TextureCubeArray <float4> t3 : register(t3);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image_array
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image_array
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image_array
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image_array
@@ -16,7 +18,7 @@ TextureCubeArray <float4> t3 : register(t3);
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_1
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_1
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_1
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_1
 
 
-float4 main() : SV_Target {
+float4 main(int2 offset : A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -25,9 +27,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias %float_0_5
-    float4 val2 = t2.SampleBias(gSampler, float3(0.1, 0.2, 1), 0.5);
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias|Offset %float_0_5 [[offset]]
+    float4 val2 = t2.SampleBias(gSampler, float3(0.1, 0.2, 1), 0.5, offset);
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 6 - 3
tools/clang/test/CodeGenSPIRV/texture.array.sample-grad.hlsl

@@ -8,6 +8,8 @@ Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
 TextureCubeArray <float4> t3 : register(t3);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image
@@ -20,7 +22,7 @@ TextureCubeArray <float4> t3 : register(t3);
 // CHECK: [[v3f_0_2:%\d+]] = OpConstantComposite %v3float %float_0_2 %float_0_2 %float_0_2
 // CHECK: [[v3f_0_2:%\d+]] = OpConstantComposite %v3float %float_0_2 %float_0_2 %float_0_2
 // CHECK: [[v3f_0_3:%\d+]] = OpConstantComposite %v3float %float_0_3 %float_0_3 %float_0_3
 // CHECK: [[v3f_0_3:%\d+]] = OpConstantComposite %v3float %float_0_3 %float_0_3 %float_0_3
 
 
-float4 main() : SV_Target {
+float4 main(int2 offset : A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -29,9 +31,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3f_0_1]] Grad [[v2f_0_2]] [[v2f_0_3]]
-    float4 val2 = t2.SampleGrad(gSampler, float3(0.1, 0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3));
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3f_0_1]] Grad|Offset [[v2f_0_2]] [[v2f_0_3]] [[offset]]
+    float4 val2 = t2.SampleGrad(gSampler, float3(0.1, 0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), offset);
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 6 - 3
tools/clang/test/CodeGenSPIRV/texture.array.sample-level.hlsl

@@ -8,6 +8,8 @@ Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
 TextureCubeArray <float4> t3 : register(t3);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image_array
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image_array
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image_array
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image_array
@@ -16,7 +18,7 @@ TextureCubeArray <float4> t3 : register(t3);
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_1
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_1
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_1
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_1
 
 
-float4 main() : SV_Target {
+float4 main(int2 offset : A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -25,9 +27,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3fc]] Lod %float_20
-    float4 val2 = t2.SampleLevel(gSampler, float3(0.1, 0.2, 1), 20);
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3fc]] Lod|Offset %float_20 [[offset]]
+    float4 val2 = t2.SampleLevel(gSampler, float3(0.1, 0.2, 1), 20, offset);
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 8 - 5
tools/clang/test/CodeGenSPIRV/texture.load.hlsl

@@ -5,24 +5,27 @@ Texture2D <float4> t2 : register(t2);
 Texture3D <float4> t3 : register(t3);
 Texture3D <float4> t3 : register(t3);
 // .Load() does not support TextureCube.
 // .Load() does not support TextureCube.
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
 // CHECK: [[v4ic:%\d+]] = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
 // 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: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 
 
-float4 main(int3 location: A) : SV_Target {
+float4 main(int3 location: A, int offset: B) : SV_Target {
 
 
 // CHECK:         [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK:         [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK-NEXT: [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
 // CHECK-NEXT: [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
 // CHECK-NEXT:   [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
 // CHECK-NEXT:   [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
-// CHECK-NEXT:       {{%\d+}} = OpImageFetch %v4float [[t1]] [[coord]] Lod|ConstOffset [[lod]] %int_1
-    float4 val1 = t1.Load(int2(1, 2), 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);
 
 
 // CHECK:         [[t2:%\d+]] = OpLoad %type_2d_image %t2
 // CHECK:         [[t2:%\d+]] = OpLoad %type_2d_image %t2
 // CHECK-NEXT:   [[loc:%\d+]] = OpLoad %v3int %location
 // CHECK-NEXT:   [[loc:%\d+]] = OpLoad %v3int %location
 // CHECK-NEXT: [[coord:%\d+]] = OpVectorShuffle %v2int [[loc]] [[loc]] 0 1
 // CHECK-NEXT: [[coord:%\d+]] = OpVectorShuffle %v2int [[loc]] [[loc]] 0 1
 // CHECK-NEXT:   [[lod:%\d+]] = OpCompositeExtract %int [[loc]] 2
 // CHECK-NEXT:   [[lod:%\d+]] = OpCompositeExtract %int [[loc]] 2
-// CHECK-NEXT:       {{%\d+}} = OpImageFetch %v4float [[t2]] [[coord]] Lod [[lod]]
-    float4 val2 = t2.Load(location);
+// CHECK-NEXT:       {{%\d+}} = OpImageFetch %v4float [[t2]] [[coord]] Lod|ConstOffset [[lod]] [[v2ic]]
+    float4 val2 = t2.Load(location, int2(1, 2));
 
 
 // CHECK:         [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK:         [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK-NEXT: [[coord:%\d+]] = OpVectorShuffle %v3int [[v4ic]] [[v4ic]] 0 1 2
 // CHECK-NEXT: [[coord:%\d+]] = OpVectorShuffle %v3int [[v4ic]] [[v4ic]] 0 1 2

+ 6 - 4
tools/clang/test/CodeGenSPIRV/texture.sample-bias.hlsl

@@ -9,6 +9,8 @@ Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 TextureCube <float4> t4 : register(t4);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
@@ -17,9 +19,8 @@ TextureCube <float4> t4 : register(t4);
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_2 %int_2
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_2 %int_2
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
-// CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 
 
-float4 main() : SV_Target {
+float4 main(int3 offset: A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -34,9 +35,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v3int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias|ConstOffset %float_0_5 [[v3ic]]
-    float4 val3 = t3.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5, 3);
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias|Offset %float_0_5 [[offset]]
+    float4 val3 = t3.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5, offset);
 
 
 // CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
 // CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 6 - 4
tools/clang/test/CodeGenSPIRV/texture.sample-grad.hlsl

@@ -9,6 +9,8 @@ Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 TextureCube <float4> t4 : register(t4);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
@@ -17,13 +19,12 @@ TextureCube <float4> t4 : register(t4);
 // CHECK: [[v2f_0_1:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_1
 // CHECK: [[v2f_0_1:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_1
 // CHECK: [[v2f_0_2:%\d+]] = OpConstantComposite %v2float %float_0_2 %float_0_2
 // CHECK: [[v2f_0_2:%\d+]] = OpConstantComposite %v2float %float_0_2 %float_0_2
 // CHECK: [[v2f_0_3:%\d+]] = OpConstantComposite %v2float %float_0_3 %float_0_3
 // CHECK: [[v2f_0_3:%\d+]] = OpConstantComposite %v2float %float_0_3 %float_0_3
-// CHECK: [[v2i_2:%\d+]] = OpConstantComposite %v2int %int_2 %int_2
 // CHECK: [[v3f_0_1:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_1 %float_0_1
 // CHECK: [[v3f_0_1:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_1 %float_0_1
 // CHECK: [[v3f_0_2:%\d+]] = OpConstantComposite %v3float %float_0_2 %float_0_2 %float_0_2
 // CHECK: [[v3f_0_2:%\d+]] = OpConstantComposite %v3float %float_0_2 %float_0_2 %float_0_2
 // CHECK: [[v3f_0_3:%\d+]] = OpConstantComposite %v3float %float_0_3 %float_0_3 %float_0_3
 // CHECK: [[v3f_0_3:%\d+]] = OpConstantComposite %v3float %float_0_3 %float_0_3 %float_0_3
 // CHECK: [[v3i_3:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 // CHECK: [[v3i_3:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 
 
-float4 main() : SV_Target {
+float4 main(int2 offset : A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -32,9 +33,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image %t2
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image %t2
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v2f_0_1]] Grad|ConstOffset [[v2f_0_2]] [[v2f_0_3]] [[v2i_2]]
-    float4 val2 = t2.SampleGrad(gSampler, float2(0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), 2);
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v2f_0_1]] Grad|Offset [[v2f_0_2]] [[v2f_0_3]] [[offset]]
+    float4 val2 = t2.SampleGrad(gSampler, float2(0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), offset);
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 6 - 4
tools/clang/test/CodeGenSPIRV/texture.sample-level.hlsl

@@ -9,6 +9,8 @@ Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 TextureCube <float4> t4 : register(t4);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
@@ -17,9 +19,8 @@ TextureCube <float4> t4 : register(t4);
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_2 %int_2
 // CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_2 %int_2
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
-// CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 
 
-float4 main() : SV_Target {
+float4 main(int3 offset: A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -34,9 +35,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v3int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3fc]] Lod|ConstOffset %float_10 [[v3ic]]
-    float4 val3 = t3.SampleLevel(gSampler, float3(0.1, 0.2, 0.3), 10, 3);
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3fc]] Lod|Offset %float_10 [[offset]]
+    float4 val3 = t3.SampleLevel(gSampler, float3(0.1, 0.2, 0.3), 10, offset);
 
 
 // CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
 // CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler

+ 6 - 4
tools/clang/test/CodeGenSPIRV/texture.sample.hlsl

@@ -9,17 +9,18 @@ Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 TextureCube <float4> t4 : register(t4);
 
 
+// CHECK: OpCapability ImageGatherExtended
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_3d_image
 // CHECK: %type_sampled_image_2 = OpTypeSampledImage %type_cube_image
 // CHECK: %type_sampled_image_2 = OpTypeSampledImage %type_cube_image
 
 
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
-// CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_2 %int_2
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
 // CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 // CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
 
 
-float4 main() : SV_Target {
+float4 main(int2 offset: A) : SV_Target {
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK:              [[t1:%\d+]] = OpLoad %type_1d_image %t1
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
@@ -28,9 +29,10 @@ float4 main() : SV_Target {
 
 
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image %t2
 // CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image %t2
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
 // CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
-// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v2fc]] ConstOffset [[v2ic]]
-    float4 val2 = t2.Sample(gSampler, float2(0.1, 0.2), 2);
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v2fc]] Offset [[offset]]
+    float4 val2 = t2.Sample(gSampler, float2(0.1, 0.2), offset);
 
 
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK:              [[t3:%\d+]] = OpLoad %type_3d_image %t3
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler