diff --git a/layers/gpuav/instrumentation/sanitizer.cpp b/layers/gpuav/instrumentation/sanitizer.cpp index 90d1c6ad437..4817af4381c 100644 --- a/layers/gpuav/instrumentation/sanitizer.cpp +++ b/layers/gpuav/instrumentation/sanitizer.cpp @@ -79,6 +79,23 @@ void RegisterSanitizer(Validator &gpuav, CommandBufferSubState &cb) { strm << ", but it must be 0, 1, 2, or 3" << GetSpirvSpecLink(spv::OpImageGather); out_vuid_msg = "SPIRV-Sanitizer-Image-Gather"; } break; + case kErrorSubCodeSanitizerPow: { + // Pow is only valid with a scalar/vector of 16/32-bit float + const uint32_t vector_size = error_record[kInstLogErrorParameterOffset_0]; + // Casting produces artifacts in float value, need to memcpy + float x_value = 0.0f; + float y_value = 0.0f; + memcpy(&x_value, &error_record[kInstLogErrorParameterOffset_1], sizeof(float)); + memcpy(&y_value, &error_record[kInstLogErrorParameterOffset_2], sizeof(float)); + strm << "Pow (from GLSL.std.450) has an undefined result because operand (x < 0) or (x == 0 && y <= 0)\n "; + if (vector_size > 0) { + // Would need a new way to print more than 2 bytes out to get this to work + strm << "Using a vector of size " << vector_size << " but currently only can print out scalar values"; + } else { + strm << "X == " << x_value << ", Y == " << y_value; + } + out_vuid_msg = "SPIRV-Sanitizer-Pow"; + } break; default: error_found = false; break; diff --git a/layers/gpuav/shaders/gpuav_error_codes.h b/layers/gpuav/shaders/gpuav_error_codes.h index e9cba85c217..a836a576cb2 100644 --- a/layers/gpuav/shaders/gpuav_error_codes.h +++ b/layers/gpuav/shaders/gpuav_error_codes.h @@ -112,7 +112,8 @@ const int kErrorSubCode_IndexedDraw_OOBInstanceIndex = 2; const int kErrorSubCodeSanitizerEmpty = 0; // reserved to mean no error was set const int kErrorSubCodeSanitizerDivideZero = 1; const int kErrorSubCodeSanitizerImageGather = 2; -const int kErrorSubCodeSanitizerCount = 3; // update when adding new item +const int kErrorSubCodeSanitizerPow = 3; +const int kErrorSubCodeSanitizerCount = 4; // update when adding new item // Pre Draw // diff --git a/layers/gpuav/shaders/instrumentation/sanitizer.comp b/layers/gpuav/shaders/instrumentation/sanitizer.comp index 4a706b4afa0..56330248846 100644 --- a/layers/gpuav/shaders/instrumentation/sanitizer.comp +++ b/layers/gpuav/shaders/instrumentation/sanitizer.comp @@ -48,3 +48,19 @@ void inst_sanitizer_image_gather(const uint inst_offset, const uint component) 0, 0); } + +bool inst_sanitizer_pow(const bool is_invalid, const uint inst_offset, const uint vector_size, const uint x, const uint y) +{ + // Using a boolean here allows us to just calculate an expression and pass the result here to save us needing to do un-fun control flow. + // In practice, the driver's compiler will detect this easily and fold in the function for us + if (is_invalid) { + error_payload = ErrorPayload( + inst_offset, + SpecConstantLinkShaderId | (kErrorGroupInstSanitizer << kErrorGroupShift) | (kErrorSubCodeSanitizerPow << kErrorSubCodeShift), + vector_size, + x, + y); + return false; + } + return true; +} \ No newline at end of file diff --git a/layers/gpuav/spirv/sanitizer_pass.cpp b/layers/gpuav/spirv/sanitizer_pass.cpp index 7fc5fba9fcb..ac98ad47ad1 100644 --- a/layers/gpuav/spirv/sanitizer_pass.cpp +++ b/layers/gpuav/spirv/sanitizer_pass.cpp @@ -18,6 +18,8 @@ #include "function_basic_block.h" #include "gpuav/shaders/gpuav_error_codes.h" #include "module.h" +#include +#include #include #include @@ -32,7 +34,9 @@ const static OfflineModule kOfflineModule = {instrumentation_sanitizer_comp, ins const static OfflineFunction kOfflineFunctions[glsl::kErrorSubCodeSanitizerCount] = { {"empty", 0}, {"inst_sanitizer_divide_by_zero", instrumentation_sanitizer_comp_function_0_offset}, - {"inst_sanitizer_image_gather", instrumentation_sanitizer_comp_function_1_offset}}; + {"inst_sanitizer_image_gather", instrumentation_sanitizer_comp_function_1_offset}, + {"inst_sanitizer_pow", instrumentation_sanitizer_comp_function_2_offset}, +}; SanitizerPass::SanitizerPass(Module& module) : Pass(module, kOfflineModule) { for (uint32_t i = 0; i < glsl::kErrorSubCodeSanitizerCount; i++) { @@ -61,11 +65,11 @@ uint32_t SanitizerPass::DivideByZeroCheck(BasicBlock& block, InstructionIt* inst block.CreateInstruction(compare_op, {bool_type.Id(), compare_id, null_type_id, divisor_id}, inst_it); return compare_id; } else { - const Type& bool_vector_type = type_manager_.GetTypeVector(bool_type, vector_size); + const uint32_t bool_vector_type_id = type_manager_.GetTypeVector(bool_type, vector_size).Id(); const uint32_t zero_id = type_manager_.GetConstantZeroVector(*meta.result_type).Id(); const uint32_t compare_id = module_.TakeNextId(); - block.CreateInstruction(compare_op, {bool_vector_type.Id(), compare_id, zero_id, divisor_id}, inst_it); + block.CreateInstruction(compare_op, {bool_vector_type_id, compare_id, zero_id, divisor_id}, inst_it); const uint32_t any_id = module_.TakeNextId(); block.CreateInstruction(spv::OpAny, {bool_type.Id(), any_id, compare_id}, inst_it); @@ -73,6 +77,49 @@ uint32_t SanitizerPass::DivideByZeroCheck(BasicBlock& block, InstructionIt* inst } } +// Based off https://godbolt.org/z/dbToMGKTd - but found can hand-roll the spirv much better +// bool is_invalid = (x < 0.0 || (x == 0.0 && y <= 0.0)); +// Returns an ID of type OpTypeBool +uint32_t SanitizerPass::PowCheck(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) { + const Type& bool_type = type_manager_.GetTypeBool(); + const uint32_t vector_size = meta.result_type->VectorSize(); + + uint32_t bool_compare_type_id = 0; + uint32_t null_type_id = 0; + if (vector_size == 0) { + bool_compare_type_id = bool_type.Id(); + null_type_id = type_manager_.GetConstantNull(*meta.result_type).Id(); + } else { + bool_compare_type_id = type_manager_.GetTypeVector(bool_type, vector_size).Id(); + null_type_id = type_manager_.GetConstantZeroVector(*meta.result_type).Id(); + } + + const uint32_t x_value_id = meta.target_instruction->Word(5); + const uint32_t y_value_id = meta.target_instruction->Word(6); + + const uint32_t compare_1_id = module_.TakeNextId(); + const uint32_t compare_2_id = module_.TakeNextId(); + const uint32_t compare_3_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpFOrdLessThan, {bool_compare_type_id, compare_1_id, x_value_id, null_type_id}, inst_it); + block.CreateInstruction(spv::OpFOrdEqual, {bool_compare_type_id, compare_2_id, x_value_id, null_type_id}, inst_it); + block.CreateInstruction(spv::OpFOrdLessThanEqual, {bool_compare_type_id, compare_3_id, y_value_id, null_type_id}, inst_it); + + const uint32_t compare_and_id = module_.TakeNextId(); + const uint32_t compare_or_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpLogicalAnd, {bool_compare_type_id, compare_and_id, compare_2_id, compare_3_id}, inst_it); + block.CreateInstruction(spv::OpLogicalOr, {bool_compare_type_id, compare_or_id, compare_1_id, compare_and_id}, inst_it); + + uint32_t result_bool_id = 0; + if (vector_size == 0) { + result_bool_id = compare_or_id; + } else { + result_bool_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpAny, {bool_type.Id(), result_bool_id, compare_or_id}, inst_it); + } + + return result_bool_id; +} + uint32_t SanitizerPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) { const uint32_t function_result = module_.TakeNextId(); const uint32_t function_def = GetLinkFunctionId(meta.sub_code); @@ -99,6 +146,34 @@ uint32_t SanitizerPass::CreateFunctionCall(BasicBlock& block, InstructionIt* ins const_cast(meta.target_instruction)->UpdateWord(5, safe_value_id); block.CreateInstruction(spv::OpFunctionCall, {void_type, function_result, function_def, inst_position_id, component_value_id}, inst_it); + } else if (meta.sub_code == glsl::kErrorSubCodeSanitizerPow) { + const uint32_t is_invalid_id = PowCheck(block, inst_it, meta); + + const uint32_t bool_type = type_manager_.GetTypeBool().Id(); + const uint32_t vector_size = meta.result_type->VectorSize(); + const uint32_t vector_size_id = type_manager_.CreateConstantUInt32(vector_size).Id(); + + uint32_t x_value_id = 0; + uint32_t y_value_id = 0; + if (vector_size == 0) { + // cast as uint as that is how we are encoding the payload currently + const Type& uint32_type = type_manager_.GetTypeInt(32, false); + const uint32_t x_value_float = meta.target_instruction->Word(5); + const uint32_t y_value_float = meta.target_instruction->Word(6); + x_value_id = module_.TakeNextId(); + y_value_id = module_.TakeNextId(); + block.CreateInstruction(spv::OpBitcast, {uint32_type.Id(), x_value_id, x_value_float}, inst_it); + block.CreateInstruction(spv::OpBitcast, {uint32_type.Id(), y_value_id, y_value_float}, inst_it); + } else { + // Put something valid, these are ignored on when printing error + x_value_id = type_manager_.GetConstantZeroUint32().Id(); + y_value_id = type_manager_.GetConstantZeroUint32().Id(); + } + + block.CreateInstruction( + spv::OpFunctionCall, + {bool_type, function_result, function_def, is_invalid_id, inst_position_id, vector_size_id, x_value_id, y_value_id}, + inst_it); } else { assert(false); } @@ -136,6 +211,7 @@ bool SanitizerPass::IsConstantZero(const Constant& constant) const { bool SanitizerPass::RequiresInstrumentation(const Instruction& inst, InstructionMeta& meta) { const spv::Op opcode = (spv::Op)inst.Opcode(); + meta.target_instruction = &inst; if (IsValueIn(opcode, {spv::OpUDiv, spv::OpSDiv, spv::OpUMod, spv::OpSMod, spv::OpSRem, spv::OpFMod, spv::OpFRem})) { // Note - It is valid to divide by zero for a float (you get NaN), but invalid for an int. @@ -154,7 +230,6 @@ bool SanitizerPass::RequiresInstrumentation(const Instruction& inst, Instruction } meta.result_type = type_manager_.FindTypeById(inst.TypeId()); - meta.target_instruction = &inst; meta.sub_code = glsl::kErrorSubCodeSanitizerDivideZero; return true; } else if (opcode == spv::OpImageGather) { @@ -163,19 +238,38 @@ bool SanitizerPass::RequiresInstrumentation(const Instruction& inst, Instruction const uint32_t constant_value = constant->GetValueUint32(); // TODO - Support spec constants if (!constant->is_spec_constant_ && constant_value > 3) { - meta.target_instruction = &inst; meta.sub_code = glsl::kErrorSubCodeSanitizerImageGather; meta.skip_safe_mode = true; meta.constant_value = constant_value; return true; } } + } else if (opcode == spv::OpExtInst && inst.Word(3) == glsl_std450_id_) { + uint32_t glsl_opcode = inst.Word(4); + if (glsl_opcode == GLSLstd450Pow) { + meta.sub_code = glsl::kErrorSubCodeSanitizerPow; + } else { + return false; + } + + // all of these only have results that are undefined + meta.skip_safe_mode = true; + meta.result_type = type_manager_.FindTypeById(inst.TypeId()); + return true; } return false; } bool SanitizerPass::Instrument() { + for (const auto& inst : module_.ext_inst_imports_) { + const char* import_string = inst->GetAsString(2); + if (strcmp(import_string, "GLSL.std.450") == 0) { + glsl_std450_id_ = inst->ResultId(); + break; + } + } + // Can safely loop function list as there is no injecting of new Functions until linking time for (const auto& function : module_.functions_) { if (function->instrumentation_added_) { diff --git a/layers/gpuav/spirv/sanitizer_pass.h b/layers/gpuav/spirv/sanitizer_pass.h index 273b95a7960..ea20b72d368 100644 --- a/layers/gpuav/spirv/sanitizer_pass.h +++ b/layers/gpuav/spirv/sanitizer_pass.h @@ -54,6 +54,7 @@ class SanitizerPass : public Pass { bool RequiresInstrumentation(const Instruction& inst, InstructionMeta& meta); uint32_t DivideByZeroCheck(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta); + uint32_t PowCheck(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta); uint32_t CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta); uint32_t GetLinkFunctionId(uint32_t sub_code); @@ -62,6 +63,8 @@ class SanitizerPass : public Pass { // Function IDs to link in uint32_t link_function_ids_[glsl::kErrorSubCodeSanitizerCount]; + + uint32_t glsl_std450_id_ = 0; // GLSL.std.450 }; } // namespace spirv diff --git a/layers/gpuav/spirv/type_manager.cpp b/layers/gpuav/spirv/type_manager.cpp index 7c21215cabb..91b3d76a56f 100644 --- a/layers/gpuav/spirv/type_manager.cpp +++ b/layers/gpuav/spirv/type_manager.cpp @@ -482,12 +482,16 @@ const Constant& TypeManager::AddConstant(std::unique_ptr new_inst, if (inst->Opcode() == spv::OpConstant) { if (type.inst_.Opcode() == spv::OpTypeInt && type.inst_.Word(2) == 32) { - int_32bit_constants_.push_back(new_constant); - } else if (type.inst_.Opcode() == spv::OpTypeFloat && type.inst_.Word(2) == 32) { - float_32bit_constants_.push_back(new_constant); + int_32bit_constants_.emplace_back(new_constant); + } else if (type.inst_.Opcode() == spv::OpTypeFloat) { + if (type.inst_.Word(2) == 16) { + float_16bit_constants_.emplace_back(new_constant); + } else if (type.inst_.Word(2) == 32) { + float_32bit_constants_.emplace_back(new_constant); + } } } else if (inst->Opcode() == spv::OpConstantNull) { - null_constants_.push_back(new_constant); + null_constants_.emplace_back(new_constant); } return *new_constant; @@ -502,6 +506,15 @@ const Constant* TypeManager::FindConstantInt32(uint32_t type_id, uint32_t value) return nullptr; } +const Constant* TypeManager::FindConstantFloat16(uint32_t type_id, uint32_t value) const { + for (const auto constant : float_16bit_constants_) { + if (constant->type_.Id() == type_id && value == constant->inst_.Word(3)) { + return constant; + } + } + return nullptr; +} + const Constant* TypeManager::FindConstantFloat32(uint32_t type_id, uint32_t value) const { for (const auto constant : float_32bit_constants_) { if (constant->type_.Id() == type_id && value == constant->inst_.Word(3)) { @@ -561,6 +574,21 @@ const Constant& TypeManager::GetConstantOneUint32() { return *uint_32bit_one_constants_; } +// It is common to use float16(0) as a default, so having it cached is helpful +const Constant& TypeManager::GetConstantZeroFloat16() { + if (!float_16bit_zero_constants_) { + const Type& float_16_type = GetTypeFloat(16); + float_16bit_zero_constants_ = FindConstantFloat16(float_16_type.Id(), 0); + if (!float_16bit_zero_constants_) { + const uint32_t constant_id = module_.TakeNextId(); + auto new_inst = std::make_unique(4, spv::OpConstant); + new_inst->Fill({float_16_type.Id(), constant_id, 0}); + float_16bit_zero_constants_ = &AddConstant(std::move(new_inst), float_16_type); + } + } + return *float_16bit_zero_constants_; +} + // It is common to use float(0) as a default, so having it cached is helpful const Constant& TypeManager::GetConstantZeroFloat32() { if (!float_32bit_zero_constants_) { diff --git a/layers/gpuav/spirv/type_manager.h b/layers/gpuav/spirv/type_manager.h index 1c03b9376d5..9b5cba8de34 100644 --- a/layers/gpuav/spirv/type_manager.h +++ b/layers/gpuav/spirv/type_manager.h @@ -62,6 +62,7 @@ struct Type { bool IsArray() const; bool IsSignedInt() const; bool IsIVec3(const TypeManager& type_manager) const; + // If returns 0, means it is a scalar uint32_t VectorSize() const; // 64-bit floats/int take up 2 dwords bool Is64Bit() const; @@ -145,12 +146,14 @@ class TypeManager { const Constant& AddConstant(std::unique_ptr new_inst, const Type& type); const Constant* FindConstantById(uint32_t id) const; const Constant* FindConstantInt32(uint32_t type_id, uint32_t value) const; + const Constant* FindConstantFloat16(uint32_t type_id, uint32_t value) const; const Constant* FindConstantFloat32(uint32_t type_id, uint32_t value) const; // most constants are uint const Constant& CreateConstantUInt32(uint32_t value); const Constant& GetConstantUInt32(uint32_t value); const Constant& GetConstantZeroUint32(); const Constant& GetConstantOneUint32(); + const Constant& GetConstantZeroFloat16(); const Constant& GetConstantZeroFloat32(); const Constant& GetConstantZeroVec3(); const Constant& GetConstantZeroUvec4(); @@ -196,9 +199,11 @@ class TypeManager { std::vector linking_struct_types_; std::vector int_32bit_constants_; + std::vector float_16bit_constants_; std::vector float_32bit_constants_; const Constant* uint_32bit_zero_constants_ = nullptr; const Constant* uint_32bit_one_constants_ = nullptr; + const Constant* float_16bit_zero_constants_ = nullptr; const Constant* float_32bit_zero_constants_ = nullptr; const Constant* vec3_zero_constants_ = nullptr; const Constant* uvec4_zero_constants_ = nullptr; diff --git a/layers/vulkan/generated/gpuav_offline_spirv.cpp b/layers/vulkan/generated/gpuav_offline_spirv.cpp index 6034a732195..bb8681cc3b5 100644 --- a/layers/vulkan/generated/gpuav_offline_spirv.cpp +++ b/layers/vulkan/generated/gpuav_offline_spirv.cpp @@ -719,9 +719,9 @@ 0x000200f8, 0x000000a0, 0x000200fe, 0x000000b0, 0x00010038}; [[maybe_unused]] const uint32_t instrumentation_ray_query_comp_function_0_offset = 334; -[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_size = 369; -[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp[369] = { - 0x07230203, 0x00010300, 0x0008000b, 0x00000026, 0x00000000, 0x00020011, 0x00000001, 0x00020011, 0x00000005, 0x0006000b, +[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_size = 483; +[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp[483] = { + 0x07230203, 0x00010300, 0x0008000b, 0x00000034, 0x00000000, 0x00020011, 0x00000001, 0x00020011, 0x00000005, 0x0006000b, 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, 0x00030003, 0x00000002, 0x000001c2, 0x00070004, 0x415f4c47, 0x675f4252, 0x735f7570, 0x65646168, 0x6e695f72, 0x00343674, 0x00070004, 0x455f4c47, 0x625f5458, 0x65666675, 0x65725f72, 0x65726566, 0x0065636e, 0x00080004, 0x455f4c47, 0x625f5458, 0x65666675, 0x65725f72, @@ -734,32 +734,45 @@ 0x00746573, 0x00040005, 0x00000007, 0x6f63706f, 0x00006564, 0x00050005, 0x00000008, 0x74636576, 0x735f726f, 0x00657a69, 0x000b0005, 0x0000000f, 0x74736e69, 0x6e61735f, 0x7a697469, 0x695f7265, 0x6567616d, 0x7461675f, 0x28726568, 0x753b3175, 0x00003b31, 0x00050005, 0x0000000d, 0x74736e69, 0x66666f5f, 0x00746573, 0x00050005, 0x0000000e, 0x706d6f63, 0x6e656e6f, - 0x00000074, 0x00060005, 0x00000013, 0x6f727245, 0x79615072, 0x64616f6c, 0x00000000, 0x00060006, 0x00000013, 0x00000000, - 0x74736e69, 0x66666f5f, 0x00746573, 0x00090006, 0x00000013, 0x00000001, 0x64616873, 0x655f7265, 0x726f7272, 0x636e655f, - 0x6e69646f, 0x00000067, 0x00060006, 0x00000013, 0x00000002, 0x61726170, 0x6574656d, 0x00305f72, 0x00060006, 0x00000013, - 0x00000003, 0x61726170, 0x6574656d, 0x00315f72, 0x00060006, 0x00000013, 0x00000004, 0x61726170, 0x6574656d, 0x00325f72, - 0x00060005, 0x00000015, 0x6f727265, 0x61705f72, 0x616f6c79, 0x00000064, 0x00090005, 0x00000016, 0x63657053, 0x736e6f43, - 0x746e6174, 0x6b6e694c, 0x64616853, 0x64497265, 0x00000000, 0x000c0047, 0x00000009, 0x00000029, 0x74736e69, 0x6e61735f, - 0x7a697469, 0x645f7265, 0x64697669, 0x79625f65, 0x72657a5f, 0x0000006f, 0x00000000, 0x000b0047, 0x0000000f, 0x00000029, - 0x74736e69, 0x6e61735f, 0x7a697469, 0x695f7265, 0x6567616d, 0x7461675f, 0x00726568, 0x00000000, 0x00040047, 0x00000016, - 0x00000001, 0x00000000, 0x00020014, 0x00000002, 0x00040015, 0x00000003, 0x00000020, 0x00000000, 0x00070021, 0x00000004, - 0x00000002, 0x00000002, 0x00000003, 0x00000003, 0x00000003, 0x00020013, 0x0000000b, 0x00050021, 0x0000000c, 0x0000000b, - 0x00000003, 0x00000003, 0x0007001e, 0x00000013, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00040020, - 0x00000014, 0x00000006, 0x00000013, 0x0004003b, 0x00000014, 0x00000015, 0x00000006, 0x00040032, 0x00000003, 0x00000016, - 0x0dead001, 0x0004002b, 0x00000003, 0x00000017, 0x0b000000, 0x00060034, 0x00000003, 0x00000018, 0x000000c5, 0x00000016, - 0x00000017, 0x0004002b, 0x00000003, 0x00000019, 0x00040000, 0x00060034, 0x00000003, 0x0000001a, 0x000000c5, 0x00000018, - 0x00000019, 0x0004002b, 0x00000003, 0x0000001b, 0x00000000, 0x0003002a, 0x00000002, 0x0000001d, 0x00030029, 0x00000002, - 0x0000001f, 0x00060034, 0x00000003, 0x00000022, 0x000000c5, 0x00000016, 0x00000017, 0x0004002b, 0x00000003, 0x00000023, - 0x00080000, 0x00060034, 0x00000003, 0x00000024, 0x000000c5, 0x00000022, 0x00000023, 0x00050036, 0x00000002, 0x00000009, - 0x00000000, 0x00000004, 0x00030037, 0x00000002, 0x00000005, 0x00030037, 0x00000003, 0x00000006, 0x00030037, 0x00000003, - 0x00000007, 0x00030037, 0x00000003, 0x00000008, 0x000200f8, 0x0000000a, 0x000300f7, 0x00000012, 0x00000000, 0x000400fa, - 0x00000005, 0x00000011, 0x00000012, 0x000200f8, 0x00000011, 0x00080050, 0x00000013, 0x0000001c, 0x00000006, 0x0000001a, - 0x00000007, 0x00000008, 0x0000001b, 0x0003003e, 0x00000015, 0x0000001c, 0x000200fe, 0x0000001d, 0x000200f8, 0x00000012, - 0x000200fe, 0x0000001f, 0x00010038, 0x00050036, 0x0000000b, 0x0000000f, 0x00000000, 0x0000000c, 0x00030037, 0x00000003, - 0x0000000d, 0x00030037, 0x00000003, 0x0000000e, 0x000200f8, 0x00000010, 0x00080050, 0x00000013, 0x00000025, 0x0000000d, - 0x00000024, 0x0000000e, 0x0000001b, 0x0000001b, 0x0003003e, 0x00000015, 0x00000025, 0x000100fd, 0x00010038}; -[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_function_0_offset = 297; -[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_function_1_offset = 343; + 0x00000074, 0x000b0005, 0x00000017, 0x74736e69, 0x6e61735f, 0x7a697469, 0x705f7265, 0x6228776f, 0x31753b31, 0x3b31753b, + 0x753b3175, 0x00003b31, 0x00050005, 0x00000012, 0x695f7369, 0x6c61766e, 0x00006469, 0x00050005, 0x00000013, 0x74736e69, + 0x66666f5f, 0x00746573, 0x00050005, 0x00000014, 0x74636576, 0x735f726f, 0x00657a69, 0x00030005, 0x00000015, 0x00000078, + 0x00030005, 0x00000016, 0x00000079, 0x00060005, 0x0000001b, 0x6f727245, 0x79615072, 0x64616f6c, 0x00000000, 0x00060006, + 0x0000001b, 0x00000000, 0x74736e69, 0x66666f5f, 0x00746573, 0x00090006, 0x0000001b, 0x00000001, 0x64616873, 0x655f7265, + 0x726f7272, 0x636e655f, 0x6e69646f, 0x00000067, 0x00060006, 0x0000001b, 0x00000002, 0x61726170, 0x6574656d, 0x00305f72, + 0x00060006, 0x0000001b, 0x00000003, 0x61726170, 0x6574656d, 0x00315f72, 0x00060006, 0x0000001b, 0x00000004, 0x61726170, + 0x6574656d, 0x00325f72, 0x00060005, 0x0000001d, 0x6f727265, 0x61705f72, 0x616f6c79, 0x00000064, 0x00090005, 0x0000001e, + 0x63657053, 0x736e6f43, 0x746e6174, 0x6b6e694c, 0x64616853, 0x64497265, 0x00000000, 0x000c0047, 0x00000009, 0x00000029, + 0x74736e69, 0x6e61735f, 0x7a697469, 0x645f7265, 0x64697669, 0x79625f65, 0x72657a5f, 0x0000006f, 0x00000000, 0x000b0047, + 0x0000000f, 0x00000029, 0x74736e69, 0x6e61735f, 0x7a697469, 0x695f7265, 0x6567616d, 0x7461675f, 0x00726568, 0x00000000, + 0x00090047, 0x00000017, 0x00000029, 0x74736e69, 0x6e61735f, 0x7a697469, 0x705f7265, 0x0000776f, 0x00000000, 0x00040047, + 0x0000001e, 0x00000001, 0x00000000, 0x00020014, 0x00000002, 0x00040015, 0x00000003, 0x00000020, 0x00000000, 0x00070021, + 0x00000004, 0x00000002, 0x00000002, 0x00000003, 0x00000003, 0x00000003, 0x00020013, 0x0000000b, 0x00050021, 0x0000000c, + 0x0000000b, 0x00000003, 0x00000003, 0x00080021, 0x00000011, 0x00000002, 0x00000002, 0x00000003, 0x00000003, 0x00000003, + 0x00000003, 0x0007001e, 0x0000001b, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00040020, 0x0000001c, + 0x00000006, 0x0000001b, 0x0004003b, 0x0000001c, 0x0000001d, 0x00000006, 0x00040032, 0x00000003, 0x0000001e, 0x0dead001, + 0x0004002b, 0x00000003, 0x0000001f, 0x0b000000, 0x00060034, 0x00000003, 0x00000020, 0x000000c5, 0x0000001e, 0x0000001f, + 0x0004002b, 0x00000003, 0x00000021, 0x00040000, 0x00060034, 0x00000003, 0x00000022, 0x000000c5, 0x00000020, 0x00000021, + 0x0004002b, 0x00000003, 0x00000023, 0x00000000, 0x0003002a, 0x00000002, 0x00000025, 0x00030029, 0x00000002, 0x00000027, + 0x00060034, 0x00000003, 0x0000002a, 0x000000c5, 0x0000001e, 0x0000001f, 0x0004002b, 0x00000003, 0x0000002b, 0x00080000, + 0x00060034, 0x00000003, 0x0000002c, 0x000000c5, 0x0000002a, 0x0000002b, 0x00060034, 0x00000003, 0x00000030, 0x000000c5, + 0x0000001e, 0x0000001f, 0x0004002b, 0x00000003, 0x00000031, 0x000c0000, 0x00060034, 0x00000003, 0x00000032, 0x000000c5, + 0x00000030, 0x00000031, 0x00050036, 0x00000002, 0x00000009, 0x00000000, 0x00000004, 0x00030037, 0x00000002, 0x00000005, + 0x00030037, 0x00000003, 0x00000006, 0x00030037, 0x00000003, 0x00000007, 0x00030037, 0x00000003, 0x00000008, 0x000200f8, + 0x0000000a, 0x000300f7, 0x0000001a, 0x00000000, 0x000400fa, 0x00000005, 0x00000019, 0x0000001a, 0x000200f8, 0x00000019, + 0x00080050, 0x0000001b, 0x00000024, 0x00000006, 0x00000022, 0x00000007, 0x00000008, 0x00000023, 0x0003003e, 0x0000001d, + 0x00000024, 0x000200fe, 0x00000025, 0x000200f8, 0x0000001a, 0x000200fe, 0x00000027, 0x00010038, 0x00050036, 0x0000000b, + 0x0000000f, 0x00000000, 0x0000000c, 0x00030037, 0x00000003, 0x0000000d, 0x00030037, 0x00000003, 0x0000000e, 0x000200f8, + 0x00000010, 0x00080050, 0x0000001b, 0x0000002d, 0x0000000d, 0x0000002c, 0x0000000e, 0x00000023, 0x00000023, 0x0003003e, + 0x0000001d, 0x0000002d, 0x000100fd, 0x00010038, 0x00050036, 0x00000002, 0x00000017, 0x00000000, 0x00000011, 0x00030037, + 0x00000002, 0x00000012, 0x00030037, 0x00000003, 0x00000013, 0x00030037, 0x00000003, 0x00000014, 0x00030037, 0x00000003, + 0x00000015, 0x00030037, 0x00000003, 0x00000016, 0x000200f8, 0x00000018, 0x000300f7, 0x0000002f, 0x00000000, 0x000400fa, + 0x00000012, 0x0000002e, 0x0000002f, 0x000200f8, 0x0000002e, 0x00080050, 0x0000001b, 0x00000033, 0x00000013, 0x00000032, + 0x00000014, 0x00000015, 0x00000016, 0x0003003e, 0x0000001d, 0x00000033, 0x000200fe, 0x00000025, 0x000200f8, 0x0000002f, + 0x000200fe, 0x00000027, 0x00010038}; +[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_function_0_offset = 362; +[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_function_1_offset = 408; +[[maybe_unused]] const uint32_t instrumentation_sanitizer_comp_function_2_offset = 434; [[maybe_unused]] const uint32_t instrumentation_vertex_attribute_fetch_oob_vert_size = 1048; [[maybe_unused]] const uint32_t instrumentation_vertex_attribute_fetch_oob_vert[1048] = { diff --git a/layers/vulkan/generated/gpuav_offline_spirv.h b/layers/vulkan/generated/gpuav_offline_spirv.h index ac5277eaa31..9c6c9124bde 100644 --- a/layers/vulkan/generated/gpuav_offline_spirv.h +++ b/layers/vulkan/generated/gpuav_offline_spirv.h @@ -76,6 +76,7 @@ extern const uint32_t instrumentation_sanitizer_comp[]; // These offset match the function in the order they are declared in the GLSL source extern const uint32_t instrumentation_sanitizer_comp_function_0_offset; extern const uint32_t instrumentation_sanitizer_comp_function_1_offset; +extern const uint32_t instrumentation_sanitizer_comp_function_2_offset; extern const uint32_t instrumentation_vertex_attribute_fetch_oob_vert_size; extern const uint32_t instrumentation_vertex_attribute_fetch_oob_vert[]; diff --git a/tests/unit/gpu_av_shader_sanitizer.cpp b/tests/unit/gpu_av_shader_sanitizer.cpp index 04f5ec35729..62c64a33db2 100644 --- a/tests/unit/gpu_av_shader_sanitizer.cpp +++ b/tests/unit/gpu_av_shader_sanitizer.cpp @@ -491,3 +491,112 @@ TEST_F(NegativeGpuAVShaderSanitizer, ImageGather) { m_default_queue->SubmitAndWait(m_command_buffer); m_errorMonitor->VerifyFound(); } + +TEST_F(NegativeGpuAVShaderSanitizer, PowScalarZero) { + const char* cs_source = R"glsl( + #version 450 core + layout(set=0, binding=0) buffer SSBO { + float x; + float y; + float result; + }; + + void main() { + result = pow(x, y - 1.0f); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL, "SPIRV-Sanitizer-Pow"); +} + +TEST_F(NegativeGpuAVShaderSanitizer, PowScalarNegative) { + const char* cs_source = R"glsl( + #version 450 core + layout(set=0, binding=0) buffer SSBO { + float x; + float y; + float result; + }; + + void main() { + float a = x - 1.0f; + float b = y + 1.0f; + result = pow(a, b); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL, "SPIRV-Sanitizer-Pow"); +} + +TEST_F(NegativeGpuAVShaderSanitizer, PowScalarConstant) { + const char* cs_source = R"( + OpCapability Shader + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %var Binding 0 + OpDecorate %var DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_n1 = OpConstant %float -1.3 + %float_1 = OpConstant %float 1.3 + %SSBO = OpTypeStruct %float +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %var = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + %main = OpFunction %void None %4 + %6 = OpLabel + %16 = OpExtInst %float %2 Pow %float_n1 %float_1 + %24 = OpAccessChain %_ptr_StorageBuffer_float %var %int_0 + OpStore %24 %16 + OpReturn + OpFunctionEnd + )"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_ASM, "SPIRV-Sanitizer-Pow"); +} + +TEST_F(NegativeGpuAVShaderSanitizer, PowVectorZero) { + const char* cs_source = R"glsl( + #version 450 core + layout(set=0, binding=0) buffer SSBO { + vec2 x; + vec2 y; + vec2 result; + }; + + void main() { + x.x = 3.0f; + y.x = 1.0f; + result = pow(x, y); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL, "SPIRV-Sanitizer-Pow"); +} + +TEST_F(NegativeGpuAVShaderSanitizer, PowVectorNegative) { + const char* cs_source = R"glsl( + #version 450 core + layout(set=0, binding=0) buffer SSBO { + vec4 x; + vec4 y; + vec4 result; + }; + + void main() { + vec4 a = x - (1.0f); + vec4 b = y + (1.0f); + result = pow(a, b); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL, "SPIRV-Sanitizer-Pow"); +} \ No newline at end of file diff --git a/tests/unit/gpu_av_shader_sanitizer_positive.cpp b/tests/unit/gpu_av_shader_sanitizer_positive.cpp index 981dab07c9e..05e002b98ef 100644 --- a/tests/unit/gpu_av_shader_sanitizer_positive.cpp +++ b/tests/unit/gpu_av_shader_sanitizer_positive.cpp @@ -248,6 +248,7 @@ TEST_F(PositiveGpuAVShaderSanitizer, MultiplePass) { a = 2 / a; vec4 b = textureGather(tex, vec2(0), 0); c = mod(4.0, c); + float d = pow(c + 50.0f, 1.0f); } )glsl"; @@ -263,3 +264,63 @@ TEST_F(PositiveGpuAVShaderSanitizer, MultiplePass) { pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); pipe.CreateComputePipeline(); } + +TEST_F(PositiveGpuAVShaderSanitizer, Pow) { + const char* cs_source = R"glsl( + #version 460 + layout(set=0, binding=0) buffer SSBO { + float x; + float y; + float result; + vec3 resultVec; + }; + + void main() { + result = pow(x + 1.0f, y + 1.0f); + + vec3 a = vec3(x) + vec3(1.0f); + vec3 b = vec3(y) + vec3(1.0f); + resultVec = pow(a, b); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL); +} + +TEST_F(PositiveGpuAVShaderSanitizer, PowConstant) { + const char* cs_source = R"glsl( + #version 460 + layout(set=0, binding=0) buffer SSBO { + float x; + float y; + float result; + vec3 resultVec; + }; + + void main() { + result = pow(1.0f, 1.0f); + resultVec = pow(vec3(1.0f), vec3(1.0f)); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL); +} + +TEST_F(PositiveGpuAVShaderSanitizer, PowVectorMix) { + const char* cs_source = R"glsl( + #version 450 core + layout(set=0, binding=0) buffer SSBO { + vec3 x; + vec3 y; + vec3 result; + }; + + void main() { + x += vec3(1.0f, 0.0f, 1.0f); + y += vec3(0.0f, 1.0f, 0.0f); + result = pow(x, y); + } + )glsl"; + + SimpleZeroComputeTest(cs_source, SPV_SOURCE_GLSL); +}