blob: 51d781d53bdf23e8465354312d30d8ee5b2d1646 [file]
/* Copyright (c) 2024-2026 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <spirv/unified1/GLSL.std.450.h>
#include "generated/spirv_grammar_helper.h"
#include "gpuav/core/gpuav.h"
#include "gpuav/resources/gpuav_state_trackers.h"
#include "gpuav/shaders/gpuav_error_codes.h"
#include "gpuav/shaders/gpuav_error_header.h"
namespace gpuav {
static std::string GetSpirvSpecLink(const uint32_t opcode) {
// Currently the Working Group decided to not provide "real" VUIDs as it would become duplicating the SPIR-V spec
// So these are not "UNASSIGNED", but instead are "SPIRV" VUs because we can point to the instruction in the SPIR-V spec
// (https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/7853)
return "\nSee more at https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#" + std::string(string_SpvOpcode(opcode));
}
void RegisterSanitizer(Validator& gpuav, CommandBufferSubState& cb) {
if (!gpuav.gpuav_settings.shader_instrumentation.sanitizer) {
return;
}
cb.on_instrumentation_error_logger_register_functions.emplace_back([](Validator& gpuav, CommandBufferSubState& cb,
const LastBound& last_bound) {
CommandBufferSubState::InstrumentationErrorLogger inst_error_logger =
[](Validator& gpuav, const Location& loc, const uint32_t* error_record, const InstrumentedShader*,
std::string& out_error_msg, std::string& out_vuid_msg) {
using namespace glsl;
bool error_found = false;
if (GetErrorGroup(error_record) != kErrorGroup_InstSanitizer) {
return error_found;
}
error_found = true;
std::ostringstream strm;
const uint32_t error_sub_code = GetSubError(error_record);
switch (error_sub_code) {
case kErrorSubCode_Sanitizer_DivideZero: {
const uint32_t opcode = error_record[kInst_LogError_ParameterOffset_0];
const uint32_t vector_size = error_record[kInst_LogError_ParameterOffset_1];
const bool is_float = opcode == spv::OpFMod || opcode == spv::OpFRem;
strm << (is_float ? "Float" : "Integer") << " divide by zero. Operand 2 of " << string_SpvOpcode(opcode)
<< " is ";
if (vector_size == 0) {
strm << "zero.";
} else {
strm << "a " << vector_size << "-wide vector which contains a zero value.";
}
if (is_float) {
strm << " The result value is undefined.";
}
strm << GetSpirvSpecLink(opcode);
out_vuid_msg = "SPIRV-Sanitizer-Divide-By-Zero";
} break;
case kErrorSubCode_Sanitizer_ImageGather: {
const uint32_t component_value = error_record[kInst_LogError_ParameterOffset_0];
const int32_t signed_value = (int32_t)component_value;
strm << "OpImageGather has a component value of ";
if (signed_value > 0) {
strm << component_value;
} else {
strm << signed_value;
}
strm << ", but it must be 0, 1, 2, or 3" << GetSpirvSpecLink(spv::OpImageGather);
out_vuid_msg = "SPIRV-Sanitizer-Image-Gather";
} break;
case kErrorSubCode_Sanitizer_Pow: {
// Pow is only valid with a scalar/vector of 16/32-bit float
const uint32_t vector_size = error_record[kInst_LogError_ParameterOffset_0];
// Should use std::bit_cast but requires c++20
const float x_value = *(float*)(error_record + kInst_LogError_ParameterOffset_1);
const float y_value = *(float*)(error_record + kInst_LogError_ParameterOffset_2);
strm << "Pow (from GLSL.std.450) has an undefined result because x < 0, or x == 0 and y <= 0. ";
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
<< "; currently only scalar values can be printed in error messages.";
} else {
strm << "X == " << x_value << ", Y == " << y_value;
}
out_vuid_msg = "SPIRV-Sanitizer-Pow";
} break;
case kErrorSubCode_Sanitizer_Atan2: {
// Atan is only valid with a scalar/vector of 16/32-bit float
strm << "Atan2 (from GLSL.std.450) has an undefined result because both the y and x operands are zero.";
out_vuid_msg = "SPIRV-Sanitizer-Atan2";
} break;
case kErrorSubCode_Sanitizer_Fminmax: {
// simple encoding done in inst_sanitizer_fminman (sanitizer.comp)
const uint32_t invalid_encode = error_record[kInst_LogError_ParameterOffset_0];
const bool x_is_invalid = (invalid_encode & 0x1) != 0;
const bool y_is_invalid = (invalid_encode & 0x2) != 0;
const uint32_t vector_size = error_record[kInst_LogError_ParameterOffset_1];
const uint32_t glsl_opcode = error_record[kInst_LogError_ParameterOffset_2];
strm << (glsl_opcode == GLSLstd450FMin ? "FMin" : "FMax")
<< " (from GLSL.std.450) has an undefined result because ";
if (x_is_invalid && y_is_invalid) {
strm << "both the x and y operands are NaN.";
} else if (x_is_invalid) {
strm << "the x operand is NaN.";
} else {
strm << "the y operand is NaN.";
}
if (vector_size > 0) {
strm << " Using a vector of size " << vector_size
<< "; currently only scalar values can be printed in error messages.";
}
out_vuid_msg = "SPIRV-Sanitizer-Fminmax";
} break;
case kErrorSubCode_Sanitizer_CoopMatAlignment: {
const uint32_t opcode = error_record[kInst_LogError_ParameterOffset_0];
const uint32_t required_alignment = error_record[kInst_LogError_ParameterOffset_1];
const uint32_t misaligned_flags = error_record[kInst_LogError_ParameterOffset_2];
const bool stride_misaligned = (misaligned_flags & 0x1) != 0;
const bool pointer_misaligned = (misaligned_flags & 0x2) != 0;
strm << string_SpvOpcode(opcode) << " has a ";
if (stride_misaligned && pointer_misaligned) {
strm << "Pointer and Stride operand that are";
} else if (pointer_misaligned) {
strm << "Pointer operand that is";
} else {
strm << "Stride operand that is";
}
strm << " not aligned to the required " << required_alignment
<< " byte alignment (the lesser of 16 bytes and the natural alignment of a row or column of the "
"matrix).";
out_vuid_msg = "VUID-RuntimeSpirv-OpCooperativeMatrixLoadKHR-08986";
} break;
default:
error_found = false;
break;
}
out_error_msg += strm.str();
return error_found;
};
return inst_error_logger;
});
}
} // namespace gpuav