diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 509051de8..2998ec1ec 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -827,6 +827,16 @@ Optimizer::PassToken CreateSplitInvalidUnreachablePass(); // 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 e309d3a4e..22c979cf3 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 #include #include +#include #include #include "constants.h" @@ -246,6 +251,7 @@ bool GraphicsRobustAccessPass::ProcessAFunction(opt::Function* function) { } 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 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( 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 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( 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 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( 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 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( } 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 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( &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 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( return; } clamp_to_count(idx, array_len); + if (module_status_.failed) return; pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); } break; @@ -557,22 +613,51 @@ opt::Instruction* opt::GraphicsRobustAccessPass::WidenInteger( 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 @@ Instruction* GraphicsRobustAccessPass::MakeRuntimeArrayLengthInst( 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 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( {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_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( {{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 b21154e5c..6fc692c13 100644 --- a/source/opt/graphics_robust_access_pass.h +++ b/source/opt/graphics_robust_access_pass.h @@ -18,13 +18,13 @@ #include #include -#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 @@ class GraphicsRobustAccessPass : public Pass { // 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 @@ class GraphicsRobustAccessPass : public Pass { 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 @@ class GraphicsRobustAccessPass : public Pass { 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 58bd404da..d38571e79 100644 --- a/test/opt/graphics_robust_access_test.cpp +++ b/test/opt/graphics_robust_access_test.cpp @@ -271,7 +271,7 @@ TEST_F(GraphicsRobustAccessTest, ACVectorExcessConstantClamped) { %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(shaders.str(), true); } @@ -329,7 +329,7 @@ TEST_F(GraphicsRobustAccessTest, ACVectorGeneralClamped) { ; 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(shaders.str(), true); @@ -354,7 +354,7 @@ TEST_F(GraphicsRobustAccessTest, ACVectorGeneralShortClamped) { ; 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(shaders.str(), true); @@ -375,11 +375,11 @@ TEST_F(GraphicsRobustAccessTest, ACVectorGeneralUShortClamped) { << 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(shaders.str(), true); @@ -404,7 +404,7 @@ TEST_F(GraphicsRobustAccessTest, ACVectorGeneralLongClamped) { ; 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(shaders.str(), true); @@ -425,11 +425,11 @@ TEST_F(GraphicsRobustAccessTest, ACVectorGeneralULongClamped) { << 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(shaders.str(), true); @@ -486,10 +486,9 @@ TEST_F(GraphicsRobustAccessTest, ACMatrixExcessConstantClamped) { %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(shaders.str(), true); } } @@ -529,7 +528,7 @@ TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) { ; 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(shaders.str(), true); @@ -585,7 +584,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) { ; 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(shaders.str(), true); @@ -606,11 +605,11 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) { %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(shaders.str(), true); @@ -631,11 +630,11 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) { %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(shaders.str(), true); @@ -656,10 +655,10 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) { %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(shaders.str(), true); @@ -683,7 +682,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) { ; 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(shaders.str(), true); @@ -707,7 +706,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) { ; 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(shaders.str(), true); @@ -728,16 +727,91 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) { %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(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(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(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(shaders.str(), true); + } +} + TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) { for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -753,9 +827,11 @@ TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) { ; 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(shaders.str(), true); @@ -908,11 +984,13 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) { %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 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) { ; 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(shaders.str(), true); @@ -1073,11 +1161,13 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) { ; 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(shaders.str(), true); @@ -1109,11 +1199,13 @@ TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { << 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 @@ TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) { ; 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 @@ TEST_F(GraphicsRobustAccessTest, ; 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 @@ TEST_F(GraphicsRobustAccessTest, ; 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