graphics robust access: use signed clamp (#3073)

Access chain indices are always interpreted as signed integers.
So use signed clamp instead of unsigned clamp.  We must also
clamp to the max signed int for the index type.

Fixes #3072
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 509051d..2998ec1 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -827,6 +827,16 @@
 //   of range. (The module is already invalid if that is the case.)
 // - TODO(dneto): The OpImageTexelPointer coordinate component is not 32-bits
 // wide.
+//
+// NOTE: Access chain indices are always treated as signed integers.  So
+//   if an array has a fixed size of more than 2^31 elements, then elements
+//   from 2^31 and above are never accessible with a 32-bit index,
+//   signed or unsigned.  For this case, this pass will clamp the index
+//   between 0 and at 2^31-1, inclusive.
+//   Similarly, if an array has more then 2^15 element and is accessed with
+//   a 16-bit index, then elements from 2^15 and above are not accessible.
+//   In this case, the pass will clamp the index between 0 and 2^15-1
+//   inclusive.
 Optimizer::PassToken CreateGraphicsRobustAccessPass();
 
 // Create descriptor scalar replacement pass.
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index e309d3a..22c979c 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -121,6 +121,10 @@
 //      the result of an OpArrayLength instruction acting on the pointer of
 //      the containing structure as noted above.
 //
+//      - Access chain indices are always treated as signed, so:
+//        - Clamp the upper bound at the signed integer maximum.
+//        - Use SClamp for all clamping.
+//
 //    - TODO(dneto): OpImageTexelPointer:
 //      - Clamp coordinate to the image size returned by OpImageQuerySize
 //      - If multi-sampled, clamp the sample index to the count returned by
@@ -141,6 +145,7 @@
 #include <cstring>
 #include <functional>
 #include <initializer_list>
+#include <limits>
 #include <utility>
 
 #include "constants.h"
@@ -246,6 +251,7 @@
   }
   for (auto* inst : access_chains) {
     ClampIndicesForAccessChain(inst);
+    if (module_status_.failed) return module_status_.modified;
   }
 
   for (auto* inst : image_texel_pointers) {
@@ -261,6 +267,8 @@
   auto* constant_mgr = context()->get_constant_mgr();
   auto* def_use_mgr = context()->get_def_use_mgr();
   auto* type_mgr = context()->get_type_mgr();
+  const bool have_int64_cap =
+      context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
 
   // Replaces one of the OpAccessChain index operands with a new value.
   // Updates def-use analysis.
@@ -268,37 +276,66 @@
                                             Instruction* new_value) {
     inst.SetOperand(operand_index, {new_value->result_id()});
     def_use_mgr->AnalyzeInstUse(&inst);
+    return SPV_SUCCESS;
   };
 
   // Replaces one of the OpAccesssChain index operands with a clamped value.
   // Replace the operand at |operand_index| with the value computed from
-  // unsigned_clamp(%old_value, %min_value, %max_value).  It also analyzes
+  // signed_clamp(%old_value, %min_value, %max_value).  It also analyzes
   // the new instruction and records that them module is modified.
-  auto clamp_index = [&inst, this, &replace_index](
+  // Assumes %min_value is signed-less-or-equal than %max_value. (All callees
+  // use 0 for %min_value).
+  auto clamp_index = [&inst, type_mgr, this, &replace_index](
                          uint32_t operand_index, Instruction* old_value,
                          Instruction* min_value, Instruction* max_value) {
-    auto* clamp_inst = MakeClampInst(old_value, min_value, max_value, &inst);
-    replace_index(operand_index, clamp_inst);
+    auto* clamp_inst =
+        MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst);
+    return replace_index(operand_index, clamp_inst);
   };
 
   // Ensures the specified index of access chain |inst| has a value that is
   // at most |count| - 1.  If the index is already a constant value less than
   // |count| then no change is made.
-  auto clamp_to_literal_count = [&inst, this, &constant_mgr, &type_mgr,
-                                 &replace_index, &clamp_index](
-                                    uint32_t operand_index, uint64_t count) {
+  auto clamp_to_literal_count =
+      [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index,
+       &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t {
     Instruction* index_inst =
         this->GetDef(inst.GetSingleWordOperand(operand_index));
     const auto* index_type =
         type_mgr->GetType(index_inst->type_id())->AsInteger();
     assert(index_type);
+    const auto index_width = index_type->width();
+
     if (count <= 1) {
       // Replace the index with 0.
-      replace_index(operand_index, GetValueForType(0, index_type));
-      return;
+      return replace_index(operand_index, GetValueForType(0, index_type));
     }
 
-    const auto index_width = index_type->width();
+    uint64_t maxval = count - 1;
+
+    // Compute the bit width of a viable type to hold |maxval|.
+    // Look for a bit width, up to 64 bits wide, to fit maxval.
+    uint32_t maxval_width = index_width;
+    while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
+      maxval_width *= 2;
+    }
+    // Determine the type for |maxval|.
+    analysis::Integer signed_type_for_query(maxval_width, true);
+    auto* maxval_type =
+        type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger();
+    // Access chain indices are treated as signed, so limit the maximum value
+    // of the index so it will always be positive for a signed clamp operation.
+    maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1));
+
+    if (index_width > 64) {
+      return this->Fail() << "Can't handle indices wider than 64 bits, found "
+                             "constant index with "
+                          << index_width << " bits as index number "
+                          << operand_index << " of access chain "
+                          << inst.PrettyPrint();
+    }
+
+    // Split into two cases: the current index is a constant, or not.
 
     // If the index is a constant then |index_constant| will not be a null
     // pointer.  (If index is an |OpConstantNull| then it |index_constant| will
@@ -313,55 +350,62 @@
         value = int64_t(int_index_constant->GetS32BitValue());
       } else if (index_width <= 64) {
         value = int_index_constant->GetS64BitValue();
-      } else {
-        this->Fail() << "Can't handle indices wider than 64 bits, found "
-                        "constant index with "
-                     << index_type->width() << "bits";
-        return;
       }
       if (value < 0) {
-        replace_index(operand_index, GetValueForType(0, index_type));
-      } else if (uint64_t(value) < count) {
+        return replace_index(operand_index, GetValueForType(0, index_type));
+      } else if (uint64_t(value) <= maxval) {
         // Nothing to do.
-        return;
+        return SPV_SUCCESS;
       } else {
-        // Replace with count - 1.
+        // Replace with maxval.
         assert(count > 0);  // Already took care of this case above.
-        replace_index(operand_index, GetValueForType(count - 1, index_type));
+        return replace_index(operand_index,
+                             GetValueForType(maxval, maxval_type));
       }
     } else {
       // Generate a clamp instruction.
-
-      // Compute the bit width of a viable type to hold (count-1).
-      const auto maxval = count - 1;
-      const auto* maxval_type = index_type;
-      // Look for a bit width, up to 64 bits wide, to fit maxval.
-      uint32_t maxval_width = index_width;
-      while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
-        maxval_width *= 2;
+      assert(maxval >= 1);
+      assert(index_width <= 64);  // Otherwise, already returned above.
+      if (index_width >= 64 && !have_int64_cap) {
+        // An inconsistent module.
+        return Fail() << "Access chain index is wider than 64 bits, but Int64 "
+                         "is not declared: "
+                      << index_inst->PrettyPrint();
       }
       // Widen the index value if necessary
       if (maxval_width > index_width) {
-        // Find the wider type.  We only need this case if a constant (array)
-        // bound is too big.  This never requires us to *add* a capability
-        // declaration for Int64 because the existence of the array bound would
-        // already have required that declaration.
+        // Find the wider type.  We only need this case if a constant array
+        // bound is too big.
+
+        // From how we calculated maxval_width, widening won't require adding
+        // the Int64 capability.
+        assert(have_int64_cap || maxval_width <= 32);
+        if (!have_int64_cap && maxval_width >= 64) {
+          // Be defensive, but this shouldn't happen.
+          return this->Fail()
+                 << "Clamping index would require adding Int64 capability. "
+                 << "Can't clamp 32-bit index " << operand_index
+                 << " of access chain " << inst.PrettyPrint();
+        }
         index_inst = WidenInteger(index_type->IsSigned(), maxval_width,
                                   index_inst, &inst);
-        maxval_type = type_mgr->GetType(index_inst->type_id())->AsInteger();
       }
+
       // Finally, clamp the index.
-      clamp_index(operand_index, index_inst, GetValueForType(0, maxval_type),
-                  GetValueForType(maxval, maxval_type));
+      return clamp_index(operand_index, index_inst,
+                         GetValueForType(0, maxval_type),
+                         GetValueForType(maxval, maxval_type));
     }
+    return SPV_SUCCESS;
   };
 
   // Ensures the specified index of access chain |inst| has a value that is at
   // most the value of |count_inst| minus 1, where |count_inst| is treated as an
-  // unsigned integer.
+  // unsigned integer. This can log a failure.
   auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count,
-                         &clamp_index, &type_mgr](uint32_t operand_index,
-                                                  Instruction* count_inst) {
+                         &clamp_index,
+                         &type_mgr](uint32_t operand_index,
+                                    Instruction* count_inst) -> spv_result_t {
     Instruction* index_inst =
         this->GetDef(inst.GetSingleWordOperand(operand_index));
     const auto* index_type =
@@ -378,12 +422,11 @@
       } else if (width <= 64) {
         value = count_constant->AsIntConstant()->GetU64BitValue();
       } else {
-        this->Fail() << "Can't handle indices wider than 64 bits, found "
-                        "constant index with "
-                     << index_type->width() << "bits";
-        return;
+        return this->Fail() << "Can't handle indices wider than 64 bits, found "
+                               "constant index with "
+                            << index_type->width() << "bits";
       }
-      clamp_to_literal_count(operand_index, value);
+      return clamp_to_literal_count(operand_index, value);
     } else {
       // Widen them to the same width.
       const auto index_width = index_type->width();
@@ -406,9 +449,21 @@
           &inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(),
           {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}},
            {SPV_OPERAND_TYPE_ID, {one->result_id()}}});
-      clamp_index(operand_index, index_inst, GetValueForType(0, wider_type),
-                  count_minus_1);
+      auto* zero = GetValueForType(0, wider_type);
+      // Make sure we clamp to an upper bound that is at most the signed max
+      // for the target type.
+      const uint64_t max_signed_value =
+          ((uint64_t(1) << (target_width - 1)) - 1);
+      // Use unsigned-min to ensure that the result is always non-negative.
+      // That ensures we satisfy the invariant for SClamp, where the "min"
+      // argument we give it (zero), is no larger than the third argument.
+      auto* upper_bound =
+          MakeUMinInst(*type_mgr, count_minus_1,
+                       GetValueForType(max_signed_value, wider_type), &inst);
+      // Now clamp the index to this upper bound.
+      return clamp_index(operand_index, index_inst, zero, upper_bound);
     }
+    return SPV_SUCCESS;
   };
 
   const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0));
@@ -483,6 +538,7 @@
           return;
         }
         clamp_to_count(idx, array_len);
+        if (module_status_.failed) return;
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
 
@@ -557,22 +613,51 @@
   return conversion;
 }
 
-Instruction* GraphicsRobustAccessPass::MakeClampInst(Instruction* x,
-                                                     Instruction* min,
-                                                     Instruction* max,
-                                                     Instruction* where) {
+Instruction* GraphicsRobustAccessPass::MakeUMinInst(
+    const analysis::TypeManager& tm, Instruction* x, Instruction* y,
+    Instruction* where) {
+  // Get IDs of instructions we'll be referencing. Evaluate them before calling
+  // the function so we force a deterministic ordering in case both of them need
+  // to take a new ID.
+  const uint32_t glsl_insts_id = GetGlslInsts();
+  uint32_t smin_id = TakeNextId();
+  const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
+  const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width();
+  assert(xwidth == ywidth);
+  (void)xwidth;
+  (void)ywidth;
+  auto* smin_inst = InsertInst(
+      where, SpvOpExtInst, x->type_id(), smin_id,
+      {
+          {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
+          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}},
+          {SPV_OPERAND_TYPE_ID, {x->result_id()}},
+          {SPV_OPERAND_TYPE_ID, {y->result_id()}},
+      });
+  return smin_inst;
+}
+
+Instruction* GraphicsRobustAccessPass::MakeSClampInst(
+    const analysis::TypeManager& tm, Instruction* x, Instruction* min,
+    Instruction* max, Instruction* where) {
   // Get IDs of instructions we'll be referencing. Evaluate them before calling
   // the function so we force a deterministic ordering in case both of them need
   // to take a new ID.
   const uint32_t glsl_insts_id = GetGlslInsts();
   uint32_t clamp_id = TakeNextId();
-  assert(x->type_id() == min->type_id());
-  assert(x->type_id() == max->type_id());
+  const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
+  const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width();
+  const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width();
+  assert(xwidth == minwidth);
+  assert(xwidth == maxwidth);
+  (void)xwidth;
+  (void)minwidth;
+  (void)maxwidth;
   auto* clamp_inst = InsertInst(
       where, SpvOpExtInst, x->type_id(), clamp_id,
       {
           {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
-          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UClamp}},
+          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}},
           {SPV_OPERAND_TYPE_ID, {x->result_id()}},
           {SPV_OPERAND_TYPE_ID, {min->result_id()}},
           {SPV_OPERAND_TYPE_ID, {max->result_id()}},
@@ -716,6 +801,7 @@
 spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
     opt::Instruction* image_texel_pointer) {
   // TODO(dneto): Write tests for this code.
+  // TODO(dneto): Use signed-clamp
   return SPV_SUCCESS;
 
   // Example:
@@ -916,9 +1002,9 @@
         {constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}});
 
   // Clamp the coordinate
-  auto* clamp_coord =
-      MakeClampInst(coord, constant_mgr->GetDefiningInstruction(coordinate_0),
-                    query_max_including_faces, image_texel_pointer);
+  auto* clamp_coord = MakeSClampInst(
+      *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0),
+      query_max_including_faces, image_texel_pointer);
   image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()});
 
   // Clamp the sample index
@@ -936,8 +1022,8 @@
                                    {{SPV_OPERAND_TYPE_ID, {query_samples_id}},
                                     {SPV_OPERAND_TYPE_ID, {component_1_id}}});
 
-    auto* clamp_samples = MakeClampInst(
-        samples, constant_mgr->GetDefiningInstruction(coordinate_0),
+    auto* clamp_samples = MakeSClampInst(
+        *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0),
         max_samples, image_texel_pointer);
     image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()});
 
diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h
index b21154e..6fc692c 100644
--- a/source/opt/graphics_robust_access_pass.h
+++ b/source/opt/graphics_robust_access_pass.h
@@ -18,13 +18,13 @@
 #include <map>
 #include <unordered_map>
 
-#include "source/diagnostic.h"
-
 #include "constants.h"
 #include "def_use_manager.h"
 #include "instruction.h"
 #include "module.h"
 #include "pass.h"
+#include "source/diagnostic.h"
+#include "type_manager.h"
 
 namespace spvtools {
 namespace opt {
@@ -49,7 +49,8 @@
   // consumer.
   spvtools::DiagnosticStream Fail();
 
-  // Returns SPV_SUCCESS if this pass can correctly process the module.
+  // Returns SPV_SUCCESS if this pass can correctly process the module,
+  // as far as we can tell from capabilities and the memory model.
   // Otherwise logs a message and returns a failure code.
   spv_result_t IsCompatibleModule();
 
@@ -59,12 +60,12 @@
   spv_result_t ProcessCurrentModule();
 
   // Process the given function.  Updates the state value |_|.  Returns true
-  // if the module was modified.
+  // if the module was modified.  This can log a failure.
   bool ProcessAFunction(opt::Function*);
 
   // Clamps indices in the OpAccessChain or OpInBoundsAccessChain instruction
   // |access_chain|. Inserts instructions before the given instruction.  Updates
-  // analyses and records that the module is modified.
+  // analyses and records that the module is modified.  This can log a failure.
   void ClampIndicesForAccessChain(Instruction* access_chain);
 
   // Returns the id of the instruction importing the "GLSL.std.450" extended
@@ -85,17 +86,29 @@
   Instruction* WidenInteger(bool sign_extend, uint32_t bit_width,
                             Instruction* value, Instruction* before_inst);
 
-  // Returns a new instruction that invokes the UClamp GLSL.std.450 extended
+  // Returns a new instruction that invokes the UMin GLSL.std.450 extended
+  // instruction with the two given operands.  That is, the result of the
+  // instruction is:
+  //  - |x| if |x| is unsigned-less than |y|
+  //  - |y| otherwise
+  // We assume that |x| and |y| are scalar integer types with the same
+  // width.  The instruction is inserted before |where|.
+  opt::Instruction* MakeUMinInst(const analysis::TypeManager& tm,
+                                 Instruction* x, Instruction* y,
+                                 Instruction* where);
+
+  // Returns a new instruction that invokes the SClamp GLSL.std.450 extended
   // instruction with the three given operands.  That is, the result of the
   // instruction is:
-  //  - |min| if |x| is unsigned-less than |min|
-  //  - |max| if |x| is unsigned-more than |max|
+  //  - |min| if |x| is signed-less than |min|
+  //  - |max| if |x| is signed-more than |max|
   //  - |x| otherwise.
-  // We assume that |min| is unsigned-less-or-equal to |max|, and that the
+  // We assume that |min| is signed-less-or-equal to |max|, and that the
   // operands all have the same scalar integer type.  The instruction is
   // inserted before |where|.
-  opt::Instruction* MakeClampInst(Instruction* x, Instruction* min,
-                                  Instruction* max, Instruction* where);
+  opt::Instruction* MakeSClampInst(const analysis::TypeManager& tm,
+                                   Instruction* x, Instruction* min,
+                                   Instruction* max, Instruction* where);
 
   // Returns a new instruction which evaluates to the length the runtime array
   // referenced by the access chain at the specfied index.  The instruction is
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 58bd404..d38571e 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -271,7 +271,7 @@
        %uint_4 = OpConstant %uint 4
        )"
             << MainPrefix() << R"(
-       %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%uint_3")
+       %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%int_3")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
@@ -329,7 +329,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_3 = OpConstant %int 3
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -354,7 +354,7 @@
        ; CHECK-DAG: %short_3 = OpConstant %short 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] UClamp %i %short_0 %short_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] SClamp %i %short_0 %short_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -375,11 +375,11 @@
             << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-NOT: = OpTypeInt 32
-       ; CHECK-DAG: %ushort_0 = OpConstant %ushort 0
-       ; CHECK-DAG: %ushort_3 = OpConstant %ushort 3
+       ; CHECK-DAG: %short_0 = OpConstant %short 0
+       ; CHECK-DAG: %short_3 = OpConstant %short 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] UClamp %i %ushort_0 %ushort_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -404,7 +404,7 @@
        ; CHECK-DAG: %long_3 = OpConstant %long 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -425,11 +425,11 @@
             << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-NOT: = OpTypeInt 32
-       ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
-       ; CHECK-DAG: %ulong_3 = OpConstant %ulong 3
+       ; CHECK-DAG: %long_0 = OpConstant %long 0
+       ; CHECK-DAG: %long_3 = OpConstant %long 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -486,10 +486,9 @@
        %uint_1 = OpConstant %uint 1
        %uint_4 = OpConstant %uint 4
        )" << MainPrefix() << R"(
-       ; CHECK: %uint_3 = OpConstant %uint 3
+       ; CHECK: %int_3 = OpConstant %int 3
        %var = OpVariable %var_ty Function)"
-            << ACCheck(ac, "%uint_4 %uint_1", "%uint_3 %uint_1")
-            << MainSuffix();
+            << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
@@ -529,7 +528,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_3 = OpConstant %int 3
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -585,7 +584,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_199 = OpConstant %int 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -606,11 +605,11 @@
        %i = OpUndef %short
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %uint_0 = OpConstant %uint 0
-       ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %int_69999 = OpConstant %int 69999
        ; CHECK: OpLabel
        ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -631,11 +630,11 @@
        %i = OpUndef %ushort
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %uint_0 = OpConstant %uint 0
-       ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %int_69999 = OpConstant %int 69999
        ; CHECK: OpLabel
        ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -656,10 +655,10 @@
        %i = OpUndef %uint
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %uint_0 = OpConstant %uint 0
-       ; CHECK-DAG: %uint_199 = OpConstant %uint 199
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %int_199 = OpConstant %int 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %uint_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -683,7 +682,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_199 = OpConstant %int 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -707,7 +706,7 @@
        ; CHECK-DAG: %long_0 = OpConstant %long 0
        ; CHECK-DAG: %long_199 = OpConstant %long 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -728,16 +727,91 @@
        %i = OpUndef %ulong
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
-       ; CHECK-DAG: %ulong_199 = OpConstant %ulong 199
+       ; CHECK-DAG: %long_0 = OpConstant %long 0
+       ; CHECK-DAG: %long_199 = OpConstant %long 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
 
+TEST_F(GraphicsRobustAccessTest,
+       ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) {
+  for (auto* ac : AccessChains()) {
+    std::ostringstream shaders;
+    shaders << "OpCapability Int16\n"
+            << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort()
+            << TypesInt() << TypesFloat() << R"(
+       %uint_50000 = OpConstant %uint 50000
+       %arr = OpTypeArray %float %uint_50000
+       %var_ty = OpTypePointer Function %arr
+       %ptr_ty = OpTypePointer Function %float
+       %i = OpUndef %ushort
+       )" << MainPrefix() << R"(
+       ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+       ; CHECK-DAG: %short_0 = OpConstant %short 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767
+       ; CHECK: OpLabel
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]]
+       %var = OpVariable %var_ty Function)"
+            << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
+    SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+  }
+}
+
+TEST_F(GraphicsRobustAccessTest,
+       ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) {
+  for (auto* ac : AccessChains()) {
+    std::ostringstream shaders;
+    shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
+            << TypesFloat() << R"(
+       %uint_3000000000 = OpConstant %uint 3000000000
+       %arr = OpTypeArray %float %uint_3000000000
+       %var_ty = OpTypePointer Function %arr
+       %ptr_ty = OpTypePointer Function %float
+       %i = OpUndef %uint
+       )" << MainPrefix() << R"(
+       ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
+       ; CHECK: OpLabel
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]]
+       %var = OpVariable %var_ty Function)"
+            << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
+    SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+  }
+}
+
+TEST_F(GraphicsRobustAccessTest,
+       ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) {
+  for (auto* ac : AccessChains()) {
+    std::ostringstream shaders;
+    shaders << "OpCapability Int64\n"
+            << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
+            << TypesLong()
+            << TypesFloat()
+            // 2^63 == 9,223,372,036,854,775,807
+            << R"(
+       %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999
+       %arr = OpTypeArray %float %ulong_9223372036854775999
+       %var_ty = OpTypePointer Function %arr
+       %ptr_ty = OpTypePointer Function %float
+       %i = OpUndef %ulong
+       )"
+            << MainPrefix() << R"(
+       ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+       ; CHECK-DAG: %long_0 = OpConstant %long 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807
+       ; CHECK: OpLabel
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]]
+       %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
+            << MainSuffix();
+    SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+  }
+}
+
 TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -753,9 +827,11 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
+       ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %uint_5 %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]]
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -908,11 +984,13 @@
        %int_0 = OpConstant %int 0
        %int_2 = OpConstant %int 2
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK: %int_1 = OpConstant %int 1
+       ; CHECK-DAG: %int_1 = OpConstant %int 1
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %int_0 %int_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]")
             << MainSuffix();
@@ -937,11 +1015,13 @@
        ; CHECK: %uint = OpTypeInt 32 0
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
             << MainSuffix();
@@ -966,11 +1046,13 @@
        ; CHECK: %uint = OpTypeInt 32 0
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
             << MainSuffix();
@@ -993,10 +1075,12 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %int_1 = OpConstant %int 1
        ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
             << MainSuffix();
@@ -1019,10 +1103,12 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
             << MainSuffix();
@@ -1046,11 +1132,13 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %long_0 = OpConstant %long 0
        ; CHECK-DAG: %long_1 = OpConstant %long 1
+       ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
        ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]]
        )" << MainPrefix()
             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -1073,11 +1161,13 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
        ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1
+       ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
        ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]]
        )" << MainPrefix()
             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -1109,11 +1199,13 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_3 = OpConstant %int 3
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
-       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %int_3
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
+       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3
        )" << MainPrefix()
             << ACCheck(ac, "%int_2 %i %int_1 %j",
                        "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]")
@@ -1148,6 +1240,7 @@
        ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_9 = OpConstant %int 9
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; This access chain is manufatured only so we can compute the array length.
        ; Note that the %int_9 is already clamped
@@ -1155,7 +1248,8 @@
             << R"( %[[ssbo_p]] %var %int_9
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
        )" << MainPrefix()
             << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2",
                        "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2")
@@ -1195,8 +1289,9 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_9 = OpConstant %int 9
        ; CHECK-DAG: %int_3 = OpConstant %int 3
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
        ; CHECK: %ac_ssbo = )" << ac
             << R"( %ssbo_pty %var %[[clamp_i]]
        ; CHECK: %ac_rtarr = )"
@@ -1207,8 +1302,9 @@
        ; definition to find the base pointer %ac_ssbo.
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
-       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
+       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
        ; CHECK: %ac = )" << ac
             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
        ; CHECK-NOT: AccessChain
@@ -1258,8 +1354,9 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_9 = OpConstant %int 9
        ; CHECK-DAG: %int_3 = OpConstant %int 3
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
        ; CHECK: %ac_ssbo = )" << ac
             << R"( %ssbo_pty %var %[[clamp_i]]
        ; CHECK: %bb1 = OpLabel
@@ -1272,8 +1369,9 @@
        ; definition to find the base pointer %ac_ssbo.
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
-       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
+       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
        ; CHECK: %ac = )" << ac
             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
        ; CHECK-NOT: AccessChain