mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-10-31 13:15:33 +00:00
Updated spirv-tools.
This commit is contained in:
committed by
Branimir Karadžić
parent
1e2e22d983
commit
31ca235db8
@@ -1 +1 @@
|
||||
"v2026.2", "SPIRV-Tools v2026.2 v2026.2-55-g18e99045"
|
||||
"v2026.4", "SPIRV-Tools v2026.4 v2026.3-49-g2736d1f8"
|
||||
|
||||
+9273
-9123
File diff suppressed because it is too large
Load Diff
@@ -68,6 +68,7 @@ enum Extension : uint32_t {
|
||||
kSPV_ARM_graph,
|
||||
kSPV_ARM_tensors,
|
||||
kSPV_EXT_arithmetic_fence,
|
||||
kSPV_EXT_cooperative_matrix_maintenance1,
|
||||
kSPV_EXT_demote_to_helper_invocation,
|
||||
kSPV_EXT_descriptor_heap,
|
||||
kSPV_EXT_descriptor_indexing,
|
||||
@@ -77,6 +78,7 @@ enum Extension : uint32_t {
|
||||
kSPV_EXT_fragment_shader_interlock,
|
||||
kSPV_EXT_long_vector,
|
||||
kSPV_EXT_mesh_shader,
|
||||
kSPV_EXT_ocp_microscaling_types,
|
||||
kSPV_EXT_opacity_micromap,
|
||||
kSPV_EXT_optnone,
|
||||
kSPV_EXT_physical_storage_buffer,
|
||||
@@ -221,10 +223,15 @@ enum Extension : uint32_t {
|
||||
kSPV_NV_stereo_view_rendering,
|
||||
kSPV_NV_tensor_addressing,
|
||||
kSPV_NV_viewport_array2,
|
||||
kSPV_QCOM_bfloat16_muladd,
|
||||
kSPV_QCOM_cooperative_matrix_conversion,
|
||||
kSPV_QCOM_image_processing,
|
||||
kSPV_QCOM_image_processing2,
|
||||
kSPV_QCOM_image_processing3,
|
||||
kSPV_QCOM_multiple_wait_queues,
|
||||
kSPV_QCOM_subgroup_size,
|
||||
kSPV_QCOM_tile_shading,
|
||||
kSPV_SAMSUNG_intrinsic,
|
||||
kSPV_VALIDATOR_ignore_type_decl_unique,
|
||||
kSPV_VALVE_mixed_float_dot_product,
|
||||
};
|
||||
|
||||
+3
-2
@@ -14,7 +14,7 @@
|
||||
{13, "Google", "Shaderc over Glslang", "Google Shaderc over Glslang"},
|
||||
{14, "Google", "spiregg", "Google spiregg"},
|
||||
{15, "Google", "rspirv", "Google rspirv"},
|
||||
{16, "X-LEGEND", "Mesa-IR/SPIR-V Translator", "X-LEGEND Mesa-IR/SPIR-V Translator"},
|
||||
{16, "X-LEGEND", "Mesa3D GLSL-IR/SPIR-V Translator", "X-LEGEND Mesa3D GLSL-IR/SPIR-V Translator"},
|
||||
{17, "Khronos", "SPIR-V Tools Linker", "Khronos SPIR-V Tools Linker"},
|
||||
{18, "Wine", "VKD3D Shader Compiler", "Wine VKD3D Shader Compiler"},
|
||||
{19, "Tellusim", "Clay Shader Compiler", "Tellusim Clay Shader Compiler"},
|
||||
@@ -48,4 +48,5 @@
|
||||
{47, "ARM", "", "ARM"},
|
||||
{48, "Goopax", "", "Goopax"},
|
||||
{49, "Icyllis Milica", "Arc3D Shader Compiler", "Icyllis Milica Arc3D Shader Compiler"},
|
||||
{50, "Isochron", "Pred", "Isochron Pred"},
|
||||
{50, "Isochron", "Pred", "Isochron Pred"},
|
||||
{51, "apilatosba", "Apila Jai Compiler", "apilatosba Apila Jai Compiler"},
|
||||
+28
-2
@@ -338,6 +338,9 @@ typedef enum spv_operand_type_t {
|
||||
SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY,
|
||||
SPV_OPERAND_TYPE_VARIABLE_CAPABILITY,
|
||||
|
||||
// SPV_QCOM_image_processing3
|
||||
SPV_OPERAND_TYPE_GATHER_MODES,
|
||||
|
||||
// This is a sentinel value, and does not represent an operand type.
|
||||
// It should come last.
|
||||
SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,
|
||||
@@ -399,6 +402,11 @@ typedef enum spv_fp_encoding_t {
|
||||
SPV_FP_ENCODING_BFLOAT16,
|
||||
SPV_FP_ENCODING_FLOAT8_E4M3,
|
||||
SPV_FP_ENCODING_FLOAT8_E5M2,
|
||||
SPV_FP_ENCODING_FLOAT6_E2M3,
|
||||
SPV_FP_ENCODING_FLOAT6_E3M2,
|
||||
SPV_FP_ENCODING_FLOAT4_E2M1,
|
||||
SPV_FP_ENCODING_FLOAT8_UNSIGNED_E8M0,
|
||||
SPV_FP_ENCODING_MXINT8,
|
||||
} spv_fp_encoding_t;
|
||||
|
||||
typedef enum spv_text_to_binary_options_t {
|
||||
@@ -781,6 +789,24 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowVulkan32BitBitwise(
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Sets custom size and alignment for buffer and acceleration structure
|
||||
// descriptor heap resources.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBufferDescriptorLayout(
|
||||
spv_validator_options options, uint32_t size, uint32_t alignment);
|
||||
|
||||
// Sets custom size and alignment for image and sampled image descriptor heap
|
||||
// resources.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetImageDescriptorLayout(
|
||||
spv_validator_options options, uint32_t size, uint32_t alignment);
|
||||
|
||||
// Sets custom size and alignment for sampler descriptor heap resources.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSamplerDescriptorLayout(
|
||||
spv_validator_options options, uint32_t size, uint32_t alignment);
|
||||
|
||||
// Sets custom size and alignment for tensor descriptor heap resources.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetTensorDescriptorLayout(
|
||||
spv_validator_options options, uint32_t size, uint32_t alignment);
|
||||
|
||||
// Creates an optimizer options object with default options. Returns a valid
|
||||
// options object. The object remains valid until it is passed into
|
||||
// |spvOptimizerOptionsDestroy|.
|
||||
@@ -1010,8 +1036,8 @@ SPIRV_TOOLS_EXPORT spv_result_t spvBinaryParse(
|
||||
// The optimizer interface.
|
||||
|
||||
// A pointer to a function that accepts a log message from an optimizer.
|
||||
typedef void (*spv_message_consumer)(
|
||||
spv_message_level_t, const char*, const spv_position_t*, const char*);
|
||||
typedef void (*spv_message_consumer)(spv_message_level_t, const char*,
|
||||
const spv_position_t*, const char*);
|
||||
|
||||
// Creates and returns an optimizer object. This object must be passed to
|
||||
// optimizer APIs below and is valid until passed to spvOptimizerDestroy.
|
||||
|
||||
@@ -138,6 +138,28 @@ class SPIRV_TOOLS_EXPORT ValidatorOptions {
|
||||
spvValidatorOptionsSetAllowVulkan32BitBitwise(options_, val);
|
||||
}
|
||||
|
||||
// Sets custom size and alignment for buffer and acceleration structure
|
||||
// descriptor heap resources.
|
||||
void SetBufferDescriptorLayout(uint32_t size, uint32_t alignment) {
|
||||
spvValidatorOptionsSetBufferDescriptorLayout(options_, size, alignment);
|
||||
}
|
||||
|
||||
// Sets custom size and alignment for image and sampled image descriptor heap
|
||||
// resources.
|
||||
void SetImageDescriptorLayout(uint32_t size, uint32_t alignment) {
|
||||
spvValidatorOptionsSetImageDescriptorLayout(options_, size, alignment);
|
||||
}
|
||||
|
||||
// Sets custom size and alignment for sampler descriptor heap resources.
|
||||
void SetSamplerDescriptorLayout(uint32_t size, uint32_t alignment) {
|
||||
spvValidatorOptionsSetSamplerDescriptorLayout(options_, size, alignment);
|
||||
}
|
||||
|
||||
// Sets custom size and alignment for tensor descriptor heap resources.
|
||||
void SetTensorDescriptorLayout(uint32_t size, uint32_t alignment) {
|
||||
spvValidatorOptionsSetTensorDescriptorLayout(options_, size, alignment);
|
||||
}
|
||||
|
||||
// Records whether or not the validator should relax the rules on pointer
|
||||
// usage in logical addressing mode.
|
||||
//
|
||||
|
||||
@@ -722,6 +722,8 @@ Optimizer::PassToken CreateSSARewritePass();
|
||||
// redundancy elimination and DCE.
|
||||
Optimizer::PassToken CreateConvertRelaxedToHalfPass();
|
||||
|
||||
Optimizer::PassToken CreateConvertToUntypedPass();
|
||||
|
||||
// Create relax float ops pass.
|
||||
// This pass decorates all float32 result instructions with RelaxedPrecision
|
||||
// if not already so decorated.
|
||||
|
||||
+6
-2
@@ -153,12 +153,16 @@ const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = {
|
||||
CASE(InBoundsAccessChain),
|
||||
CASE(PtrAccessChain),
|
||||
CASE(InBoundsPtrAccessChain),
|
||||
CASE(UntypedAccessChainKHR),
|
||||
CASE(UntypedInBoundsAccessChainKHR),
|
||||
CASE(UntypedPtrAccessChainKHR),
|
||||
CASE(UntypedInBoundsPtrAccessChainKHR),
|
||||
CASE(CooperativeMatrixLengthNV),
|
||||
CASE(CooperativeMatrixLengthKHR)
|
||||
};
|
||||
|
||||
// The 60 is determined by counting the opcodes listed in the spec.
|
||||
static_assert(61 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]),
|
||||
// The count is determined by counting the opcodes listed in the spec.
|
||||
static_assert(65 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]),
|
||||
"OpSpecConstantOp opcode table is incomplete");
|
||||
#undef CASE
|
||||
// clang-format on
|
||||
|
||||
+3
-1
@@ -489,6 +489,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
// Assume non-numeric values. This will be updated for literal numbers.
|
||||
parsed_operand.number_kind = SPV_NUMBER_NONE;
|
||||
parsed_operand.number_bit_width = 0;
|
||||
parsed_operand.fp_encoding = SPV_FP_ENCODING_UNKNOWN;
|
||||
|
||||
if (_.word_index >= _.num_words)
|
||||
return exhaustedInputDiagnostic(inst_offset, opcode, type);
|
||||
@@ -744,7 +745,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER:
|
||||
case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL:
|
||||
case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL:
|
||||
case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: {
|
||||
case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS:
|
||||
case SPV_OPERAND_TYPE_GATHER_MODES: {
|
||||
// A single word that is a plain enum value.
|
||||
|
||||
// Map an optional operand type to its corresponding concrete type.
|
||||
|
||||
+4
-3
@@ -599,7 +599,7 @@ spv_result_t DisassembleTargetInstruction(
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t GetLineLengthWithoutColor(const std::string line) {
|
||||
uint32_t GetLineLengthWithoutColor(const std::string& line) {
|
||||
// Currently, every added color is in the form \x1b...m, so instead of doing a
|
||||
// lot of string comparisons with spvtools::clr::* strings, we just ignore
|
||||
// those ranges.
|
||||
@@ -804,11 +804,12 @@ void InstructionDisassembler::EmitInstructionImpl(
|
||||
comment_separator = ", ";
|
||||
}
|
||||
|
||||
stream_ << line.str();
|
||||
const std::string line_str = line.str();
|
||||
stream_ << line_str;
|
||||
|
||||
if (!comments.str().empty()) {
|
||||
// Align the comments
|
||||
const uint32_t line_length = GetLineLengthWithoutColor(line.str());
|
||||
const uint32_t line_length = GetLineLengthWithoutColor(line_str);
|
||||
uint32_t align = std::max(
|
||||
{line_length + 2, last_instruction_comment_alignment_, kCommentColumn});
|
||||
// Round up the alignment to a multiple of 4 for more niceness.
|
||||
|
||||
+23
-8
@@ -119,6 +119,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
|
||||
const DefUseManager& def_use_manager,
|
||||
const DecorationManager& decoration_manager,
|
||||
bool allow_partial_linkage,
|
||||
bool allow_multiple_exports,
|
||||
LinkageTable* linkings_to_do);
|
||||
|
||||
// Checks that for each pair of import and export, the import and export have
|
||||
@@ -416,6 +417,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
|
||||
const DefUseManager& def_use_manager,
|
||||
const DecorationManager& decoration_manager,
|
||||
bool allow_partial_linkage,
|
||||
bool allow_multiple_exports,
|
||||
LinkageTable* linkings_to_do) {
|
||||
spv_position_t position = {};
|
||||
|
||||
@@ -504,6 +506,23 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
|
||||
<< possible_export.second.name << "\".";
|
||||
}
|
||||
|
||||
// Detect multiple strong definitions of the same symbol.
|
||||
// The import-matching loop below only visits symbols that are imported, so it
|
||||
// would silently accept multiple exports when nothing imports them. Check
|
||||
// all exports here so that duplicates are always rejected.
|
||||
//
|
||||
// The function-variants (multitarget) flow intentionally keeps several
|
||||
// exports of the same name (one per variant), so skip the check in that case.
|
||||
if (!allow_multiple_exports) {
|
||||
for (const auto& exp : exports) {
|
||||
if (exp.second.size() > 1u)
|
||||
return DiagnosticStream(position, consumer, "",
|
||||
SPV_ERROR_INVALID_BINARY)
|
||||
<< "Too many external references, " << exp.second.size()
|
||||
<< ", were found for \"" << exp.first << "\".";
|
||||
}
|
||||
}
|
||||
|
||||
// Find the import/export pairs
|
||||
for (const auto& import : imports) {
|
||||
std::vector<LinkageSymbolInfo> possible_exports;
|
||||
@@ -512,10 +531,6 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
|
||||
if (possible_exports.empty() && !allow_partial_linkage)
|
||||
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
|
||||
<< "Unresolved external reference to \"" << import.name << "\".";
|
||||
else if (possible_exports.size() > 1u)
|
||||
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
|
||||
<< "Too many external references, " << possible_exports.size()
|
||||
<< ", were found for \"" << import.name << "\".";
|
||||
|
||||
if (!possible_exports.empty())
|
||||
linkings_to_do->emplace_back(import, possible_exports.front());
|
||||
@@ -910,10 +925,10 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,
|
||||
|
||||
// Phase 5: Find the import/export pairs
|
||||
LinkageTable linkings_to_do;
|
||||
res = GetImportExportPairs(consumer, linked_context,
|
||||
*linked_context.get_def_use_mgr(),
|
||||
*linked_context.get_decoration_mgr(),
|
||||
options.GetAllowPartialLinkage(), &linkings_to_do);
|
||||
res = GetImportExportPairs(
|
||||
consumer, linked_context, *linked_context.get_def_use_mgr(),
|
||||
*linked_context.get_decoration_mgr(), options.GetAllowPartialLinkage(),
|
||||
make_multitarget, &linkings_to_do);
|
||||
if (res != SPV_SUCCESS) return res;
|
||||
|
||||
// Phase 6: Ensure the import and export have the same types and decorations.
|
||||
|
||||
+33
-4
@@ -78,13 +78,21 @@ void FriendlyNameMapper::SaveName(uint32_t id,
|
||||
const std::string& suggested_name) {
|
||||
if (name_for_id_.find(id) != name_for_id_.end()) return;
|
||||
|
||||
const std::string sanitized_suggested_name = Sanitize(suggested_name);
|
||||
std::string name = sanitized_suggested_name;
|
||||
std::string base_name;
|
||||
|
||||
// Limit the size of the name to limit memory usage.
|
||||
const uint32_t kMaxSize = 256;
|
||||
if (suggested_name.size() > kMaxSize) {
|
||||
base_name = to_string(id);
|
||||
} else {
|
||||
base_name = Sanitize(suggested_name);
|
||||
}
|
||||
std::string name = base_name;
|
||||
auto inserted = used_names_.insert(name);
|
||||
if (!inserted.second) {
|
||||
const std::string base_name = sanitized_suggested_name + "_";
|
||||
const std::string base_name_prefix = base_name + "_";
|
||||
for (uint32_t index = 0; !inserted.second; ++index) {
|
||||
name = base_name + to_string(index);
|
||||
name = base_name_prefix + to_string(index);
|
||||
inserted = used_names_.insert(name);
|
||||
}
|
||||
}
|
||||
@@ -227,6 +235,27 @@ spv_result_t FriendlyNameMapper::ParseInstruction(
|
||||
SaveName(result_id, "fp8e5m2");
|
||||
break;
|
||||
}
|
||||
if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float6E2M3EXT) {
|
||||
SaveName(result_id, "fp6e2m3");
|
||||
break;
|
||||
}
|
||||
if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float6E3M2EXT) {
|
||||
SaveName(result_id, "fp6e3m2");
|
||||
break;
|
||||
}
|
||||
if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float4E2M1EXT) {
|
||||
SaveName(result_id, "fp4e2m1");
|
||||
break;
|
||||
}
|
||||
if (spv::FPEncoding(inst.words[3]) ==
|
||||
spv::FPEncoding::Float8UnsignedE8M0EXT) {
|
||||
SaveName(result_id, "fp8e8m0");
|
||||
break;
|
||||
}
|
||||
if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::MXInt8EXT) {
|
||||
SaveName(result_id, "mxint8");
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (bit_width) {
|
||||
case 16:
|
||||
|
||||
+19
-2
@@ -225,6 +225,8 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
|
||||
return "NonSemantic.Shader.DebugInfo.100 debug operation";
|
||||
case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER:
|
||||
return "NonSemantic.Shader.DebugInfo.100 debug type qualifier";
|
||||
case SPV_OPERAND_TYPE_GATHER_MODES:
|
||||
return "gather modes";
|
||||
|
||||
case SPV_OPERAND_TYPE_NONE:
|
||||
return "NONE";
|
||||
@@ -347,6 +349,7 @@ bool spvOperandIsConcrete(spv_operand_type_t type) {
|
||||
case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS:
|
||||
case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION:
|
||||
case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER:
|
||||
case SPV_OPERAND_TYPE_GATHER_MODES:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
@@ -577,10 +580,10 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
|
||||
case spv::Op::OpTypeArray:
|
||||
out = [](unsigned index) { return index == 1; };
|
||||
break;
|
||||
case spv::Op::OpCooperativeMatrixPerElementOpNV:
|
||||
case spv::Op::OpCooperativeMatrixPerElementOpEXT:
|
||||
out = [](unsigned index) { return index == 3; };
|
||||
break;
|
||||
case spv::Op::OpCooperativeMatrixReduceNV:
|
||||
case spv::Op::OpCooperativeMatrixReduceEXT:
|
||||
out = [](unsigned index) { return index == 4; };
|
||||
break;
|
||||
case spv::Op::OpCooperativeMatrixLoadTensorNV:
|
||||
@@ -590,6 +593,10 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
|
||||
case spv::Op::OpGraphEntryPointARM:
|
||||
out = [](unsigned index) { return index == 0; };
|
||||
break;
|
||||
case spv::Op::OpExtInstWithForwardRefsKHR:
|
||||
// Any operand may be a forward reference.
|
||||
out = [](unsigned) { return true; };
|
||||
break;
|
||||
default:
|
||||
out = [](unsigned) { return false; };
|
||||
break;
|
||||
@@ -647,6 +654,16 @@ spv_fp_encoding_t spvFPEncodingFromOperandFPEncoding(spv::FPEncoding encoding) {
|
||||
return SPV_FP_ENCODING_FLOAT8_E4M3;
|
||||
case spv::FPEncoding::Float8E5M2EXT:
|
||||
return SPV_FP_ENCODING_FLOAT8_E5M2;
|
||||
case spv::FPEncoding::Float6E2M3EXT:
|
||||
return SPV_FP_ENCODING_FLOAT6_E2M3;
|
||||
case spv::FPEncoding::Float6E3M2EXT:
|
||||
return SPV_FP_ENCODING_FLOAT6_E3M2;
|
||||
case spv::FPEncoding::Float4E2M1EXT:
|
||||
return SPV_FP_ENCODING_FLOAT4_E2M1;
|
||||
case spv::FPEncoding::Float8UnsignedE8M0EXT:
|
||||
return SPV_FP_ENCODING_FLOAT8_UNSIGNED_E8M0;
|
||||
case spv::FPEncoding::MXInt8EXT:
|
||||
return SPV_FP_ENCODING_MXINT8;
|
||||
case spv::FPEncoding::Max:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -310,28 +310,31 @@ Pass::Status AggressiveDCEPass::ProcessDebugInformation(
|
||||
// DebugDeclare Variable is not live. Find the value that was being
|
||||
// stored to this variable. If it's live then create a new DebugValue
|
||||
// with this value. Otherwise let it die in peace.
|
||||
get_def_use_mgr()->ForEachUser(var_id, [this,
|
||||
var_id](Instruction* user) {
|
||||
if (user->opcode() == spv::Op::OpStore) {
|
||||
uint32_t stored_value_id = 0;
|
||||
const uint32_t kStoreValueInIdx = 1;
|
||||
stored_value_id = user->GetSingleWordInOperand(kStoreValueInIdx);
|
||||
if (!IsLive(get_def_use_mgr()->GetDef(stored_value_id))) {
|
||||
return true;
|
||||
}
|
||||
bool user_succeeded = get_def_use_mgr()->WhileEachUser(
|
||||
var_id, [this, var_id](Instruction* user) {
|
||||
if (user->opcode() == spv::Op::OpStore) {
|
||||
uint32_t stored_value_id = 0;
|
||||
const uint32_t kStoreValueInIdx = 1;
|
||||
stored_value_id =
|
||||
user->GetSingleWordInOperand(kStoreValueInIdx);
|
||||
if (!IsLive(get_def_use_mgr()->GetDef(stored_value_id))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// value being stored is still live
|
||||
Instruction* next_inst = user->NextNode();
|
||||
bool added =
|
||||
context()->get_debug_info_mgr()->AddDebugValueForVariable(
|
||||
user, var_id, stored_value_id, user);
|
||||
if (added && next_inst) {
|
||||
auto new_debug_value = next_inst->PreviousNode();
|
||||
AddToWorklist(new_debug_value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// value being stored is still live
|
||||
Instruction* next_inst = user->NextNode();
|
||||
bool added =
|
||||
context()->get_debug_info_mgr()->AddDebugValueForVariable(
|
||||
user, var_id, stored_value_id, user);
|
||||
if (context()->id_overflow()) return false;
|
||||
if (added && next_inst) {
|
||||
auto new_debug_value = next_inst->PreviousNode();
|
||||
AddToWorklist(new_debug_value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!user_succeeded) return false;
|
||||
} else if (inst->GetShaderDebugOpcode() ==
|
||||
NonSemanticShaderDebugInfoDebugValue) {
|
||||
uint32_t var_operand_idx = kDebugValueValueInIdx;
|
||||
@@ -1162,6 +1165,7 @@ void AggressiveDCEPass::InitExtensions() {
|
||||
"SPV_KHR_maximal_reconvergence",
|
||||
"SPV_NV_push_constant_bank",
|
||||
"SPV_EXT_opacity_micromap",
|
||||
"SPV_KHR_opacity_micromap",
|
||||
"SPV_EXT_shader_invocation_reorder",
|
||||
"SPV_EXT_shader_atomic_float16_add",
|
||||
"SPV_KHR_abort",
|
||||
|
||||
+5
-3
@@ -1110,8 +1110,9 @@ const analysis::Constant* GetConstantUniformValue(
|
||||
if (!uniform_inst) return nullptr;
|
||||
|
||||
uint32_t uniform_id = uniform_inst->result_id();
|
||||
uint32_t count = type->AsVector()->element_count();
|
||||
uniform =
|
||||
const_mgr->GetConstant(type, std::vector<uint32_t>(4, uniform_id));
|
||||
const_mgr->GetConstant(type, std::vector<uint32_t>(count, uniform_id));
|
||||
}
|
||||
|
||||
return uniform;
|
||||
@@ -1558,8 +1559,9 @@ ConstantFoldingRule FoldFMix() {
|
||||
Instruction* one_inst = const_mgr->GetDefiningInstruction(one);
|
||||
if (one_inst == nullptr) return nullptr;
|
||||
uint32_t one_id = one_inst->result_id();
|
||||
one =
|
||||
const_mgr->GetConstant(result_type, std::vector<uint32_t>(4, one_id));
|
||||
uint32_t count = result_type->AsVector()->element_count();
|
||||
one = const_mgr->GetConstant(result_type,
|
||||
std::vector<uint32_t>(count, one_id));
|
||||
}
|
||||
|
||||
const analysis::Constant* temp1 = FoldFPBinaryOp(
|
||||
|
||||
@@ -36,7 +36,8 @@ using utils::ParseNumber;
|
||||
// Returns true if the given char is ':', '\0' or considered as blank space
|
||||
// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' ').
|
||||
bool IsSeparator(char ch) {
|
||||
return std::strchr(":\0", ch) || std::isspace(ch) != 0;
|
||||
return std::strchr(":\0", ch) ||
|
||||
std::isspace(static_cast<unsigned char>(ch)) != 0;
|
||||
}
|
||||
|
||||
// Reads characters starting from |str| until it meets a separator. Parses a
|
||||
@@ -436,7 +437,7 @@ ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
|
||||
descriptor_set_binding_pairs->push_back({descriptor_set, binding});
|
||||
|
||||
// Skip trailing spaces.
|
||||
while (std::isspace(*str)) str++;
|
||||
while (std::isspace(static_cast<unsigned char>(*str))) str++;
|
||||
}
|
||||
|
||||
return descriptor_set_binding_pairs;
|
||||
|
||||
@@ -0,0 +1,779 @@
|
||||
// Copyright (c) 2026 Google LLC
|
||||
//
|
||||
// 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 "convert_to_untyped.h"
|
||||
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/operand.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status ConvertToUntyped::Process() {
|
||||
if (HasUnsupportedFeatures()) {
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
CheckWorkgroup();
|
||||
|
||||
bool changed = ConvertPointers();
|
||||
if (changed) {
|
||||
AddUntypedPointerCapability();
|
||||
}
|
||||
|
||||
if (context()->id_overflow()) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
if (changed) {
|
||||
return Pass::Status::SuccessWithChange;
|
||||
}
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool ConvertToUntyped::HasUnsupportedFeatures() {
|
||||
// Only support converting shaders.
|
||||
const auto& caps = context()->get_feature_mgr()->GetCapabilities();
|
||||
if (!caps.contains(spv::Capability::Shader)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unsupported
|
||||
CapabilitySet unsupported{
|
||||
spv::Capability::RawAccessChainsNV,
|
||||
spv::Capability::PredicatedIOINTEL,
|
||||
spv::Capability::GraphARM,
|
||||
spv::Capability::CooperativeMatrixReductionsNV,
|
||||
spv::Capability::CooperativeMatrixConversionsNV,
|
||||
spv::Capability::CooperativeMatrixPerElementOperationsNV,
|
||||
spv::Capability::CooperativeMatrixTensorAddressingNV,
|
||||
spv::Capability::CooperativeMatrixBlockLoadsNV,
|
||||
spv::Capability::CooperativeVectorNV,
|
||||
spv::Capability::CooperativeVectorTrainingNV};
|
||||
return caps.HasAnyOf(unsupported);
|
||||
}
|
||||
|
||||
void ConvertToUntyped::CheckWorkgroup() {
|
||||
if (!context()->get_feature_mgr()->GetCapabilities().contains(
|
||||
spv::Capability::WorkgroupMemoryExplicitLayoutKHR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The module could have a mix of explicit and non-explicit layout for
|
||||
// workgroup memory (though in different entry points). This transform
|
||||
// requires all explicit to work.
|
||||
for (auto& inst : context()->types_values()) {
|
||||
if (inst.opcode() == spv::Op::OpVariable) {
|
||||
auto pointee_ty = context()
|
||||
->get_type_mgr()
|
||||
->GetType(inst.type_id())
|
||||
->AsPointer()
|
||||
->pointee_type();
|
||||
if (!pointee_ty->AsStruct()) {
|
||||
return;
|
||||
}
|
||||
bool has_block = false;
|
||||
for (auto& dec : pointee_ty->decorations()) {
|
||||
if (dec[0] == uint32_t(spv::Decoration::Block)) {
|
||||
has_block = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_block) {
|
||||
return;
|
||||
}
|
||||
} else if (inst.opcode() == spv::Op::OpUntypedVariableKHR) {
|
||||
auto data_ty =
|
||||
context()->get_type_mgr()->GetType(inst.GetSingleWordInOperand(1));
|
||||
if (!data_ty->AsStruct()) {
|
||||
return;
|
||||
}
|
||||
bool has_block = false;
|
||||
for (auto& dec : data_ty->decorations()) {
|
||||
if (dec[0] == uint32_t(spv::Decoration::Block)) {
|
||||
has_block = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_block) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All workgroup variables were block-decorated.
|
||||
support_workgroup_ = true;
|
||||
}
|
||||
|
||||
void ConvertToUntyped::AddUntypedPointerCapability() {
|
||||
bool needs_ext = true;
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const std::string name = ei.GetInOperand(0).AsString();
|
||||
if (name == "SPV_KHR_untyped_pointers") {
|
||||
needs_ext = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needs_ext) {
|
||||
std::vector<uint32_t> words =
|
||||
spvtools::utils::MakeVector("SPV_KHR_untyped_pointers");
|
||||
context()->AddExtension(
|
||||
MakeUnique<Instruction>(context(), spv::Op::OpExtension, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_LITERAL_STRING, words}}));
|
||||
}
|
||||
|
||||
if (!context()->get_feature_mgr()->HasCapability(
|
||||
spv::Capability::UntypedPointersKHR)) {
|
||||
context()->AddCapability(MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpCapability, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_CAPABILITY,
|
||||
{uint32_t(spv::Capability::UntypedPointersKHR)}}}));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ConvertToUntyped::NextId() {
|
||||
uint32_t id = context()->TakeNextId();
|
||||
return id;
|
||||
}
|
||||
|
||||
bool ConvertToUntyped::SupportedStorageClass(spv::StorageClass sc) {
|
||||
switch (sc) {
|
||||
case spv::StorageClass::StorageBuffer:
|
||||
case spv::StorageClass::Uniform:
|
||||
case spv::StorageClass::PushConstant:
|
||||
case spv::StorageClass::PhysicalStorageBuffer:
|
||||
return true;
|
||||
case spv::StorageClass::Workgroup:
|
||||
return support_workgroup_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConvertToUntyped::SupportedStorageClass(uint32_t sc) {
|
||||
return SupportedStorageClass(static_cast<spv::StorageClass>(sc));
|
||||
}
|
||||
|
||||
bool ConvertToUntyped::ShouldConvert(const Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case spv::Op::OpTypePointer:
|
||||
return SupportedStorageClass(inst->GetSingleWordInOperand(0));
|
||||
case spv::Op::OpAccessChain:
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpPtrAccessChain:
|
||||
case spv::Op::OpInBoundsPtrAccessChain:
|
||||
case spv::Op::OpVariable:
|
||||
return SupportedStorageClass(context()
|
||||
->get_type_mgr()
|
||||
->GetType(inst->type_id())
|
||||
->AsPointer()
|
||||
->storage_class());
|
||||
case spv::Op::OpCopyMemory: {
|
||||
const auto* dst =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
const auto* dst_ptr =
|
||||
context()->get_type_mgr()->GetType(dst->type_id())->AsPointer();
|
||||
const auto* src =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(1));
|
||||
const auto* src_ptr =
|
||||
context()->get_type_mgr()->GetType(src->type_id())->AsPointer();
|
||||
return SupportedStorageClass(dst_ptr->storage_class()) ||
|
||||
SupportedStorageClass(src_ptr->storage_class());
|
||||
}
|
||||
case spv::Op::OpArrayLength: {
|
||||
const auto* obj =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
const auto* ptr =
|
||||
context()->get_type_mgr()->GetType(obj->type_id())->AsPointer();
|
||||
return SupportedStorageClass(ptr->storage_class());
|
||||
}
|
||||
case spv::Op::OpCooperativeMatrixLoadKHR:
|
||||
case spv::Op::OpCooperativeMatrixStoreKHR: {
|
||||
const auto* ptr =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
const auto* ptr_ty =
|
||||
context()->get_type_mgr()->GetType(ptr->type_id())->AsPointer();
|
||||
return SupportedStorageClass(ptr_ty->storage_class());
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, uint32_t> ConvertToUntyped::MatrixStride(Instruction* inst) {
|
||||
std::deque<uint32_t> indices;
|
||||
bool keep_going = true;
|
||||
while (keep_going) {
|
||||
keep_going = false;
|
||||
switch (inst->opcode()) {
|
||||
case spv::Op::OpAccessChain:
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpUntypedAccessChainKHR: {
|
||||
uint32_t base_idx =
|
||||
inst->opcode() == spv::Op::OpUntypedAccessChainKHR ? 1 : 0;
|
||||
for (uint32_t i = inst->NumInOperands() - 1; i > base_idx; i--) {
|
||||
if (auto* constant =
|
||||
context()->get_constant_mgr()->FindDeclaredConstant(
|
||||
inst->GetSingleWordInOperand(i))) {
|
||||
uint64_t ext = constant->GetZeroExtendedValue();
|
||||
if (ext > std::numeric_limits<uint32_t>::max()) {
|
||||
return std::make_pair(false, 0);
|
||||
}
|
||||
indices.push_front(static_cast<uint32_t>(ext));
|
||||
} else {
|
||||
// Assume we aren't at the start to accumulate a stride.
|
||||
indices.push_front(1);
|
||||
}
|
||||
}
|
||||
inst = context()->get_def_use_mgr()->GetDef(
|
||||
inst->GetSingleWordInOperand(base_idx));
|
||||
keep_going = true;
|
||||
break;
|
||||
}
|
||||
case spv::Op::OpCopyObject:
|
||||
inst = context()->get_def_use_mgr()->GetDef(
|
||||
inst->GetSingleWordInOperand(0));
|
||||
break;
|
||||
case spv::Op::OpVariable:
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool row_major = false;
|
||||
uint32_t mat_stride = 0;
|
||||
auto type = context()
|
||||
->get_type_mgr()
|
||||
->GetType(inst->type_id())
|
||||
->AsPointer()
|
||||
->pointee_type();
|
||||
for (uint32_t i = 0; i < indices.size(); i++) {
|
||||
switch (type->kind()) {
|
||||
case analysis::Type::kStruct: {
|
||||
auto where = type->AsStruct()->element_decorations().find(indices[i]);
|
||||
if (where != type->AsStruct()->element_decorations().end()) {
|
||||
const auto& decs = where->second;
|
||||
for (auto dec : decs) {
|
||||
if (dec[0] == uint32_t(spv::Decoration::MatrixStride)) {
|
||||
mat_stride = dec[1];
|
||||
}
|
||||
if (dec[0] == uint32_t(spv::Decoration::RowMajor)) {
|
||||
row_major = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
type = type->AsStruct()->element_types()[indices[i]];
|
||||
break;
|
||||
}
|
||||
case analysis::Type::kArray:
|
||||
type = type->AsArray()->element_type();
|
||||
break;
|
||||
case analysis::Type::kRuntimeArray:
|
||||
type = type->AsRuntimeArray()->element_type();
|
||||
break;
|
||||
case analysis::Type::kMatrix:
|
||||
return std::make_pair(row_major, mat_stride);
|
||||
// No other types could hold a matrix
|
||||
default:
|
||||
return std::make_pair(false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (type->AsMatrix()) {
|
||||
return std::make_pair(row_major, mat_stride);
|
||||
}
|
||||
return std::make_pair(false, 0);
|
||||
}
|
||||
|
||||
Instruction* ConvertToUntyped::ConvertPointerType(Instruction* inst) {
|
||||
Instruction* replacement = nullptr;
|
||||
auto sc = inst->GetSingleWordInOperand(0);
|
||||
auto decs = context()->get_decoration_mgr()->GetDecorationsFor(
|
||||
inst->result_id(), false);
|
||||
// De-duplicate undecorated pointers.
|
||||
if (!decs.empty() || !base_ptrs_.count(sc)) {
|
||||
// Rewrite inst in place. This maintains dominance ordering.
|
||||
inst->SetOpcode(spv::Op::OpTypeUntypedPointerKHR);
|
||||
inst->SetInOperands(
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS, {sc}}});
|
||||
if (decs.empty()) {
|
||||
base_ptrs_[sc] = inst;
|
||||
}
|
||||
} else {
|
||||
replacement = base_ptrs_[sc];
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
|
||||
void ConvertToUntyped::ConvertVariable(Instruction* inst) {
|
||||
auto ptr_ty =
|
||||
context()->get_type_mgr()->GetType(inst->type_id())->AsPointer();
|
||||
auto data =
|
||||
context()->get_type_mgr()->GetTypeInstruction(ptr_ty->pointee_type());
|
||||
// Rewrite inst in place. This maintains dominance ordering.
|
||||
inst->SetOpcode(spv::Op::OpUntypedVariableKHR);
|
||||
inst->SetInOperands(std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_STORAGE_CLASS, {uint32_t(ptr_ty->storage_class())}},
|
||||
{SPV_OPERAND_TYPE_ID, {data}}});
|
||||
}
|
||||
|
||||
void ConvertToUntyped::ConvertAccessChain(Instruction* inst) {
|
||||
spv::Op converted_op = inst->opcode();
|
||||
switch (converted_op) {
|
||||
case spv::Op::OpAccessChain:
|
||||
converted_op = spv::Op::OpUntypedAccessChainKHR;
|
||||
break;
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
converted_op = spv::Op::OpUntypedInBoundsAccessChainKHR;
|
||||
break;
|
||||
case spv::Op::OpPtrAccessChain:
|
||||
converted_op = spv::Op::OpUntypedPtrAccessChainKHR;
|
||||
break;
|
||||
case spv::Op::OpInBoundsPtrAccessChain:
|
||||
converted_op = spv::Op::OpUntypedInBoundsAccessChainKHR;
|
||||
break;
|
||||
default:
|
||||
assert(false && "unhandled opcode");
|
||||
break;
|
||||
}
|
||||
auto base =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
auto ptr_ty =
|
||||
context()->get_type_mgr()->GetType(base->type_id())->AsPointer();
|
||||
auto data =
|
||||
context()->get_type_mgr()->GetTypeInstruction(ptr_ty->pointee_type());
|
||||
// Add the base type, but then reuse all the other operands.
|
||||
std::vector<Operand> operands;
|
||||
operands.reserve(inst->NumInOperands() + 1);
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {data}});
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
|
||||
operands.push_back(inst->GetInOperand(i));
|
||||
}
|
||||
inst->SetOpcode(converted_op);
|
||||
inst->SetInOperands(std::move(operands));
|
||||
}
|
||||
|
||||
void ConvertToUntyped::ConvertArrayLength(Instruction* inst) {
|
||||
auto structure =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
auto ptr_ty =
|
||||
context()->get_type_mgr()->GetType(structure->type_id())->AsPointer();
|
||||
auto data =
|
||||
context()->get_type_mgr()->GetTypeInstruction(ptr_ty->pointee_type());
|
||||
// Add the structure type, but then reuse all the other operands.
|
||||
std::vector<Operand> operands;
|
||||
operands.reserve(inst->NumInOperands() + 1);
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {data}});
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
|
||||
operands.push_back(inst->GetInOperand(i));
|
||||
}
|
||||
inst->SetOpcode(spv::Op::OpUntypedArrayLengthKHR);
|
||||
inst->SetInOperands(std::move(operands));
|
||||
}
|
||||
|
||||
std::vector<Operand> ConvertToUntyped::StoreOperands(
|
||||
const std::vector<Operand>& operands, uint32_t align) {
|
||||
// Add placeholders for the pointer and object.
|
||||
std::vector<Operand> st_operands{{SPV_OPERAND_TYPE_ID, {0}},
|
||||
{SPV_OPERAND_TYPE_ID, {0}}};
|
||||
if (operands.empty() && align != 0) {
|
||||
// Add alignment
|
||||
st_operands.push_back({SPV_OPERAND_TYPE_MEMORY_ACCESS,
|
||||
{uint32_t(spv::MemoryAccessMask::Aligned)}});
|
||||
st_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {align}});
|
||||
} else if (!operands.empty()) {
|
||||
// Add/Remove alignment
|
||||
// Copy MakePointerAvailable scope
|
||||
// Drop MakePointerVisible (and ignore scope)
|
||||
uint32_t idx = 0;
|
||||
st_operands.push_back(operands[idx++]);
|
||||
if ((operands[0].words[0] & uint32_t(spv::MemoryAccessMask::Aligned)) !=
|
||||
0) {
|
||||
idx++;
|
||||
if (align == 0) {
|
||||
st_operands[2].words[0] ^= uint32_t(spv::MemoryAccessMask::Aligned);
|
||||
}
|
||||
}
|
||||
if (align != 0) {
|
||||
st_operands[2].words[0] |= uint32_t(spv::MemoryAccessMask::Aligned);
|
||||
st_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {align}});
|
||||
}
|
||||
|
||||
if ((operands[0].words[0] &
|
||||
uint32_t(spv::MemoryAccessMask::MakePointerAvailable)) != 0) {
|
||||
st_operands.push_back(operands[idx]);
|
||||
}
|
||||
|
||||
st_operands[2].words[0] &=
|
||||
~uint32_t(spv::MemoryAccessMask::MakePointerVisible);
|
||||
}
|
||||
|
||||
return st_operands;
|
||||
}
|
||||
|
||||
std::vector<Operand> ConvertToUntyped::LoadOperands(
|
||||
const std::vector<Operand>& operands, uint32_t align) {
|
||||
// Add placeholder operand for pointer
|
||||
std::vector<Operand> ld_operands{{SPV_OPERAND_TYPE_ID, {0}}};
|
||||
if (operands.empty() && align != 0) {
|
||||
// Add alignment
|
||||
ld_operands.push_back({SPV_OPERAND_TYPE_MEMORY_ACCESS,
|
||||
{uint32_t(spv::MemoryAccessMask::Aligned)}});
|
||||
ld_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {align}});
|
||||
} else if (!operands.empty()) {
|
||||
// Add/Remove alignment
|
||||
// Drop MakePointerAvailable (and ignore scope)
|
||||
// Copy MakePointerVisible and scope
|
||||
uint32_t idx = 0;
|
||||
ld_operands.push_back(operands[idx++]);
|
||||
if ((operands[0].words[0] & uint32_t(spv::MemoryAccessMask::Aligned)) !=
|
||||
0) {
|
||||
idx++;
|
||||
if (align == 0) {
|
||||
ld_operands[1].words[0] ^= uint32_t(spv::MemoryAccessMask::Aligned);
|
||||
}
|
||||
}
|
||||
if (align != 0) {
|
||||
ld_operands[1].words[0] |= uint32_t(spv::MemoryAccessMask::Aligned);
|
||||
ld_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {align}});
|
||||
}
|
||||
|
||||
if ((operands[0].words[0] &
|
||||
uint32_t(spv::MemoryAccessMask::MakePointerAvailable)) != 0) {
|
||||
idx++;
|
||||
ld_operands[1].words[0] ^=
|
||||
uint32_t(spv::MemoryAccessMask::MakePointerAvailable);
|
||||
}
|
||||
|
||||
if ((operands[0].words[0] &
|
||||
uint32_t(spv::MemoryAccessMask::MakePointerVisible)) != 0) {
|
||||
ld_operands.push_back(operands[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return ld_operands;
|
||||
}
|
||||
|
||||
void ConvertToUntyped::ConvertCopyMemory(Instruction* inst) {
|
||||
auto dst =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
auto dst_type =
|
||||
context()->get_type_mgr()->GetType(dst->type_id())->AsPointer();
|
||||
auto dst_sc = dst_type->storage_class();
|
||||
auto dst_untyped = SupportedStorageClass(dst_sc);
|
||||
auto src =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(1));
|
||||
auto src_type =
|
||||
context()->get_type_mgr()->GetType(src->type_id())->AsPointer();
|
||||
auto src_sc = src_type->storage_class();
|
||||
auto src_untyped = SupportedStorageClass(src_sc);
|
||||
|
||||
// Get the memory access operands for src and dst.
|
||||
std::vector<Operand> dst_operands;
|
||||
std::vector<Operand> src_operands;
|
||||
if (inst->NumInOperands() > 2) {
|
||||
uint32_t i = 2;
|
||||
for (; i < inst->NumInOperands(); i++) {
|
||||
auto& op = inst->GetInOperand(i);
|
||||
if (i > 2 && op.type == SPV_OPERAND_TYPE_MEMORY_ACCESS) {
|
||||
break;
|
||||
}
|
||||
dst_operands.push_back(op);
|
||||
}
|
||||
if (inst->NumInOperands() > i) {
|
||||
// The are separate src and dst memory access operands.
|
||||
for (; i < inst->NumInOperands(); i++) {
|
||||
src_operands.push_back(inst->GetInOperand(i));
|
||||
}
|
||||
} else {
|
||||
// Shared memory access operands.
|
||||
src_operands = dst_operands;
|
||||
}
|
||||
}
|
||||
|
||||
auto [dst_row_major, dst_mat_stride] = MatrixStride(dst);
|
||||
auto [src_row_major, src_mat_stride] = MatrixStride(src);
|
||||
|
||||
// src and dst pointee types must match.
|
||||
auto pointee_type = dst_type->pointee_type();
|
||||
|
||||
// Matrices (and their columns) are a special case.
|
||||
// The layout information is not on the matrix itself so we could be copying
|
||||
// matrices with completely different layouts. Just break them down into
|
||||
// component loads and stores.
|
||||
if ((dst_mat_stride != 0 || src_mat_stride != 0) &&
|
||||
!pointee_type->AsFloat()) {
|
||||
uint32_t num_cols = 1;
|
||||
uint32_t num_rows = 1;
|
||||
const analysis::Type* ele_type = nullptr;
|
||||
if (auto mat_type = pointee_type->AsMatrix()) {
|
||||
num_cols = mat_type->element_count();
|
||||
auto vec_type = mat_type->element_type()->AsVector();
|
||||
num_rows = vec_type->element_count();
|
||||
ele_type = vec_type->element_type();
|
||||
} else if (auto vec_type = pointee_type->AsVector()) {
|
||||
num_rows = vec_type->element_count();
|
||||
ele_type = vec_type->element_type();
|
||||
}
|
||||
|
||||
uint32_t align = ele_type->AsFloat()->width() / 8;
|
||||
|
||||
// Setup operands for the loads and stores.
|
||||
auto ld_operands = LoadOperands(src_operands, align);
|
||||
auto st_operands = StoreOperands(dst_operands, align);
|
||||
|
||||
// Setup the access operands.
|
||||
spv::Op src_access_op =
|
||||
src_untyped ? spv::Op::OpUntypedAccessChainKHR : spv::Op::OpAccessChain;
|
||||
spv::Op dst_access_op =
|
||||
dst_untyped ? spv::Op::OpUntypedAccessChainKHR : spv::Op::OpAccessChain;
|
||||
uint32_t base_type_id = context()->get_type_mgr()->GetId(pointee_type);
|
||||
uint32_t ele_type_id = context()->get_type_mgr()->GetId(ele_type);
|
||||
|
||||
std::vector<Operand> src_access_operands;
|
||||
if (src_untyped) {
|
||||
src_access_operands.push_back({SPV_OPERAND_TYPE_ID, {base_type_id}});
|
||||
}
|
||||
src_access_operands.push_back({SPV_OPERAND_TYPE_ID, {src->result_id()}});
|
||||
src_access_operands.push_back({SPV_OPERAND_TYPE_ID, {0}});
|
||||
|
||||
std::vector<Operand> dst_access_operands;
|
||||
if (dst_untyped) {
|
||||
dst_access_operands.push_back({SPV_OPERAND_TYPE_ID, {base_type_id}});
|
||||
}
|
||||
dst_access_operands.push_back({SPV_OPERAND_TYPE_ID, {dst->result_id()}});
|
||||
dst_access_operands.push_back({SPV_OPERAND_TYPE_ID, {0}});
|
||||
|
||||
if (num_cols > 1) {
|
||||
src_access_operands.push_back({SPV_OPERAND_TYPE_ID, {0}});
|
||||
dst_access_operands.push_back({SPV_OPERAND_TYPE_ID, {0}});
|
||||
}
|
||||
|
||||
// Finally, unroll the copy as individual loads and stores.
|
||||
const uint32_t src_access_idx = src_untyped ? 2 : 1;
|
||||
const uint32_t dst_access_idx = dst_untyped ? 2 : 1;
|
||||
for (uint32_t c = 0; c < num_cols; c++) {
|
||||
for (uint32_t r = 0; r < num_rows; r++) {
|
||||
// Update the access operands
|
||||
if (num_cols == 1) {
|
||||
uint32_t row_id = context()->get_constant_mgr()->GetUIntConstId(r);
|
||||
src_access_operands[src_access_idx].words[0] = row_id;
|
||||
dst_access_operands[dst_access_idx].words[0] = row_id;
|
||||
} else {
|
||||
uint32_t col_id = context()->get_constant_mgr()->GetUIntConstId(c);
|
||||
uint32_t row_id = context()->get_constant_mgr()->GetUIntConstId(r);
|
||||
src_access_operands[src_access_idx].words[0] = col_id;
|
||||
src_access_operands[src_access_idx + 1].words[0] = row_id;
|
||||
dst_access_operands[dst_access_idx].words[0] = col_id;
|
||||
dst_access_operands[dst_access_idx + 1].words[0] = row_id;
|
||||
}
|
||||
|
||||
uint32_t src_access_id = NextId();
|
||||
inst->InsertBefore(MakeUnique<Instruction>(
|
||||
context(), src_access_op, base_ptrs_[uint32_t(src_sc)]->result_id(),
|
||||
src_access_id, src_access_operands));
|
||||
|
||||
uint32_t src_ld = NextId();
|
||||
// Update load operand ids
|
||||
ld_operands[0].words[0] = src_access_id;
|
||||
inst->InsertBefore(MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpLoad, ele_type_id, src_ld, ld_operands));
|
||||
|
||||
uint32_t dst_access_id = NextId();
|
||||
inst->InsertBefore(MakeUnique<Instruction>(
|
||||
context(), dst_access_op, base_ptrs_[uint32_t(dst_sc)]->result_id(),
|
||||
dst_access_id, dst_access_operands));
|
||||
|
||||
// Update store operand ids
|
||||
st_operands[0].words[0] = dst_access_id;
|
||||
st_operands[1].words[0] = src_ld;
|
||||
inst->InsertBefore(MakeUnique<Instruction>(context(), spv::Op::OpStore,
|
||||
0, 0, st_operands));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// For other cases just load from src and store to dst.
|
||||
// Don't bother with OpCopyMemorySized, too many shenanigans are necessary for
|
||||
// that.
|
||||
//
|
||||
// We don't need to set an align.
|
||||
auto ld_operands = LoadOperands(src_operands, 0);
|
||||
auto st_operands = StoreOperands(dst_operands, 0);
|
||||
|
||||
ld_operands[0].words[0] = src->result_id();
|
||||
auto ld = inst->InsertBefore(MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpLoad,
|
||||
context()->get_type_mgr()->GetId(pointee_type), NextId(), ld_operands));
|
||||
|
||||
st_operands[0].words[0] = dst->result_id();
|
||||
st_operands[1].words[0] = ld->result_id();
|
||||
inst->InsertBefore(
|
||||
MakeUnique<Instruction>(context(), spv::Op::OpStore, 0, 0, st_operands));
|
||||
}
|
||||
|
||||
void ConvertToUntyped::UpdateCooperativeMatrixLoadStore(Instruction* inst) {
|
||||
uint32_t stride_idx =
|
||||
inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR ? 2 : 3;
|
||||
// With typed pointers the stride is counted in elements, but with untyped
|
||||
// pointers the stride is interpreted in bytes. So if there is no stride
|
||||
// specified, nothing needs done.
|
||||
if (inst->NumInOperands() <= stride_idx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ptr =
|
||||
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
|
||||
auto ptr_ty = context()->get_type_mgr()->GetType(ptr->type_id())->AsPointer();
|
||||
|
||||
// The pointer must point to a scalar or vector of numeric type so there is a
|
||||
// very limited number of permutations to consider.
|
||||
//
|
||||
// Also the memory is in contiguous locations, so just use the size (e.g.
|
||||
// don't worry about 3-element vectors).
|
||||
auto type = ptr_ty->pointee_type();
|
||||
uint32_t size = 1;
|
||||
if (auto vec_ty = type->AsVector()) {
|
||||
size *= vec_ty->element_count();
|
||||
type = vec_ty->element_type();
|
||||
} else if (auto long_vec_ty = type->AsCooperativeVectorNV()) {
|
||||
size *= long_vec_ty->components();
|
||||
type = long_vec_ty->component_type();
|
||||
}
|
||||
|
||||
if (auto int_type = type->AsInteger()) {
|
||||
size *= int_type->width() / 8;
|
||||
} else if (auto float_type = type->AsFloat()) {
|
||||
size *= float_type->width() / 8;
|
||||
}
|
||||
|
||||
auto mul = inst->InsertBefore(MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpIMul, context()->get_type_mgr()->GetUIntTypeId(),
|
||||
NextId(),
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(stride_idx)}},
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{context()->get_constant_mgr()->GetUIntConstId(size)}}}));
|
||||
|
||||
inst->SetInOperand(stride_idx, {mul->result_id()});
|
||||
}
|
||||
|
||||
void ConvertToUntyped::Convert(Instruction* inst) {
|
||||
Instruction* replacement = nullptr;
|
||||
switch (inst->opcode()) {
|
||||
case spv::Op::OpTypePointer:
|
||||
replacement = ConvertPointerType(inst);
|
||||
if (replacement != nullptr) {
|
||||
to_delete_.push_back(inst);
|
||||
remapped_ids_[inst->result_id()] = replacement->result_id();
|
||||
}
|
||||
break;
|
||||
case spv::Op::OpVariable:
|
||||
ConvertVariable(inst);
|
||||
break;
|
||||
case spv::Op::OpAccessChain:
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpPtrAccessChain:
|
||||
case spv::Op::OpInBoundsPtrAccessChain:
|
||||
ConvertAccessChain(inst);
|
||||
break;
|
||||
case spv::Op::OpArrayLength:
|
||||
ConvertArrayLength(inst);
|
||||
break;
|
||||
case spv::Op::OpCopyMemory:
|
||||
ConvertCopyMemory(inst);
|
||||
to_delete_.push_back(inst);
|
||||
break;
|
||||
case spv::Op::OpCooperativeMatrixLoadKHR:
|
||||
case spv::Op::OpCooperativeMatrixStoreKHR:
|
||||
UpdateCooperativeMatrixLoadStore(inst);
|
||||
break;
|
||||
default:
|
||||
assert(false && "unhandled opcode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConvertToUntyped::ConvertPointers() {
|
||||
if (context()->get_feature_mgr()->HasCapability(
|
||||
spv::Capability::UntypedPointersKHR)) {
|
||||
// Untyped pointers capability is already declared, pre-populate base_ptrs_.
|
||||
// All possibly supported storage classes are listed for extensibility
|
||||
// purposes.
|
||||
std::vector<spv::StorageClass> storage_classes{
|
||||
spv::StorageClass::StorageBuffer,
|
||||
spv::StorageClass::Uniform,
|
||||
spv::StorageClass::Workgroup,
|
||||
spv::StorageClass::PushConstant,
|
||||
spv::StorageClass::PhysicalStorageBuffer,
|
||||
spv::StorageClass::UniformConstant};
|
||||
for (auto sc : storage_classes) {
|
||||
if (!SupportedStorageClass(sc)) {
|
||||
continue;
|
||||
}
|
||||
const analysis::Pointer ptr_type{nullptr, sc};
|
||||
auto id = context()->get_type_mgr()->GetId(&ptr_type);
|
||||
if (id != 0) {
|
||||
base_ptrs_[uint32_t(sc)] = context()->get_def_use_mgr()->GetDef(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert instructions that change opcode with untyped pointers.
|
||||
std::vector<Instruction*> to_convert;
|
||||
get_module()->ForEachInst([this, &to_convert](Instruction* inst) {
|
||||
if (ShouldConvert(inst)) {
|
||||
to_convert.push_back(inst);
|
||||
}
|
||||
});
|
||||
|
||||
if (to_convert.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto* inst : to_convert) {
|
||||
Convert(inst);
|
||||
}
|
||||
|
||||
// Update operands of instructions
|
||||
get_module()->ForEachInst([this](Instruction* inst) {
|
||||
for (uint32_t i = 0; i < inst->NumOperands(); i++) {
|
||||
auto& op = inst->GetOperand(i);
|
||||
if (spvIsIdType(op.type) && op.type != SPV_OPERAND_TYPE_RESULT_ID) {
|
||||
auto where = remapped_ids_.find(op.words[0]);
|
||||
if (where != remapped_ids_.end()) {
|
||||
op.words[0] = where->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delete dead instructions
|
||||
for (auto* inst : to_delete_) {
|
||||
context()->KillInst(inst);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2026 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef LIBSPIRV_OPT_CONVERT_TO_UNTYPED_H_
|
||||
#define LIBSPIRV_OPT_CONVERT_TO_UNTYPED_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
class ConvertToUntyped : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "convert-to-untyped"; }
|
||||
Status Process() override;
|
||||
|
||||
private:
|
||||
// Adds the untyped pointer extension and capability if they are not present.
|
||||
void AddUntypedPointerCapability();
|
||||
|
||||
// Updates the module by converting instructions and replacing uses.
|
||||
//
|
||||
// The TypeManager is not updated as the mutations occur and all instructions
|
||||
// continue to use old ids (mapped to correctly typed pointers) until after
|
||||
// conversions. Then all operands are updated (and unneeded instructions
|
||||
// removed).
|
||||
//
|
||||
// Returns true if conversions were made.
|
||||
bool ConvertPointers();
|
||||
|
||||
// Returns true if sc is supports untyped pointers.
|
||||
bool SupportedStorageClass(spv::StorageClass sc);
|
||||
bool SupportedStorageClass(uint32_t sc);
|
||||
|
||||
// Returns true if inst needs conversion.
|
||||
bool ShouldConvert(const Instruction* inst);
|
||||
|
||||
// Returns true if the module has unsupported features.
|
||||
bool HasUnsupportedFeatures();
|
||||
|
||||
// Determines whether workgroup storage class can be converted.
|
||||
void CheckWorkgroup();
|
||||
|
||||
// Converts or updates inst.
|
||||
void Convert(Instruction* inst);
|
||||
|
||||
// Converts OpTypePointer to OpTypeUntypedPointerKHR
|
||||
// All undecorated pointers in each storage class are de-duplicated.
|
||||
Instruction* ConvertPointerType(Instruction* inst);
|
||||
|
||||
// Converts OpVariable to OpUntypedVariableKHR
|
||||
void ConvertVariable(Instruction* inst);
|
||||
|
||||
// Converts access chains to equivalent untyped version.
|
||||
void ConvertAccessChain(Instruction* inst);
|
||||
|
||||
// Converts OpArrayLength to OpUntypedArrayLengthKHR.
|
||||
void ConvertArrayLength(Instruction* inst);
|
||||
|
||||
// Converts OpCopyMemory.
|
||||
// If the copy is a matrix or the column of a matrix, it is converted to loads
|
||||
// and stores element-wise.
|
||||
// Otherwise, converts the copy to a load and store.
|
||||
void ConvertCopyMemory(Instruction* inst);
|
||||
|
||||
// Updates cooperative matrix load/store strides to be in bytes.
|
||||
void UpdateCooperativeMatrixLoadStore(Instruction* inst);
|
||||
|
||||
uint32_t NextId();
|
||||
|
||||
// Returns (row_major, matrix_stride) for the inst.
|
||||
std::pair<bool, uint32_t> MatrixStride(Instruction* inst);
|
||||
|
||||
// Memory access operand conversion (from OpCopyMemory)
|
||||
// Converts operands into appropriate memory access operands for OpStore.
|
||||
std::vector<Operand> StoreOperands(const std::vector<Operand>& operands,
|
||||
uint32_t align);
|
||||
// Converts operands into appropriate memory access operands for OpLoad.
|
||||
std::vector<Operand> LoadOperands(const std::vector<Operand>& operands,
|
||||
uint32_t align);
|
||||
|
||||
// Tracks the de-duplicated pointers per storage class.
|
||||
std::unordered_map<uint32_t, Instruction*> base_ptrs_{};
|
||||
// Tracks result id to its replacement result id
|
||||
std::unordered_map<uint32_t, uint32_t> remapped_ids_{};
|
||||
// Converted instructions that need deleted.
|
||||
std::vector<Instruction*> to_delete_{};
|
||||
|
||||
bool support_workgroup_ = false;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
#endif // LIBSPIRV_OPT_CONVERT_TO_UNTYPED_H_
|
||||
@@ -480,7 +480,12 @@ Pass::Status DeadBranchElimPass::Process() {
|
||||
return EliminateDeadBranches(fp);
|
||||
};
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
if (modified) FixBlockOrder();
|
||||
if (modified) {
|
||||
context()->InvalidateAnalyses(IRContext::kAnalysisCFG |
|
||||
IRContext::kAnalysisDominatorAnalysis |
|
||||
IRContext::kAnalysisStructuredCFG);
|
||||
FixBlockOrder();
|
||||
}
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
+23
-6
@@ -153,6 +153,7 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
|
||||
// such as inlining. Invalidate Constant and DefUse managers if used.
|
||||
uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) {
|
||||
uint32_t id = context->TakeNextId();
|
||||
if (id == 0) return 0;
|
||||
std::unique_ptr<Instruction> new_const(new Instruction(
|
||||
context, spv::Op::OpConstant, context->get_type_mgr()->GetUIntTypeId(),
|
||||
id,
|
||||
@@ -236,10 +237,12 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
else
|
||||
line_number =
|
||||
context()->get_constant_mgr()->GetUIntConstId(line_number);
|
||||
if (line_number == 0) return kNoInlinedAt;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (result_id == 0) return kNoInlinedAt;
|
||||
std::unique_ptr<Instruction> inlined_at(new Instruction(
|
||||
context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
@@ -417,6 +420,7 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() {
|
||||
if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (result_id == 0) return nullptr;
|
||||
std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
|
||||
context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
@@ -450,8 +454,10 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
|
||||
Instruction* insert_before) {
|
||||
auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id);
|
||||
if (inlined_at == nullptr) return nullptr;
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (result_id == 0) return nullptr;
|
||||
std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
|
||||
new_inlined_at->SetResultId(context()->TakeNextId());
|
||||
new_inlined_at->SetResultId(result_id);
|
||||
RegisterDbgInst(new_inlined_at.get());
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
|
||||
@@ -591,12 +597,18 @@ Instruction* DebugInfoManager::AddDebugValueForDecl(Instruction* dbg_decl,
|
||||
Instruction* line) {
|
||||
if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr;
|
||||
|
||||
Instruction* empty_expr = GetEmptyDebugExpression();
|
||||
if (empty_expr == nullptr) return nullptr;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (result_id == 0) return nullptr;
|
||||
|
||||
std::unique_ptr<Instruction> dbg_val(dbg_decl->Clone(context()));
|
||||
dbg_val->SetResultId(context()->TakeNextId());
|
||||
dbg_val->SetResultId(result_id);
|
||||
dbg_val->SetInOperand(kExtInstInstructionInIdx, {CommonDebugInfoDebugValue});
|
||||
dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id});
|
||||
dbg_val->SetOperand(kDebugValueOperandExpressionIndex,
|
||||
{GetEmptyDebugExpression()->result_id()});
|
||||
{empty_expr->result_id()});
|
||||
dbg_val->UpdateDebugInfoFrom(dbg_decl, line);
|
||||
|
||||
auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val));
|
||||
@@ -798,10 +810,16 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
|
||||
context()->ForgetUses(dbg_global_var);
|
||||
context()->AnalyzeUses(dbg_global_var);
|
||||
|
||||
Instruction* empty_expr = GetEmptyDebugExpression();
|
||||
if (empty_expr == nullptr) return;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (result_id == 0) return;
|
||||
|
||||
// Create a DebugDeclare
|
||||
std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
|
||||
context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
context()->TakeNextId(),
|
||||
result_id,
|
||||
{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
@@ -809,8 +827,7 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_global_var->result_id()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{GetEmptyDebugExpression()->result_id()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {empty_expr->result_id()}},
|
||||
}));
|
||||
// Must insert after all OpVariables in block
|
||||
Instruction* insert_before = local_var;
|
||||
|
||||
@@ -116,6 +116,11 @@ class DominatorAnalysisBase {
|
||||
// If there is no such basic block, nullptr is returned.
|
||||
BasicBlock* CommonDominator(BasicBlock* b1, BasicBlock* b2) const;
|
||||
|
||||
// Returns true if this analysis has the same tree as |other|.
|
||||
inline bool operator==(const DominatorAnalysisBase& other) const {
|
||||
return tree_ == other.tree_;
|
||||
}
|
||||
|
||||
protected:
|
||||
DominatorTree tree_;
|
||||
};
|
||||
|
||||
@@ -381,5 +381,24 @@ void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const {
|
||||
out_stream << "}\n";
|
||||
}
|
||||
|
||||
bool DominatorTree::operator==(const DominatorTree& other) const {
|
||||
if (postdominator_ != other.postdominator_) return false;
|
||||
if (nodes_.size() != other.nodes_.size()) return false;
|
||||
|
||||
for (const auto& pair : nodes_) {
|
||||
uint32_t id = pair.first;
|
||||
const DominatorTreeNode& node = pair.second;
|
||||
const DominatorTreeNode* other_node = other.GetTreeNode(id);
|
||||
if (!other_node) return false;
|
||||
|
||||
if ((node.parent_ == nullptr) != (other_node->parent_ == nullptr))
|
||||
return false;
|
||||
if (node.parent_ != nullptr) {
|
||||
if (node.parent_->id() != other_node->parent_->id()) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -202,6 +202,9 @@ class DominatorTree {
|
||||
// Returns true if this tree is a post dominator tree.
|
||||
bool IsPostDominator() const { return postdominator_; }
|
||||
|
||||
// Returns true if this tree has the same nodes and structure as |other|.
|
||||
bool operator==(const DominatorTree& other) const;
|
||||
|
||||
// Clean up the tree.
|
||||
void ClearTree() {
|
||||
nodes_.clear();
|
||||
|
||||
+107
-7
@@ -14,14 +14,14 @@
|
||||
|
||||
#include "source/opt/folding_rules.h"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "ir_builder.h"
|
||||
#include "source/latest_version_glsl_std_450_header.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@@ -2124,6 +2124,84 @@ std::vector<Operand> GetExtractOperandsForElementOfCompositeConstruct(
|
||||
return {};
|
||||
}
|
||||
|
||||
// If the OpCompositeConstruct that feeds an OpCopyLogical can be retyped to
|
||||
// the OpCopyLogical's result type, the layout conversion can be expressed at
|
||||
// constituent granularity instead of at aggregate granularity. This rewrites
|
||||
// the OpCopyLogical as an OpCompositeConstruct of the result type, using the
|
||||
// same constituents where their types already match the corresponding
|
||||
// field/element of the result type, and inserting per-field OpCopyLogical
|
||||
// instructions only for the fields that genuinely require a layout
|
||||
// conversion.
|
||||
bool CompositeConstructFeedingCopyLogical(
|
||||
IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == spv::Op::OpCopyLogical &&
|
||||
"Wrong opcode. Should be OpCopyLogical.");
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
|
||||
uint32_t src_id = inst->GetSingleWordInOperand(0);
|
||||
Instruction* src_inst = def_use_mgr->GetDef(src_id);
|
||||
if (src_inst->opcode() != spv::Op::OpCompositeConstruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Instruction* dst_type_inst = def_use_mgr->GetDef(inst->type_id());
|
||||
const uint32_t num_constituents = src_inst->NumInOperands();
|
||||
|
||||
// Determine the expected type id for each constituent of the destination
|
||||
// type.
|
||||
std::vector<uint32_t> expected_type_ids;
|
||||
expected_type_ids.reserve(num_constituents);
|
||||
if (dst_type_inst->opcode() == spv::Op::OpTypeStruct) {
|
||||
if (dst_type_inst->NumInOperands() != num_constituents) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < num_constituents; ++i) {
|
||||
expected_type_ids.push_back(dst_type_inst->GetSingleWordInOperand(i));
|
||||
}
|
||||
} else if (dst_type_inst->opcode() == spv::Op::OpTypeArray) {
|
||||
const uint32_t elem_type_id = dst_type_inst->GetSingleWordInOperand(0);
|
||||
for (uint32_t i = 0; i < num_constituents; ++i) {
|
||||
expected_type_ids.push_back(elem_type_id);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the new constituent list, inserting OpCopyLogical instructions for
|
||||
// the fields whose types differ from the result type.
|
||||
InstructionBuilder ir_builder(
|
||||
context, inst,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
std::vector<Operand> operands;
|
||||
operands.reserve(num_constituents);
|
||||
for (uint32_t i = 0; i < num_constituents; ++i) {
|
||||
const uint32_t cid = src_inst->GetSingleWordInOperand(i);
|
||||
Instruction* cdef = def_use_mgr->GetDef(cid);
|
||||
if (cdef->type_id() == expected_type_ids[i]) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {cid}});
|
||||
continue;
|
||||
}
|
||||
if (def_use_mgr->GetDef(expected_type_ids[i])->opcode() ==
|
||||
spv::Op::OpTypePointer) {
|
||||
assert(def_use_mgr->GetDef(expected_type_ids[i])->opcode() !=
|
||||
spv::Op::OpTypePointer &&
|
||||
"Unreachable for valid input");
|
||||
}
|
||||
Instruction* per_field_copy = ir_builder.AddUnaryOp(
|
||||
expected_type_ids[i], spv::Op::OpCopyLogical, cid);
|
||||
if (per_field_copy == nullptr) {
|
||||
return false;
|
||||
}
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {per_field_copy->result_id()}});
|
||||
}
|
||||
|
||||
inst->SetOpcode(spv::Op::OpCompositeConstruct);
|
||||
inst->SetInOperands(std::move(operands));
|
||||
context->UpdateDefUse(inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompositeConstructFeedingExtract(
|
||||
IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
@@ -3232,6 +3310,10 @@ FoldingRule MergeBinaryOpSelect(spv::Op opcode) {
|
||||
|
||||
return [opcode](IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
|
||||
const analysis::Constant* const_input = ConstInput(constants);
|
||||
if (!const_input) {
|
||||
return false;
|
||||
@@ -3241,11 +3323,28 @@ FoldingRule MergeBinaryOpSelect(spv::Op opcode) {
|
||||
return false;
|
||||
}
|
||||
std::vector<const analysis::Constant*> select_constants =
|
||||
context->get_constant_mgr()->GetOperandConstants(non_const);
|
||||
const_mgr->GetOperandConstants(non_const);
|
||||
if (!select_constants[1] || !select_constants[2]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The OpSelect that will be created below will use the condition from
|
||||
// `non_const` and a result type matching `inst`. Before SPIR-V 1.4,
|
||||
// OpSelect could not have a scalar condition with a vector result.
|
||||
// We must avoid generating the OpSelect if that would happen.
|
||||
const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
|
||||
if (result_type && result_type->AsVector()) {
|
||||
Instruction* cond_inst =
|
||||
def_use_mgr->GetDef(non_const->GetSingleWordInOperand(0));
|
||||
const analysis::Type* cond_type = type_mgr->GetType(cond_inst->type_id());
|
||||
if (cond_type && !cond_type->AsVector()) {
|
||||
if (spvVersionForTargetEnv(context->grammar().target_env()) <
|
||||
SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstructionBuilder ir_builder(
|
||||
context, inst,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
@@ -3274,15 +3373,13 @@ FoldingRule MergeBinaryOpSelect(spv::Op opcode) {
|
||||
if (context->get_instruction_folder().FoldInstruction(lhs)) {
|
||||
context->AnalyzeDefUse(lhs);
|
||||
while (lhs->opcode() == spv::Op::OpCopyObject) {
|
||||
lhs =
|
||||
context->get_def_use_mgr()->GetDef(lhs->GetSingleWordInOperand(0));
|
||||
lhs = def_use_mgr->GetDef(lhs->GetSingleWordInOperand(0));
|
||||
}
|
||||
}
|
||||
if (context->get_instruction_folder().FoldInstruction(rhs)) {
|
||||
context->AnalyzeDefUse(rhs);
|
||||
while (rhs->opcode() == spv::Op::OpCopyObject) {
|
||||
rhs =
|
||||
context->get_def_use_mgr()->GetDef(rhs->GetSingleWordInOperand(0));
|
||||
rhs = def_use_mgr->GetDef(rhs->GetSingleWordInOperand(0));
|
||||
}
|
||||
}
|
||||
inst->SetOpcode(spv::Op::OpSelect);
|
||||
@@ -4476,6 +4573,9 @@ void FoldingRules::AddFoldingRules() {
|
||||
rules_[spv::Op::OpCompositeConstruct].push_back(
|
||||
CompositeExtractFeedingConstruct);
|
||||
|
||||
rules_[spv::Op::OpCopyLogical].push_back(
|
||||
CompositeConstructFeedingCopyLogical);
|
||||
|
||||
rules_[spv::Op::OpCompositeExtract].push_back(InsertFeedingExtract());
|
||||
rules_[spv::Op::OpCompositeExtract].push_back(
|
||||
CompositeConstructFeedingExtract);
|
||||
|
||||
+52
-5
@@ -118,6 +118,12 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
// dominator analysis should be invalidated as well.
|
||||
if (analyses_to_invalidate & kAnalysisCFG) {
|
||||
analyses_to_invalidate |= kAnalysisDominatorAnalysis;
|
||||
analyses_to_invalidate |= kAnalysisStructuredCFG;
|
||||
}
|
||||
|
||||
if (analyses_to_invalidate & kAnalysisLoopAnalysis) {
|
||||
analyses_to_invalidate |= kAnalysisScalarEvolution;
|
||||
analyses_to_invalidate |= kAnalysisLiveness;
|
||||
}
|
||||
|
||||
if (analyses_to_invalidate & kAnalysisDefUse) {
|
||||
@@ -142,6 +148,9 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
dominator_trees_.clear();
|
||||
post_dominator_trees_.clear();
|
||||
}
|
||||
if (analyses_to_invalidate & kAnalysisLoopAnalysis) {
|
||||
loop_descriptors_.clear();
|
||||
}
|
||||
if (analyses_to_invalidate & kAnalysisNameMap) {
|
||||
id_to_name_.reset(nullptr);
|
||||
}
|
||||
@@ -160,6 +169,12 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
if (analyses_to_invalidate & kAnalysisLiveness) {
|
||||
liveness_mgr_.reset(nullptr);
|
||||
}
|
||||
if (analyses_to_invalidate & kAnalysisScalarEvolution) {
|
||||
scalar_evolution_analysis_.reset(nullptr);
|
||||
}
|
||||
if (analyses_to_invalidate & kAnalysisRegisterPressure) {
|
||||
reg_pressure_.reset(nullptr);
|
||||
}
|
||||
if (analyses_to_invalidate & kAnalysisTypes) {
|
||||
type_mgr_.reset(nullptr);
|
||||
}
|
||||
@@ -211,7 +226,8 @@ Instruction* IRContext::KillInst(Instruction* inst) {
|
||||
if (inst->opcode() == spv::Op::OpCapability ||
|
||||
inst->opcode() == spv::Op::OpConditionalCapabilityINTEL ||
|
||||
inst->opcode() == spv::Op::OpExtension ||
|
||||
inst->opcode() == spv::Op::OpConditionalExtensionINTEL) {
|
||||
inst->opcode() == spv::Op::OpConditionalExtensionINTEL ||
|
||||
inst->opcode() == spv::Op::OpExtInstImport) {
|
||||
// We reset the feature manager, instead of updating it, because it is just
|
||||
// as much work. We would have to remove all capabilities implied by this
|
||||
// capability that are not also implied by the remaining OpCapability
|
||||
@@ -271,7 +287,8 @@ void IRContext::CollectNonSemanticTree(
|
||||
work_list.pop_back();
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
i, [&work_list, to_kill, &seen](Instruction* user) {
|
||||
if (user->IsNonSemanticInstruction() && seen.insert(user).second) {
|
||||
if (user->IsNonSemanticInstruction() && !user->IsDebugLineInst() &&
|
||||
seen.insert(user).second) {
|
||||
work_list.push_back(user);
|
||||
to_kill->insert(user);
|
||||
}
|
||||
@@ -392,7 +409,6 @@ bool IRContext::IsConsistent() {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
if (AreAnalysesValid(kAnalysisIdToFuncMapping)) {
|
||||
for (auto& fn : *module_) {
|
||||
if (id_to_func_[fn.result_id()] != &fn) {
|
||||
@@ -437,6 +453,37 @@ bool IRContext::IsConsistent() {
|
||||
}
|
||||
}
|
||||
|
||||
if (AreAnalysesValid(kAnalysisDominatorAnalysis)) {
|
||||
for (const auto& it : dominator_trees_) {
|
||||
const Function* f = it.first;
|
||||
const DominatorAnalysis& cached_dom = it.second;
|
||||
DominatorAnalysis new_dom;
|
||||
new_dom.InitializeTree(*cfg(), f);
|
||||
|
||||
if (!(cached_dom == new_dom)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto& it : post_dominator_trees_) {
|
||||
const Function* f = it.first;
|
||||
const PostDominatorAnalysis& cached_post_dom = it.second;
|
||||
PostDominatorAnalysis new_post_dom;
|
||||
new_post_dom.InitializeTree(*cfg(), f);
|
||||
|
||||
if (!(cached_post_dom == new_post_dom)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AreAnalysesValid(kAnalysisStructuredCFG)) {
|
||||
StructuredCFGAnalysis new_struct_cfg(this);
|
||||
StructuredCFGAnalysis* cached_struct_cfg = struct_cfg_analysis_.get();
|
||||
if (!(*cached_struct_cfg == new_struct_cfg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_mgr_ != nullptr) {
|
||||
FeatureManager current(grammar_);
|
||||
current.Analyze(module());
|
||||
@@ -973,9 +1020,9 @@ void IRContext::AddCalls(const Function* func, std::queue<uint32_t>* todo) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
if (ii->opcode() == spv::Op::OpFunctionCall)
|
||||
todo->push(ii->GetSingleWordInOperand(0));
|
||||
if (ii->opcode() == spv::Op::OpCooperativeMatrixPerElementOpNV)
|
||||
if (ii->opcode() == spv::Op::OpCooperativeMatrixPerElementOpEXT)
|
||||
todo->push(ii->GetSingleWordInOperand(1));
|
||||
if (ii->opcode() == spv::Op::OpCooperativeMatrixReduceNV)
|
||||
if (ii->opcode() == spv::Op::OpCooperativeMatrixReduceEXT)
|
||||
todo->push(ii->GetSingleWordInOperand(2));
|
||||
if (ii->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) {
|
||||
const auto memory_operands_index = 3;
|
||||
|
||||
@@ -472,6 +472,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
|
||||
"SPV_KHR_maximal_reconvergence",
|
||||
"SPV_NV_push_constant_bank",
|
||||
"SPV_EXT_opacity_micromap",
|
||||
"SPV_KHR_opacity_micromap",
|
||||
"SPV_EXT_shader_invocation_reorder",
|
||||
"SPV_EXT_shader_atomic_float16_add",
|
||||
"SPV_KHR_abort",
|
||||
|
||||
@@ -309,6 +309,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
|
||||
"SPV_KHR_maximal_reconvergence",
|
||||
"SPV_NV_push_constant_bank",
|
||||
"SPV_EXT_opacity_micromap",
|
||||
"SPV_KHR_opacity_micromap",
|
||||
"SPV_EXT_shader_invocation_reorder",
|
||||
"SPV_EXT_shader_atomic_float16_add",
|
||||
"SPV_KHR_abort",
|
||||
|
||||
@@ -161,6 +161,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
|
||||
"SPV_KHR_maximal_reconvergence",
|
||||
"SPV_NV_push_constant_bank",
|
||||
"SPV_EXT_opacity_micromap",
|
||||
"SPV_KHR_opacity_micromap",
|
||||
"SPV_EXT_shader_invocation_reorder",
|
||||
"SPV_EXT_shader_atomic_float16_add",
|
||||
"SPV_KHR_abort",
|
||||
|
||||
+18
-3
@@ -17,10 +17,12 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/operand.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/reflect.h"
|
||||
#include "source/util/hash_combine.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@@ -149,7 +151,8 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
#undef DELEGATE
|
||||
}
|
||||
|
||||
void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
|
||||
void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop,
|
||||
bool filter_duplicate_decorations) const {
|
||||
binary->push_back(header_.magic_number);
|
||||
binary->push_back(header_.version);
|
||||
// TODO(antiagainst): should we change the generator number?
|
||||
@@ -162,9 +165,21 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
|
||||
const Instruction* last_line_inst = nullptr;
|
||||
bool between_merge_and_branch = false;
|
||||
bool between_label_and_phi_var = false;
|
||||
auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst,
|
||||
&between_merge_and_branch, &between_label_and_phi_var,
|
||||
std::unordered_set<std::vector<uint32_t>,
|
||||
spvtools::utils::VectorHash<uint32_t>>
|
||||
seen_decorations;
|
||||
auto write_inst = [binary, skip_nop, filter_duplicate_decorations,
|
||||
&last_scope, &last_line_inst, &between_merge_and_branch,
|
||||
&between_label_and_phi_var, &seen_decorations,
|
||||
this](const Instruction* i) {
|
||||
if (filter_duplicate_decorations && i->IsDecoration()) {
|
||||
std::vector<uint32_t> inst_binary;
|
||||
i->ToBinaryWithoutAttachedDebugInsts(&inst_binary);
|
||||
if (seen_decorations.count(inst_binary)) {
|
||||
return;
|
||||
}
|
||||
seen_decorations.insert(inst_binary);
|
||||
}
|
||||
// Skip emitting line instructions between merge and branch instructions.
|
||||
auto opcode = i->opcode();
|
||||
if (between_merge_and_branch && i->IsLineInst()) {
|
||||
|
||||
+6
-3
@@ -275,9 +275,12 @@ class Module {
|
||||
void ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false) const;
|
||||
|
||||
// Pushes the binary segments for this instruction into the back of *|binary|.
|
||||
// If |skip_nop| is true and this is a OpNop, do nothing.
|
||||
void ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const;
|
||||
// Pushes the binary segments for this module into the back of *`binary`.
|
||||
// If `skip_nop` is true, OpNop instructions will not be added to binary.
|
||||
// If `filter_duplicate_decorations` is true, duplicate decorations will not
|
||||
// be added to the binary.
|
||||
void ToBinary(std::vector<uint32_t>* binary, bool skip_nop,
|
||||
bool filter_duplicate_decorations = true) const;
|
||||
|
||||
// Returns 1 more than the maximum Id value mentioned in the module.
|
||||
uint32_t ComputeIdBound() const;
|
||||
|
||||
+9
-1
@@ -465,6 +465,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag,
|
||||
RegisterPass(CreateReplaceInvalidOpcodePass());
|
||||
} else if (pass_name == "convert-relaxed-to-half") {
|
||||
RegisterPass(CreateConvertRelaxedToHalfPass());
|
||||
} else if (pass_name == "convert-to-untyped") {
|
||||
RegisterPass(CreateConvertToUntypedPass());
|
||||
} else if (pass_name == "relax-float-ops") {
|
||||
RegisterPass(CreateRelaxFloatOpsPass());
|
||||
} else if (pass_name == "simplify-instructions") {
|
||||
@@ -719,7 +721,8 @@ bool Optimizer::Run(const uint32_t* original_binary,
|
||||
!context->module()->ContainsDebugInfo()) {
|
||||
std::vector<uint32_t> optimized_binary_with_nop;
|
||||
context->module()->ToBinary(&optimized_binary_with_nop,
|
||||
/* skip_nop = */ false);
|
||||
/* skip_nop = */ false,
|
||||
/* filter_duplicate_decorations = */ false);
|
||||
assert(optimized_binary_with_nop.size() == original_binary_size &&
|
||||
"Binary size unexpectedly changed despite the optimizer saying "
|
||||
"there was no change");
|
||||
@@ -1054,6 +1057,11 @@ Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
|
||||
MakeUnique<opt::ConvertToHalfPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateConvertToUntypedPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::ConvertToUntyped>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateRelaxFloatOpsPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::RelaxFloatOpsPass>());
|
||||
|
||||
+1
@@ -29,6 +29,7 @@
|
||||
#include "source/opt/compact_ids_pass.h"
|
||||
#include "source/opt/convert_to_half_pass.h"
|
||||
#include "source/opt/convert_to_sampled_image_pass.h"
|
||||
#include "source/opt/convert_to_untyped.h"
|
||||
#include "source/opt/copy_prop_arrays.h"
|
||||
#include "source/opt/dead_branch_elim_pass.h"
|
||||
#include "source/opt/dead_insert_elim_pass.h"
|
||||
|
||||
@@ -363,7 +363,8 @@ Pass::Status SetSpecConstantDefaultValuePass::Process() {
|
||||
// Returns true if the given char is ':', '\0' or considered as blank space
|
||||
// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' ').
|
||||
bool IsSeparator(char ch) {
|
||||
return std::strchr(":\0", ch) || std::isspace(ch) != 0;
|
||||
return std::strchr(":\0", ch) ||
|
||||
std::isspace(static_cast<unsigned char>(ch)) != 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<SetSpecConstantDefaultValuePass::SpecIdToValueStrMap>
|
||||
@@ -375,7 +376,8 @@ SetSpecConstantDefaultValuePass::ParseDefaultValuesString(const char* str) {
|
||||
// The parsing loop, break when points to the end.
|
||||
while (*str) {
|
||||
// Find the spec id.
|
||||
while (std::isspace(*str)) str++; // skip leading spaces.
|
||||
while (std::isspace(static_cast<unsigned char>(*str)))
|
||||
str++; // skip leading spaces.
|
||||
const char* entry_begin = str;
|
||||
while (!IsSeparator(*str)) str++;
|
||||
const char* entry_end = str;
|
||||
@@ -407,7 +409,7 @@ SetSpecConstantDefaultValuePass::ParseDefaultValuesString(const char* str) {
|
||||
(*spec_id_to_value)[spec_id] = std::string(val_begin, val_end - val_begin);
|
||||
|
||||
// Skip trailing spaces.
|
||||
while (std::isspace(*str)) str++;
|
||||
while (std::isspace(static_cast<unsigned char>(*str))) str++;
|
||||
}
|
||||
|
||||
return spec_id_to_value;
|
||||
|
||||
@@ -95,8 +95,14 @@ Pass::Status StripDebugInfoPass::Process() {
|
||||
|
||||
// clear OpLine information
|
||||
context()->module()->ForEachInst([&modified](Instruction* inst) {
|
||||
modified |= !inst->dbg_line_insts().empty();
|
||||
inst->dbg_line_insts().clear();
|
||||
if (!inst->dbg_line_insts().empty()) {
|
||||
modified = true;
|
||||
inst->dbg_line_insts().clear();
|
||||
}
|
||||
if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
|
||||
modified = true;
|
||||
inst->SetDebugScope({kNoDebugScope, kNoInlinedAt});
|
||||
}
|
||||
});
|
||||
|
||||
if (!get_module()->trailing_dbg_line_info().empty()) {
|
||||
|
||||
@@ -245,5 +245,18 @@ StructuredCFGAnalysis::FindFuncsCalledFromContinue() {
|
||||
return called_from_continue;
|
||||
}
|
||||
|
||||
bool StructuredCFGAnalysis::operator==(
|
||||
const StructuredCFGAnalysis& other) const {
|
||||
if (bb_to_construct_.size() != other.bb_to_construct_.size()) return false;
|
||||
for (const auto& pair : bb_to_construct_) {
|
||||
uint32_t bb_id = pair.first;
|
||||
const ConstructInfo& info = pair.second;
|
||||
auto it = other.bb_to_construct_.find(bb_id);
|
||||
if (it == other.bb_to_construct_.end()) return false;
|
||||
if (!(info == it->second)) return false;
|
||||
}
|
||||
return merge_blocks_ == other.merge_blocks_;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -115,6 +115,10 @@ class StructuredCFGAnalysis {
|
||||
// Return true if |bb_id| is the merge block for a construct.
|
||||
bool IsMergeBlock(uint32_t bb_id);
|
||||
|
||||
// Returns true if this analysis has the same constructs and merge blocks as
|
||||
// |other|.
|
||||
bool operator==(const StructuredCFGAnalysis& other) const;
|
||||
|
||||
// Returns the set of function ids that are called directly or indirectly from
|
||||
// a continue construct.
|
||||
std::unordered_set<uint32_t> FindFuncsCalledFromContinue();
|
||||
@@ -141,6 +145,13 @@ class StructuredCFGAnalysis {
|
||||
uint32_t containing_loop;
|
||||
uint32_t containing_switch;
|
||||
bool in_continue;
|
||||
|
||||
bool operator==(const ConstructInfo& other) const {
|
||||
return containing_construct == other.containing_construct &&
|
||||
containing_loop == other.containing_loop &&
|
||||
containing_switch == other.containing_switch &&
|
||||
in_continue == other.in_continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Populates |bb_to_construct_| with the innermost containing merge and loop
|
||||
|
||||
+13
-5
@@ -248,11 +248,19 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
{(type->AsInteger()->IsSigned() ? 1u : 0u)}}});
|
||||
break;
|
||||
case Type::kFloat:
|
||||
// TODO: Handle FP encoding enums once actually used.
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpTypeFloat, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsFloat()->width()}}});
|
||||
if (type->AsFloat()->encoding() == spv::FPEncoding::Max) {
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpTypeFloat, 0, id,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{type->AsFloat()->width()}}});
|
||||
} else {
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpTypeFloat, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsFloat()->width()}},
|
||||
{SPV_OPERAND_TYPE_FPENCODING,
|
||||
{static_cast<uint32_t>(type->AsFloat()->encoding())}}});
|
||||
}
|
||||
break;
|
||||
case Type::kVector: {
|
||||
uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type());
|
||||
|
||||
+20
@@ -400,6 +400,18 @@ bool Float::IsSameImpl(const Type* that, IsSameCache*) const {
|
||||
std::string Float::str() const {
|
||||
std::ostringstream oss;
|
||||
switch (encoding_) {
|
||||
case spv::FPEncoding::Float4E2M1EXT:
|
||||
assert(width_ == 4);
|
||||
oss << "fp4e2m1";
|
||||
break;
|
||||
case spv::FPEncoding::Float6E2M3EXT:
|
||||
assert(width_ == 6);
|
||||
oss << "fp6e2m3";
|
||||
break;
|
||||
case spv::FPEncoding::Float6E3M2EXT:
|
||||
assert(width_ == 6);
|
||||
oss << "fp6e3m2";
|
||||
break;
|
||||
case spv::FPEncoding::BFloat16KHR:
|
||||
assert(width_ == 16);
|
||||
oss << "bfloat16";
|
||||
@@ -412,6 +424,14 @@ std::string Float::str() const {
|
||||
assert(width_ == 8);
|
||||
oss << "fp8e5m2";
|
||||
break;
|
||||
case spv::FPEncoding::Float8UnsignedE8M0EXT:
|
||||
assert(width_ == 8);
|
||||
oss << "fp8e8m0";
|
||||
break;
|
||||
case spv::FPEncoding::MXInt8EXT:
|
||||
assert(width_ == 8);
|
||||
oss << "mxint8";
|
||||
break;
|
||||
default:
|
||||
oss << "float" << width_;
|
||||
break;
|
||||
|
||||
+20
@@ -51,6 +51,18 @@ void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst,
|
||||
case SPV_FP_ENCODING_IEEE754_BINARY32:
|
||||
*out << spvtools::utils::FloatProxy<float>(word);
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT4_E2M1:
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::Float4_E2M1>(
|
||||
uint8_t(word & 0xF));
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT6_E2M3:
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::Float6_E2M3>(
|
||||
uint8_t(word & 0x3F));
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT6_E3M2:
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::Float6_E3M2>(
|
||||
uint8_t(word & 0x3F));
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT8_E4M3:
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>(
|
||||
uint8_t(word & 0xFF));
|
||||
@@ -59,6 +71,14 @@ void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst,
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>(
|
||||
uint8_t(word & 0xFF));
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT8_UNSIGNED_E8M0:
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E8M0>(
|
||||
uint8_t(word & 0xFF));
|
||||
break;
|
||||
case SPV_FP_ENCODING_MXINT8:
|
||||
*out << spvtools::utils::HexFixedPoint<spvtools::utils::MXInt8>(
|
||||
uint8_t(word & 0xFF));
|
||||
break;
|
||||
case SPV_FP_ENCODING_BFLOAT16:
|
||||
*out << spvtools::utils::FloatProxy<spvtools::utils::BFloat16>(
|
||||
uint16_t(word & 0xFFFF));
|
||||
|
||||
+3
-2
@@ -226,7 +226,8 @@ bool spvReadEnvironmentFromText(const std::vector<char>& text,
|
||||
char minor = text[minor_digit_pos];
|
||||
char next_char =
|
||||
minor_digit_pos + 1 < text.size() ? text[minor_digit_pos + 1] : 0;
|
||||
if (std::isdigit(minor) && !std::isdigit(next_char)) {
|
||||
if (std::isdigit(static_cast<unsigned char>(minor)) &&
|
||||
!std::isdigit(static_cast<unsigned char>(next_char))) {
|
||||
const auto index = minor - '0';
|
||||
assert(index >= 0);
|
||||
if (static_cast<size_t>(index) < ordered_universal_envs.size()) {
|
||||
@@ -243,7 +244,7 @@ bool spvReadEnvironmentFromText(const std::vector<char>& text,
|
||||
for (; i < text.size(); ++i) {
|
||||
if (text[i] == '\n') break;
|
||||
}
|
||||
} else if (!std::isspace(c)) {
|
||||
} else if (!std::isspace(static_cast<unsigned char>(c))) {
|
||||
// Allow blanks, but end the search if we find something else.
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -140,3 +140,30 @@ void spvValidatorOptionsSetFriendlyNames(spv_validator_options options,
|
||||
bool val) {
|
||||
options->use_friendly_names = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetBufferDescriptorLayout(spv_validator_options options,
|
||||
uint32_t size,
|
||||
uint32_t alignment) {
|
||||
options->buffer_descriptor_layout.size = size;
|
||||
options->buffer_descriptor_layout.alignment = alignment;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetImageDescriptorLayout(spv_validator_options options,
|
||||
uint32_t size,
|
||||
uint32_t alignment) {
|
||||
options->image_descriptor_layout.size = size;
|
||||
options->image_descriptor_layout.alignment = alignment;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetSamplerDescriptorLayout(
|
||||
spv_validator_options options, uint32_t size, uint32_t alignment) {
|
||||
options->sampler_descriptor_layout.size = size;
|
||||
options->sampler_descriptor_layout.alignment = alignment;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetTensorDescriptorLayout(spv_validator_options options,
|
||||
uint32_t size,
|
||||
uint32_t alignment) {
|
||||
options->tensor_descriptor_layout.size = size;
|
||||
options->tensor_descriptor_layout.alignment = alignment;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ struct validator_universal_limits_t {
|
||||
uint32_t max_id_bound{0x3FFFFF};
|
||||
};
|
||||
|
||||
struct OpaqueResourceLayout {
|
||||
uint32_t size{0};
|
||||
uint32_t alignment{1};
|
||||
};
|
||||
|
||||
// Manages command line options passed to the SPIR-V Validator. New struct
|
||||
// members may be added for any new option.
|
||||
struct spv_validator_options_t {
|
||||
@@ -66,6 +71,11 @@ struct spv_validator_options_t {
|
||||
bool allow_vulkan_32_bit_bitwise;
|
||||
bool before_hlsl_legalization;
|
||||
bool use_friendly_names;
|
||||
|
||||
OpaqueResourceLayout buffer_descriptor_layout;
|
||||
OpaqueResourceLayout image_descriptor_layout;
|
||||
OpaqueResourceLayout sampler_descriptor_layout;
|
||||
OpaqueResourceLayout tensor_descriptor_layout;
|
||||
};
|
||||
|
||||
#endif // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_
|
||||
|
||||
Vendored
+1
-1
@@ -45,7 +45,7 @@
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
bool spvIsValidIDCharacter(const char value) {
|
||||
return value == '_' || 0 != ::isalnum(value);
|
||||
return value == '_' || 0 != ::isalnum(static_cast<unsigned char>(value));
|
||||
}
|
||||
|
||||
// Returns true if the given string represents a valid ID name.
|
||||
|
||||
@@ -168,8 +168,17 @@ bool validBitWidthForFPEncoding(spv_fp_encoding_t enc, uint32_t width,
|
||||
case SPV_FP_ENCODING_IEEE754_BINARY64:
|
||||
*expected = 64;
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT4_E2M1:
|
||||
*expected = 4;
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT6_E2M3:
|
||||
case SPV_FP_ENCODING_FLOAT6_E3M2:
|
||||
*expected = 6;
|
||||
break;
|
||||
case SPV_FP_ENCODING_FLOAT8_E5M2:
|
||||
case SPV_FP_ENCODING_FLOAT8_E4M3:
|
||||
case SPV_FP_ENCODING_FLOAT8_UNSIGNED_E8M0:
|
||||
case SPV_FP_ENCODING_MXINT8:
|
||||
*expected = 8;
|
||||
break;
|
||||
default:
|
||||
|
||||
+10
@@ -78,5 +78,15 @@ std::ostream& operator<<(std::ostream& out, const BitVector& bv) {
|
||||
return out;
|
||||
}
|
||||
|
||||
bool BitVector::operator==(const BitVector& other) const {
|
||||
size_t max_size = std::max(bits_.size(), other.bits_.size());
|
||||
for (size_t i = 0; i < max_size; ++i) {
|
||||
BitContainer b1 = (i < bits_.size()) ? bits_[i] : 0;
|
||||
BitContainer b2 = (i < other.bits_.size()) ? other.bits_[i] : 0;
|
||||
if (b1 != b2) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -109,6 +109,10 @@ class BitVector {
|
||||
// |this|. Return true if |this| changed.
|
||||
bool Or(const BitVector& that);
|
||||
|
||||
// Returns true if |this| and |that| have the same bits set.
|
||||
bool operator==(const BitVector& that) const;
|
||||
bool operator!=(const BitVector& that) const { return !(*this == that); }
|
||||
|
||||
private:
|
||||
std::vector<BitContainer> bits_;
|
||||
};
|
||||
|
||||
@@ -47,6 +47,13 @@ inline size_t hash_combine(std::size_t hash, const T& val,
|
||||
return hash_combine(hash_combine(hash, val), args...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct VectorHash {
|
||||
size_t operator()(const std::vector<T>& v) const {
|
||||
return hash_combine(0, v);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
} // namespace spvtools
|
||||
|
||||
|
||||
+730
-62
File diff suppressed because it is too large
Load Diff
@@ -159,6 +159,45 @@ EncodeNumberStatus ParseAndEncodeFloatingPointNumber(
|
||||
|
||||
const auto bit_width = AssumedBitWidth(type);
|
||||
switch (DeduceEncoding(type)) {
|
||||
case SPV_FP_ENCODING_FLOAT4_E2M1: {
|
||||
HexFloat<FloatProxy<Float4_E2M1>> hVal(0);
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
ErrorMsgStream(error_msg) << "Invalid E2M1 float literal: " << text;
|
||||
return EncodeNumberStatus::kInvalidText;
|
||||
}
|
||||
// getAsFloat will return the Float16 value, and get_value
|
||||
// will return a uint8_t representing the bits of the float.
|
||||
// The encoding is therefore correct from the perspective of the SPIR-V
|
||||
// spec since the top 28 bits will be 0.
|
||||
emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
|
||||
return EncodeNumberStatus::kSuccess;
|
||||
} break;
|
||||
case SPV_FP_ENCODING_FLOAT6_E2M3: {
|
||||
HexFloat<FloatProxy<Float6_E2M3>> hVal(0);
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
ErrorMsgStream(error_msg) << "Invalid E2M3 float literal: " << text;
|
||||
return EncodeNumberStatus::kInvalidText;
|
||||
}
|
||||
// getAsFloat will return the Float16 value, and get_value
|
||||
// will return a uint8_t representing the bits of the float.
|
||||
// The encoding is therefore correct from the perspective of the SPIR-V
|
||||
// spec since the top 26 bits will be 0.
|
||||
emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
|
||||
return EncodeNumberStatus::kSuccess;
|
||||
} break;
|
||||
case SPV_FP_ENCODING_FLOAT6_E3M2: {
|
||||
HexFloat<FloatProxy<Float6_E3M2>> hVal(0);
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
ErrorMsgStream(error_msg) << "Invalid E3M2 float literal: " << text;
|
||||
return EncodeNumberStatus::kInvalidText;
|
||||
}
|
||||
// getAsFloat will return the Float16 value, and get_value
|
||||
// will return a uint8_t representing the bits of the float.
|
||||
// The encoding is therefore correct from the perspective of the SPIR-V
|
||||
// spec since the top 26 bits will be 0.
|
||||
emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
|
||||
return EncodeNumberStatus::kSuccess;
|
||||
} break;
|
||||
case SPV_FP_ENCODING_FLOAT8_E4M3: {
|
||||
HexFloat<FloatProxy<Float8_E4M3>> hVal(0);
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
@@ -185,6 +224,28 @@ EncodeNumberStatus ParseAndEncodeFloatingPointNumber(
|
||||
emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
|
||||
return EncodeNumberStatus::kSuccess;
|
||||
} break;
|
||||
case SPV_FP_ENCODING_FLOAT8_UNSIGNED_E8M0: {
|
||||
HexFloat<FloatProxy<Float8_E8M0>> hVal(0);
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
ErrorMsgStream(error_msg) << "Invalid E8M0 float literal: " << text;
|
||||
return EncodeNumberStatus::kInvalidText;
|
||||
}
|
||||
// getAsFloat will return the Float16 value, and get_value
|
||||
// will return a uint16_t representing the bits of the float.
|
||||
// The encoding is therefore correct from the perspective of the SPIR-V
|
||||
// spec since the top 16 bits will be 0.
|
||||
emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
|
||||
return EncodeNumberStatus::kSuccess;
|
||||
} break;
|
||||
case SPV_FP_ENCODING_MXINT8: {
|
||||
HexFixedPoint<MXInt8> hVal;
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
ErrorMsgStream(error_msg) << "Invalid MXInt8 float literal: " << text;
|
||||
return EncodeNumberStatus::kInvalidText;
|
||||
}
|
||||
emit(static_cast<uint32_t>(hVal.value()));
|
||||
return EncodeNumberStatus::kSuccess;
|
||||
} break;
|
||||
case SPV_FP_ENCODING_BFLOAT16: {
|
||||
HexFloat<FloatProxy<BFloat16>> hVal(0);
|
||||
if (!ParseNumber(text, &hVal)) {
|
||||
|
||||
+14
-3
@@ -169,6 +169,16 @@ template <typename T>
|
||||
struct IsHexFloat<HexFloat<T>> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsHexFixedPoint {
|
||||
static const bool value = false;
|
||||
};
|
||||
template <typename T>
|
||||
struct IsHexFixedPoint<HexFixedPoint<T>> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
// Parses a numeric value of a given type from the given text. The number
|
||||
// should take up the entire string, and should be within bounds for the target
|
||||
// type. On success, returns true and populates the object referenced by
|
||||
@@ -179,9 +189,10 @@ bool ParseNumber(const char* text, T* value_pointer) {
|
||||
// with a single-byte type leads to implementation-defined behaviour.
|
||||
// Similarly for uint8_t.
|
||||
// HexFloat<T> overloads the operator
|
||||
static_assert(sizeof(T) > 1 || IsHexFloat<T>::value,
|
||||
"Single-byte types other than HexFloat<> are not supported in "
|
||||
"this parse method");
|
||||
static_assert(
|
||||
sizeof(T) > 1 || IsHexFloat<T>::value || IsHexFixedPoint<T>::value,
|
||||
"Single-byte types other than HexFloat<> are not supported in "
|
||||
"this parse method");
|
||||
|
||||
if (!text) return false;
|
||||
std::istringstream text_stream(text);
|
||||
|
||||
+9
@@ -23,6 +23,11 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#endif
|
||||
|
||||
// A macro to call spvtools::utils::PrintTimerDescription(std::ostream*, bool).
|
||||
// The first argument must be given as std::ostream*. If it is NULL, the
|
||||
// function does nothing. Otherwise, it prints resource types measured by Timer
|
||||
@@ -382,6 +387,10 @@ class CumulativeTimer : public Timer {
|
||||
} // namespace utils
|
||||
} // namespace spvtools
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#else // defined(SPIRV_TIMER_ENABLED)
|
||||
|
||||
#define SPIRV_TIMER_DESCRIPTION(...)
|
||||
|
||||
+9
-1
@@ -18,6 +18,7 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "source/binary.h"
|
||||
@@ -225,6 +226,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
bool has_mask_task_nv = false;
|
||||
bool has_mask_task_ext = false;
|
||||
std::vector<Instruction*> visited_entry_points;
|
||||
std::unordered_set<std::string> graph_entry_point_names;
|
||||
for (auto& instruction : vstate->ordered_instructions()) {
|
||||
{
|
||||
// In order to do this work outside of Process Instruction we need to be
|
||||
@@ -281,7 +283,12 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
execution_model == spv::ExecutionModel::MeshEXT);
|
||||
}
|
||||
if (inst->opcode() == spv::Op::OpGraphEntryPointARM) {
|
||||
const auto graph = inst->GetOperandAs<uint32_t>(1);
|
||||
const std::string name = inst->GetOperandAs<std::string>(1);
|
||||
if (!graph_entry_point_names.insert(name).second) {
|
||||
return vstate->diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Graph entry points cannot share the same name.";
|
||||
}
|
||||
const auto graph = inst->GetOperandAs<uint32_t>(0);
|
||||
vstate->RegisterGraphEntryPoint(graph);
|
||||
}
|
||||
if (inst->opcode() == spv::Op::OpFunctionCall) {
|
||||
@@ -420,6 +427,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
if (auto error = PerformCfgChecks(*vstate)) return error;
|
||||
if (auto error = CheckIdDefinitionDominateUse(*vstate)) return error;
|
||||
if (auto error = ValidateDecorations(*vstate)) return error;
|
||||
if (auto error = ValidateExplicitLayout(*vstate)) return error;
|
||||
if (auto error = ValidateInterfaces(*vstate)) return error;
|
||||
// TODO(dsinclair): Restructure ValidateBuiltins so we can move into the
|
||||
// for() above as it loops over all ordered_instructions internally.
|
||||
|
||||
+2
@@ -144,6 +144,8 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst);
|
||||
/// has been propagated down to the group members.
|
||||
spv_result_t ValidateDecorations(ValidationState_t& _);
|
||||
|
||||
spv_result_t ValidateExplicitLayout(ValidationState_t& _);
|
||||
|
||||
/// Performs validation of built-in variables.
|
||||
spv_result_t ValidateBuiltIns(ValidationState_t& _);
|
||||
|
||||
|
||||
@@ -215,6 +215,29 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
|
||||
<< fail() << "must be a variable";
|
||||
}
|
||||
break;
|
||||
case spv::Decoration::CooperativeMatrixTransposeEXT:
|
||||
switch (target->opcode()) {
|
||||
case spv::Op::OpConvertFToU:
|
||||
case spv::Op::OpConvertFToS:
|
||||
case spv::Op::OpConvertSToF:
|
||||
case spv::Op::OpConvertUToF:
|
||||
case spv::Op::OpUConvert:
|
||||
case spv::Op::OpSConvert:
|
||||
case spv::Op::OpFConvert:
|
||||
case spv::Op::OpCooperativeMatrixConvertUseEXT:
|
||||
break;
|
||||
default:
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< fail()
|
||||
<< "must be a cooperative matrix conversion instruction";
|
||||
}
|
||||
if (!_.IsCooperativeMatrixKHRType(target->type_id()) ||
|
||||
!_.IsCooperativeMatrixKHRType(_.GetOperandTypeId(target, 2))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< fail()
|
||||
<< "must be a cooperative matrix conversion instruction";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+37
-22
@@ -63,8 +63,8 @@ spv_result_t ValidateFloat(ValidationState_t& _, const Instruction* inst,
|
||||
<< "Expected arithmetic operands to be of Result Type: "
|
||||
<< spvOpcodeString(opcode) << " operand index " << operand_index;
|
||||
}
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
|
||||
spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type,
|
||||
type_id, false, false);
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else if (_.GetOperandTypeId(inst, operand_index) != result_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
@@ -108,8 +108,8 @@ spv_result_t ValidateUnsignedInt(ValidationState_t& _, const Instruction* inst,
|
||||
<< "Expected arithmetic operands to be of Result Type: "
|
||||
<< spvOpcodeString(opcode) << " operand index " << operand_index;
|
||||
}
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
|
||||
spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type,
|
||||
type_id, false, false);
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else if (_.GetOperandTypeId(inst, operand_index) != result_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
@@ -164,8 +164,8 @@ spv_result_t ValidateSignedInt(ValidationState_t& _, const Instruction* inst,
|
||||
<< "Expected arithmetic operands to be of Result Type: "
|
||||
<< spvOpcodeString(opcode) << " operand index " << operand_index;
|
||||
}
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
|
||||
spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type,
|
||||
type_id, false, false);
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
}
|
||||
|
||||
@@ -715,8 +715,8 @@ spv_result_t ValidateCooperativeMatrixMulAddKHR(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCooperativeMatrixReduceNV(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
spv_result_t ValidateCooperativeMatrixReduceEXT(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const spv::Op opcode = inst->opcode();
|
||||
const uint32_t result_type = inst->type_id();
|
||||
if (!_.IsCooperativeMatrixKHRType(result_type)) {
|
||||
@@ -744,33 +744,48 @@ spv_result_t ValidateCooperativeMatrixReduceNV(ValidationState_t& _,
|
||||
"type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
if (_.FindDef(result_type)->GetOperandAs<uint32_t>(2) !=
|
||||
matrix_type->GetOperandAs<uint32_t>(2)) {
|
||||
const auto result_scope =
|
||||
_.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(2));
|
||||
const auto matrix_scope =
|
||||
_.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(2));
|
||||
if (std::get<1>(result_scope) && std::get<1>(matrix_scope) &&
|
||||
std::get<2>(result_scope) != std::get<2>(matrix_scope)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Result Type and Matrix type must have the same scope: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (!_.IsCooperativeMatrixAccType(result_type)) {
|
||||
const auto result_use =
|
||||
_.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(5));
|
||||
if (std::get<1>(result_use) &&
|
||||
std::get<2>(result_use) !=
|
||||
uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Result Type must have UseAccumulator: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
if (!_.IsCooperativeMatrixAccType(matrix_type_id)) {
|
||||
const auto matrix_use =
|
||||
_.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(5));
|
||||
if (std::get<1>(matrix_use) &&
|
||||
std::get<2>(matrix_use) !=
|
||||
uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Matrix type must have UseAccumulator: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
const auto reduce_value = inst->GetOperandAs<uint32_t>(3);
|
||||
const uint32_t reduce_row = uint32_t(spv::CooperativeMatrixReduceMask::Row);
|
||||
const uint32_t reduce_column =
|
||||
uint32_t(spv::CooperativeMatrixReduceMask::Column);
|
||||
const uint32_t reduce_row_column = reduce_row | reduce_column;
|
||||
const uint32_t reduce_2x2 =
|
||||
uint32_t(spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2);
|
||||
|
||||
if ((reduce_value &
|
||||
uint32_t(
|
||||
spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) &&
|
||||
(reduce_value & uint32_t(spv::CooperativeMatrixReduceMask::Row |
|
||||
spv::CooperativeMatrixReduceMask::Column))) {
|
||||
if (reduce_value != reduce_row && reduce_value != reduce_column &&
|
||||
reduce_value != reduce_row_column && reduce_value != reduce_2x2) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Reduce 2x2 must not be used with Row/Column: "
|
||||
<< "Reduce must be Row, Column, Row|Column, or 2x2: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
@@ -787,8 +802,8 @@ spv_result_t ValidateCooperativeMatrixReduceNV(ValidationState_t& _,
|
||||
uint32_t(spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) {
|
||||
if (std::get<1>(result_rows) && std::get<1>(result_cols) &&
|
||||
std::get<1>(matrix_rows) && std::get<1>(matrix_cols) &&
|
||||
(std::get<2>(result_rows) != std::get<2>(matrix_rows) / 2 ||
|
||||
std::get<2>(result_cols) != std::get<2>(matrix_cols) / 2)) {
|
||||
(uint64_t{std::get<2>(result_rows)} * 2 != std::get<2>(matrix_rows) ||
|
||||
uint64_t{std::get<2>(result_cols)} * 2 != std::get<2>(matrix_cols))) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For Reduce2x2, result rows/cols must be half of matrix "
|
||||
"rows/cols: "
|
||||
@@ -884,8 +899,8 @@ spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) {
|
||||
return ValidateCooperativeMatrixMulAddNV(_, inst);
|
||||
case spv::Op::OpCooperativeMatrixMulAddKHR:
|
||||
return ValidateCooperativeMatrixMulAddKHR(_, inst);
|
||||
case spv::Op::OpCooperativeMatrixReduceNV:
|
||||
return ValidateCooperativeMatrixReduceNV(_, inst);
|
||||
case spv::Op::OpCooperativeMatrixReduceEXT:
|
||||
return ValidateCooperativeMatrixReduceEXT(_, inst);
|
||||
|
||||
case spv::Op::OpSpecConstantOp: {
|
||||
switch (inst->GetOperandAs<spv::Op>(2u)) {
|
||||
|
||||
+45
-20
@@ -33,27 +33,52 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) {
|
||||
const uint32_t result_type = inst->type_id();
|
||||
|
||||
switch (opcode) {
|
||||
case spv::Op::OpControlBarrier: {
|
||||
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation(
|
||||
[](spv::ExecutionModel model, std::string* message) {
|
||||
if (model != spv::ExecutionModel::TessellationControl &&
|
||||
model != spv::ExecutionModel::GLCompute &&
|
||||
model != spv::ExecutionModel::Kernel &&
|
||||
model != spv::ExecutionModel::TaskNV &&
|
||||
model != spv::ExecutionModel::MeshNV) {
|
||||
if (message) {
|
||||
*message =
|
||||
"In SPIR-V 1.2 or earlier, OpControlBarrier requires "
|
||||
"one of the following "
|
||||
"Execution Models: TessellationControl, GLCompute, "
|
||||
"Kernel, MeshNV or TaskNV";
|
||||
}
|
||||
return false;
|
||||
case spv::Op::OpControlBarrier:
|
||||
case spv::Op::OpControlBarrierArriveEXT:
|
||||
case spv::Op::OpControlBarrierWaitEXT: {
|
||||
if (opcode == spv::Op::OpControlBarrier) {
|
||||
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
|
||||
std::string* message) {
|
||||
if (model != spv::ExecutionModel::TessellationControl &&
|
||||
model != spv::ExecutionModel::GLCompute &&
|
||||
model != spv::ExecutionModel::Kernel &&
|
||||
model != spv::ExecutionModel::TaskNV &&
|
||||
model != spv::ExecutionModel::MeshNV) {
|
||||
if (message) {
|
||||
*message =
|
||||
"In SPIR-V 1.2 or earlier, OpControlBarrier requires "
|
||||
"one of the following "
|
||||
"Execution Models: TessellationControl, GLCompute, "
|
||||
"Kernel, MeshNV or TaskNV";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == spv::Op::OpControlBarrierArriveEXT ||
|
||||
opcode == spv::Op::OpControlBarrierWaitEXT) {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation([&_](spv::ExecutionModel model,
|
||||
std::string* message) {
|
||||
if (model != spv::ExecutionModel::GLCompute &&
|
||||
model != spv::ExecutionModel::Kernel) {
|
||||
if (message) {
|
||||
*message = _.VkErrorID(13552)
|
||||
.append(
|
||||
"The SplitBarrierEXT capability must not "
|
||||
"be enabled in any stage other than "
|
||||
"GLCompute or Kernel.")
|
||||
.c_str();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
const uint32_t execution_scope = inst->word(1);
|
||||
|
||||
@@ -147,6 +147,11 @@ bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
|
||||
case spv::Capability::Int8:
|
||||
case spv::Capability::BFloat16TypeKHR:
|
||||
case spv::Capability::Float8EXT:
|
||||
case spv::Capability::Float4EXT:
|
||||
case spv::Capability::Float6EXT:
|
||||
case spv::Capability::Float8UnsignedE8M0EXT:
|
||||
case spv::Capability::MXInt8EXT:
|
||||
case spv::Capability::BitcastExtractEXT:
|
||||
case spv::Capability::PushConstantBanksNV:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -345,6 +346,9 @@ spv_result_t ValidateLoopMerge(ValidationState_t& _, const Instruction* inst) {
|
||||
if ((loop_control >> spv::LoopControlShift::PartialCount) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
if ((loop_control >> spv::LoopControlShift::MultipleWaitQueuesQCOM) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
|
||||
// That the right number of operands is present is checked by the parser. The
|
||||
// above code tracks operands for expanded validation checking in the future.
|
||||
|
||||
@@ -633,6 +633,10 @@ spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpPtrAccessChain:
|
||||
case spv::Op::OpInBoundsPtrAccessChain:
|
||||
case spv::Op::OpUntypedAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsAccessChainKHR:
|
||||
case spv::Op::OpUntypedPtrAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
|
||||
if (!_.HasCapability(spv::Capability::Kernel)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Specialization constant operation " << spvOpcodeString(op)
|
||||
|
||||
+243
-25
@@ -26,6 +26,45 @@
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsNumericScalarOrVectorType(ValidationState_t& _, uint32_t type_id) {
|
||||
return _.IsFloatScalarType(type_id) || _.IsFloatVectorType(type_id) ||
|
||||
_.IsIntScalarType(type_id) || _.IsIntVectorType(type_id);
|
||||
}
|
||||
|
||||
bool IsIEEEOrAlternativeFloatTypeAllowedForOCPMicroscalingFConvert(
|
||||
ValidationState_t& _, uint32_t type_id) {
|
||||
const uint32_t component_type = _.GetComponentType(type_id);
|
||||
const Instruction* inst = _.FindDef(component_type);
|
||||
if (!inst || inst->opcode() != spv::Op::OpTypeFloat) return false;
|
||||
|
||||
if (inst->words().size() <= 3) return true;
|
||||
|
||||
const auto encoding = inst->GetOperandAs<spv::FPEncoding>(2);
|
||||
return encoding == spv::FPEncoding::Float8E4M3EXT ||
|
||||
encoding == spv::FPEncoding::Float8E5M2EXT ||
|
||||
encoding == spv::FPEncoding::BFloat16KHR;
|
||||
}
|
||||
|
||||
spv_result_t ValidateVulkanOCPMicroscalingFloatIntConversion(
|
||||
ValidationState_t& _, const Instruction* inst, uint32_t float_type) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
_.ContainsOCPMicroscalingType(float_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12465) << spvOpcodeString(inst->opcode())
|
||||
<< " must not consume or produce OCP microscaling types in the "
|
||||
"Vulkan environment.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
bool HasCoopMatTranspose(ValidationState_t& _, uint32_t id) {
|
||||
return _.HasDecoration(id, spv::Decoration::CooperativeMatrixTransposeEXT);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t ValidateShaderBitWidth(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (_.HasCapability(spv::Capability::Shader)) {
|
||||
@@ -68,7 +107,8 @@ spv_result_t ValidateConvertFToU(ValidationState_t& _, const Instruction* inst,
|
||||
} else if (_.IsCooperativeMatrixType(result_type) ||
|
||||
_.IsCooperativeMatrixType(input_type)) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true,
|
||||
HasCoopMatTranspose(_, inst->id()));
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else {
|
||||
if (_.GetDimension(result_type) != _.GetDimension(input_type))
|
||||
@@ -77,6 +117,10 @@ spv_result_t ValidateConvertFToU(ValidationState_t& _, const Instruction* inst,
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (auto error =
|
||||
ValidateVulkanOCPMicroscalingFloatIntConversion(_, inst, input_type))
|
||||
return error;
|
||||
|
||||
if (auto error = ValidateShaderBitWidth(_, inst)) return error;
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@@ -110,7 +154,8 @@ spv_result_t ValidateConvertFToS(ValidationState_t& _, const Instruction* inst,
|
||||
} else if (_.IsCooperativeMatrixType(result_type) ||
|
||||
_.IsCooperativeMatrixType(input_type)) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true,
|
||||
HasCoopMatTranspose(_, inst->id()));
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else {
|
||||
if (_.GetDimension(result_type) != _.GetDimension(input_type))
|
||||
@@ -119,6 +164,10 @@ spv_result_t ValidateConvertFToS(ValidationState_t& _, const Instruction* inst,
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (auto error =
|
||||
ValidateVulkanOCPMicroscalingFloatIntConversion(_, inst, input_type))
|
||||
return error;
|
||||
|
||||
if (auto error = ValidateShaderBitWidth(_, inst)) return error;
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@@ -153,7 +202,8 @@ spv_result_t ValidateConvertIntToF(ValidationState_t& _,
|
||||
} else if (_.IsCooperativeMatrixType(result_type) ||
|
||||
_.IsCooperativeMatrixType(input_type)) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true,
|
||||
HasCoopMatTranspose(_, inst->id()));
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else {
|
||||
if (_.GetDimension(result_type) != _.GetDimension(input_type))
|
||||
@@ -162,6 +212,10 @@ spv_result_t ValidateConvertIntToF(ValidationState_t& _,
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (auto error =
|
||||
ValidateVulkanOCPMicroscalingFloatIntConversion(_, inst, result_type))
|
||||
return error;
|
||||
|
||||
if (auto error = ValidateShaderBitWidth(_, inst)) return error;
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@@ -196,7 +250,8 @@ spv_result_t ValidateUConvert(ValidationState_t& _, const Instruction* inst,
|
||||
} else if (_.IsCooperativeMatrixType(result_type) ||
|
||||
_.IsCooperativeMatrixType(input_type)) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true,
|
||||
HasCoopMatTranspose(_, inst->id()));
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else {
|
||||
if (_.GetDimension(result_type) != _.GetDimension(input_type))
|
||||
@@ -241,7 +296,8 @@ spv_result_t ValidateSConvert(ValidationState_t& _, const Instruction* inst,
|
||||
} else if (_.IsCooperativeMatrixType(result_type) ||
|
||||
_.IsCooperativeMatrixType(input_type)) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true,
|
||||
HasCoopMatTranspose(_, inst->id()));
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else {
|
||||
if (_.GetDimension(result_type) != _.GetDimension(input_type))
|
||||
@@ -286,7 +342,8 @@ spv_result_t ValidateFConvert(ValidationState_t& _, const Instruction* inst,
|
||||
} else if (_.IsCooperativeMatrixType(result_type) ||
|
||||
_.IsCooperativeMatrixType(input_type)) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, true,
|
||||
HasCoopMatTranspose(_, inst->id()));
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
} else {
|
||||
if (_.GetDimension(result_type) != _.GetDimension(input_type))
|
||||
@@ -304,6 +361,25 @@ spv_result_t ValidateFConvert(ValidationState_t& _, const Instruction* inst,
|
||||
"component type of Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (_.ContainsOCPMicroscalingType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12466) << spvOpcodeString(opcode)
|
||||
<< " must not produce OCP microscaling types in the Vulkan "
|
||||
"environment.";
|
||||
}
|
||||
if (_.ContainsOCPMicroscalingType(input_type) &&
|
||||
!IsIEEEOrAlternativeFloatTypeAllowedForOCPMicroscalingFConvert(
|
||||
_, result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12467) << spvOpcodeString(opcode)
|
||||
<< " consuming an OCP microscaling type in the Vulkan "
|
||||
"environment must produce IEEE 754, Float8E4M3EXT, "
|
||||
"Float8E5M2EXT, or BFloat16KHR.";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -545,10 +621,12 @@ spv_result_t ValidatePtrCastToGeneric(ValidationState_t& _,
|
||||
|
||||
if (input_storage_class != spv::StorageClass::Workgroup &&
|
||||
input_storage_class != spv::StorageClass::CrossWorkgroup &&
|
||||
input_storage_class != spv::StorageClass::Function)
|
||||
input_storage_class != spv::StorageClass::Function &&
|
||||
input_storage_class != spv::StorageClass::CodeSectionINTEL)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected input to have storage class Workgroup, "
|
||||
<< "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
|
||||
<< "CrossWorkgroup, Function or CodeSectionINTEL: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
if (result_data_type != input_data_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
@@ -572,10 +650,12 @@ spv_result_t ValidateGenericCastToPtr(ValidationState_t& _,
|
||||
|
||||
if (result_storage_class != spv::StorageClass::Workgroup &&
|
||||
result_storage_class != spv::StorageClass::CrossWorkgroup &&
|
||||
result_storage_class != spv::StorageClass::Function)
|
||||
result_storage_class != spv::StorageClass::Function &&
|
||||
result_storage_class != spv::StorageClass::CodeSectionINTEL)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Result Type to have storage class Workgroup, "
|
||||
<< "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
|
||||
<< "CrossWorkgroup, Function or CodeSectionINTEL: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
|
||||
spv::StorageClass input_storage_class;
|
||||
@@ -633,10 +713,12 @@ spv_result_t ValidateGenericCastToPtrExplicit(ValidationState_t& _,
|
||||
|
||||
if (target_storage_class != spv::StorageClass::Workgroup &&
|
||||
target_storage_class != spv::StorageClass::CrossWorkgroup &&
|
||||
target_storage_class != spv::StorageClass::Function)
|
||||
target_storage_class != spv::StorageClass::Function &&
|
||||
target_storage_class != spv::StorageClass::CodeSectionINTEL)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected target storage class to be Workgroup, "
|
||||
<< "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
|
||||
<< "CrossWorkgroup, Function or CodeSectionINTEL: "
|
||||
<< spvOpcodeString(opcode);
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -694,8 +776,8 @@ spv_result_t ValidateBitcast(ValidationState_t& _, const Instruction* inst,
|
||||
}
|
||||
|
||||
if (result_is_coopmat) {
|
||||
spv_result_t ret =
|
||||
_.CooperativeMatrixShapesMatch(inst, result_type, input_type, false);
|
||||
spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type,
|
||||
input_type, false, false);
|
||||
if (ret != SPV_SUCCESS) return ret;
|
||||
}
|
||||
|
||||
@@ -782,40 +864,174 @@ spv_result_t ValidateConvertUToAccelerationStructure(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateBitcastExtract(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const spv::Op opcode = inst->opcode();
|
||||
const uint32_t result_type = inst->type_id();
|
||||
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
|
||||
const uint32_t offset_type = _.GetOperandTypeId(inst, 3);
|
||||
|
||||
if (!IsNumericScalarOrVectorType(_, result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12468)
|
||||
<< "Expected Result Type to be a numerical scalar or vector type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
!_.ContainsOCPMicroscalingNonByteType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12468)
|
||||
<< "Expected Result Type to be a Float4EXT or Float6EXT type in "
|
||||
"the Vulkan environment: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (!base_type || !IsNumericScalarOrVectorType(_, base_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12469)
|
||||
<< "Expected Base to be a numerical scalar or vector type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(12469)
|
||||
<< "Expected Base to be an integer scalar or vector type in the "
|
||||
"Vulkan environment: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (_.GetDimension(result_type) != _.GetDimension(base_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Base to have the same dimension as Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (_.GetBitWidth(base_type) <= _.GetBitWidth(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Base component bit width to be greater than Result "
|
||||
"Type component bit width: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
if (!offset_type || !_.IsIntScalarType(offset_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Offset to be a scalar integer type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCooperativeMatrix(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const spv::Op opcode = inst->opcode();
|
||||
const uint32_t result_type = inst->type_id();
|
||||
if (!_.IsCooperativeMatrixType(result_type)) {
|
||||
if (!_.IsCooperativeMatrixKHRType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected cooperative matrix Result Type: "
|
||||
<< "Expected OpTypeCooperativeMatrixKHR Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
const uint32_t input_type = _.GetOperandTypeId(inst, 2);
|
||||
if (!_.IsCooperativeMatrixType(input_type)) {
|
||||
if (!_.IsCooperativeMatrixKHRType(input_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected cooperative matrix type for Matrix input: "
|
||||
<< "Expected OpTypeCooperativeMatrixKHR type for Matrix input: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV);
|
||||
const bool has_transpose_decoration = HasCoopMatTranspose(_, inst->id());
|
||||
const bool swap_row_col = opcode == spv::Op::OpCooperativeMatrixTransposeNV ||
|
||||
has_transpose_decoration;
|
||||
if (auto error = _.CooperativeMatrixShapesMatch(inst, result_type, input_type,
|
||||
true, swap_row_col))
|
||||
return error;
|
||||
|
||||
if (opcode == spv::Op::OpCooperativeMatrixConvertNV) {
|
||||
if (_.FindDef(result_type)->GetOperandAs<uint32_t>(1) !=
|
||||
_.FindDef(input_type)->GetOperandAs<uint32_t>(1)) {
|
||||
if (opcode == spv::Op::OpCooperativeMatrixConvertUseEXT) {
|
||||
auto result_comp_type_id =
|
||||
_.FindDef(result_type)->GetOperandAs<uint32_t>(1);
|
||||
auto input_comp_type_id = _.FindDef(input_type)->GetOperandAs<uint32_t>(1);
|
||||
auto result_comp_type = _.FindDef(result_comp_type_id);
|
||||
auto input_comp_type = _.FindDef(input_comp_type_id);
|
||||
|
||||
const bool same_component_type = result_comp_type_id == input_comp_type_id;
|
||||
const bool signedness_only_difference =
|
||||
result_comp_type->opcode() == spv::Op::OpTypeInt &&
|
||||
input_comp_type->opcode() == spv::Op::OpTypeInt &&
|
||||
result_comp_type->word(2) == input_comp_type->word(2) &&
|
||||
result_comp_type->word(3) != input_comp_type->word(3);
|
||||
if (!same_component_type && !signedness_only_difference) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Result Type and Matrix component types mismatch: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
|
||||
const auto result_use_id =
|
||||
_.FindDef(result_type)->GetOperandAs<uint32_t>(5);
|
||||
const auto input_use_id = _.FindDef(input_type)->GetOperandAs<uint32_t>(5);
|
||||
const auto result_use_eval = _.EvalInt32IfConst(result_use_id);
|
||||
const auto input_use_eval = _.EvalInt32IfConst(input_use_id);
|
||||
const bool result_is_const_int32 = std::get<1>(result_use_eval);
|
||||
const bool input_is_const_int32 = std::get<1>(input_use_eval);
|
||||
const uint32_t result_use = std::get<2>(result_use_eval);
|
||||
const uint32_t input_use = std::get<2>(input_use_eval);
|
||||
|
||||
const auto is_accumulator = [](uint32_t use) {
|
||||
return use == uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR);
|
||||
};
|
||||
const auto is_a_or_b = [](uint32_t use) {
|
||||
return use == uint32_t(spv::CooperativeMatrixUse::MatrixAKHR) ||
|
||||
use == uint32_t(spv::CooperativeMatrixUse::MatrixBKHR);
|
||||
};
|
||||
|
||||
bool invalid_use_change = result_use_id == input_use_id;
|
||||
if (_.HasCapability(spv::Capability::CooperativeMatrixConversionsEXT)) {
|
||||
if (result_is_const_int32 && input_is_const_int32) {
|
||||
invalid_use_change |=
|
||||
!((is_accumulator(input_use) && is_a_or_b(result_use)) ||
|
||||
(is_a_or_b(input_use) && is_accumulator(result_use)));
|
||||
} else {
|
||||
invalid_use_change |=
|
||||
(result_is_const_int32 && !is_accumulator(result_use) &&
|
||||
!is_a_or_b(result_use)) ||
|
||||
(input_is_const_int32 && !is_accumulator(input_use) &&
|
||||
!is_a_or_b(input_use));
|
||||
}
|
||||
} else {
|
||||
invalid_use_change |=
|
||||
(result_is_const_int32 && !is_a_or_b(result_use)) ||
|
||||
(input_is_const_int32 && !is_accumulator(input_use));
|
||||
}
|
||||
|
||||
if (invalid_use_change) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Matrix and Result Type must convert between "
|
||||
"MatrixAccumulatorKHR and MatrixAKHR or MatrixBKHR: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) {
|
||||
if (!_.IsCooperativeMatrixBType(result_type)) {
|
||||
const auto result_use_id =
|
||||
_.FindDef(result_type)->GetOperandAs<uint32_t>(5);
|
||||
const auto input_use_id = _.FindDef(input_type)->GetOperandAs<uint32_t>(5);
|
||||
const auto result_use_eval = _.EvalInt32IfConst(result_use_id);
|
||||
const auto input_use_eval = _.EvalInt32IfConst(input_use_id);
|
||||
const bool result_is_const_int32 = std::get<1>(result_use_eval);
|
||||
const bool input_is_const_int32 = std::get<1>(input_use_eval);
|
||||
const uint32_t result_use = std::get<2>(result_use_eval);
|
||||
const uint32_t input_use = std::get<2>(input_use_eval);
|
||||
if (result_use_id == input_use_id ||
|
||||
(result_is_const_int32 &&
|
||||
result_use != uint32_t(spv::CooperativeMatrixUse::MatrixBKHR)) ||
|
||||
(input_is_const_int32 &&
|
||||
input_use !=
|
||||
uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR))) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Result Type must have UseB: " << spvOpcodeString(opcode);
|
||||
<< "Result Type must have UseB and Matrix must have "
|
||||
"UseAccumulator: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
@@ -920,9 +1136,11 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
return ValidateGenericCastToPtrExplicit(_, inst);
|
||||
case spv::Op::OpBitcast:
|
||||
return ValidateBitcast(_, inst);
|
||||
case spv::Op::OpBitcastExtractEXT:
|
||||
return ValidateBitcastExtract(_, inst);
|
||||
case spv::Op::OpConvertUToAccelerationStructureKHR:
|
||||
return ValidateConvertUToAccelerationStructure(_, inst);
|
||||
case spv::Op::OpCooperativeMatrixConvertNV:
|
||||
case spv::Op::OpCooperativeMatrixConvertUseEXT:
|
||||
case spv::Op::OpCooperativeMatrixTransposeNV:
|
||||
return ValidateCooperativeMatrix(_, inst);
|
||||
case spv::Op::OpBitCastArrayQCOM:
|
||||
|
||||
+33
-1217
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
||||
#include "source/latest_version_glsl_std_450_header.h"
|
||||
#include "source/latest_version_opencl_std_header.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/operand.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/table2.h"
|
||||
#include "source/val/instruction.h"
|
||||
@@ -163,6 +164,38 @@ std::string GetExtInstName(const ValidationState_t& _,
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Rejects an instruction whose result or any operand uses a BFloat16 or FP8
|
||||
// (E4M3/E5M2) type, i.e. an OpTypeFloat that is not IEEE 754 encoded.
|
||||
spv_result_t ValidateExtInstFloatEncoding(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
auto check = [&](uint32_t type_id) -> spv_result_t {
|
||||
if (_.IsBfloat16Type(type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< GetExtInstName(_, inst) << ": doesn't support BFloat16 type.";
|
||||
}
|
||||
if (_.IsFP8Type(type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< GetExtInstName(_, inst)
|
||||
<< ": doesn't support FP8 E4M3/E5M2 types.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
};
|
||||
|
||||
if (spv_result_t result = check(inst->type_id())) return result;
|
||||
|
||||
const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
|
||||
for (uint32_t operand_index = 4; operand_index < num_operands;
|
||||
++operand_index) {
|
||||
// Some ext instructions take literal operands (e.g. OpenCL.std vloadn's
|
||||
// component count); only <id> operands have a meaningful result type.
|
||||
if (!spvIsIdType(inst->operand(operand_index).type)) continue;
|
||||
if (spv_result_t result = check(_.GetOperandTypeId(inst, operand_index)))
|
||||
return result;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns the declared NSDI version from the OpExtInstImport referenced by
|
||||
// |inst|. Returns 0 if not a NonSemantic.Shader.DebugInfo import.
|
||||
uint32_t GetNSDIVersion(const ValidationState_t& _, const Instruction* inst) {
|
||||
@@ -396,10 +429,14 @@ spv_result_t ValidateOperandDebugType(ValidationState_t& _,
|
||||
// Check for common types.
|
||||
std::function<bool(CommonDebugInfoInstructions)> expectation =
|
||||
[&allow_template_param](CommonDebugInfoInstructions dbg_inst) {
|
||||
// TODO - Should DebugTypeTemplateParameterPack be allowed?
|
||||
if (allow_template_param &&
|
||||
(dbg_inst == CommonDebugInfoDebugTypeTemplateParameter ||
|
||||
dbg_inst == CommonDebugInfoDebugTypeTemplateTemplateParameter)) {
|
||||
return true;
|
||||
} else if (dbg_inst == CommonDebugInfoDebugInfoNone) {
|
||||
// DebugInfoNone is a safe "null" type that can be used
|
||||
return true;
|
||||
}
|
||||
return CommonDebugInfoDebugTypeBasic <= dbg_inst &&
|
||||
dbg_inst <= CommonDebugInfoDebugTypeTemplate;
|
||||
@@ -4416,6 +4453,14 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
const spv_ext_inst_type_t ext_inst_type =
|
||||
spv_ext_inst_type_t(inst->ext_inst_type());
|
||||
|
||||
// GLSL.std.450 and OpenCL.std define a floating-point type as an OpTypeFloat
|
||||
// using the IEEE 754 encoding, so they don't support BFloat16 or FP8 types.
|
||||
if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450 ||
|
||||
ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
|
||||
if (spv_result_t result = ValidateExtInstFloatEncoding(_, inst))
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
|
||||
return ValidateExtInstGlslStd450(_, inst);
|
||||
} else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
|
||||
|
||||
+66
-12
@@ -87,8 +87,8 @@ spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) {
|
||||
spv::Op::OpGetKernelLocalSizeForSubgroupCount,
|
||||
spv::Op::OpGetKernelMaxNumSubgroups,
|
||||
spv::Op::OpName,
|
||||
spv::Op::OpCooperativeMatrixPerElementOpNV,
|
||||
spv::Op::OpCooperativeMatrixReduceNV,
|
||||
spv::Op::OpCooperativeMatrixPerElementOpEXT,
|
||||
spv::Op::OpCooperativeMatrixReduceEXT,
|
||||
spv::Op::OpCooperativeMatrixLoadTensorNV,
|
||||
spv::Op::OpConditionalEntryPointINTEL,
|
||||
spv::Op::OpConstantFunctionPointerINTEL};
|
||||
@@ -311,7 +311,7 @@ spv_result_t ValidateCooperativeMatrixPerElementOp(ValidationState_t& _,
|
||||
const auto function = _.FindDef(function_id);
|
||||
if (!function || spv::Op::OpFunction != function->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV Function <id> "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT Function <id> "
|
||||
<< _.getIdName(function_id) << " is not a function.";
|
||||
}
|
||||
|
||||
@@ -320,14 +320,14 @@ spv_result_t ValidateCooperativeMatrixPerElementOp(ValidationState_t& _,
|
||||
const auto matrix_type_id = matrix->type_id();
|
||||
if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV Matrix <id> "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT Matrix <id> "
|
||||
<< _.getIdName(matrix_id) << " is not a cooperative matrix.";
|
||||
}
|
||||
|
||||
const auto result_type_id = inst->GetOperandAs<uint32_t>(0);
|
||||
if (matrix_type_id != result_type_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV Result Type <id> "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT Result Type <id> "
|
||||
<< _.getIdName(result_type_id) << " must match matrix type <id> "
|
||||
<< _.getIdName(matrix_type_id) << ".";
|
||||
}
|
||||
@@ -339,7 +339,7 @@ spv_result_t ValidateCooperativeMatrixPerElementOp(ValidationState_t& _,
|
||||
auto return_type_id = function_type->GetOperandAs<uint32_t>(1);
|
||||
if (return_type_id != matrix_comp_type_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV function return type <id> "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function return type <id> "
|
||||
<< _.getIdName(return_type_id)
|
||||
<< " must match matrix component type <id> "
|
||||
<< _.getIdName(matrix_comp_type_id) << ".";
|
||||
@@ -347,9 +347,9 @@ spv_result_t ValidateCooperativeMatrixPerElementOp(ValidationState_t& _,
|
||||
|
||||
if (function_type->operands().size() < 5) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV function type <id> "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type <id> "
|
||||
<< _.getIdName(function_type_id)
|
||||
<< " must have a least three parameters.";
|
||||
<< " must have at least three parameters.";
|
||||
}
|
||||
|
||||
const auto param0_id = function_type->GetOperandAs<uint32_t>(2);
|
||||
@@ -357,25 +357,79 @@ spv_result_t ValidateCooperativeMatrixPerElementOp(ValidationState_t& _,
|
||||
const auto param2_id = function_type->GetOperandAs<uint32_t>(4);
|
||||
if (!_.IsIntScalarType(param0_id, 32)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV function type first parameter "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type first "
|
||||
"parameter "
|
||||
"type <id> "
|
||||
<< _.getIdName(param0_id) << " must be a 32-bit integer.";
|
||||
}
|
||||
|
||||
if (!_.IsIntScalarType(param1_id, 32)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV function type second "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type second "
|
||||
"parameter type <id> "
|
||||
<< _.getIdName(param1_id) << " must be a 32-bit integer.";
|
||||
}
|
||||
|
||||
if (param2_id != matrix_comp_type_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpNV function type third parameter "
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type third "
|
||||
"parameter "
|
||||
"type <id> "
|
||||
<< _.getIdName(param2_id) << " must match matrix component type.";
|
||||
}
|
||||
|
||||
const auto optional_operand_count = inst->operands().size() - 4;
|
||||
const auto expected_function_type_operands = 5 + optional_operand_count;
|
||||
if (function_type->operands().size() != expected_function_type_operands) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type <id> "
|
||||
<< _.getIdName(function_type_id) << " must have "
|
||||
<< (3 + optional_operand_count)
|
||||
<< " parameters to match the instruction operands.";
|
||||
}
|
||||
|
||||
for (uint32_t operand_index = 4; operand_index < inst->operands().size();
|
||||
++operand_index) {
|
||||
const auto optional_operand_id =
|
||||
inst->GetOperandAs<uint32_t>(operand_index);
|
||||
const auto optional_operand = _.FindDef(optional_operand_id);
|
||||
const auto optional_operand_type_id =
|
||||
optional_operand ? optional_operand->type_id() : 0;
|
||||
const auto param_id =
|
||||
function_type->GetOperandAs<uint32_t>(operand_index + 1);
|
||||
|
||||
if (!_.IsCooperativeMatrixType(optional_operand_type_id)) {
|
||||
if (param_id != optional_operand_type_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type optional "
|
||||
"parameter type <id> "
|
||||
<< _.getIdName(param_id)
|
||||
<< " must match optional operand type <id> "
|
||||
<< _.getIdName(optional_operand_type_id) << ".";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (optional_operand_type_id != matrix_type_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpEXT optional matrix operand "
|
||||
"type <id> "
|
||||
<< _.getIdName(optional_operand_type_id)
|
||||
<< " must match matrix type <id> " << _.getIdName(matrix_type_id)
|
||||
<< ".";
|
||||
}
|
||||
|
||||
const auto optional_matrix_comp_type_id =
|
||||
_.FindDef(optional_operand_type_id)->GetOperandAs<uint32_t>(1);
|
||||
if (param_id != optional_matrix_comp_type_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpCooperativeMatrixPerElementOpEXT function type optional "
|
||||
"parameter type <id> "
|
||||
<< _.getIdName(param_id)
|
||||
<< " must match optional matrix component type.";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -392,7 +446,7 @@ spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
case spv::Op::OpFunctionCall:
|
||||
if (auto error = ValidateFunctionCall(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpCooperativeMatrixPerElementOpNV:
|
||||
case spv::Op::OpCooperativeMatrixPerElementOpEXT:
|
||||
if (auto error = ValidateCooperativeMatrixPerElementOp(_, inst))
|
||||
return error;
|
||||
break;
|
||||
|
||||
+4
-3
@@ -131,14 +131,15 @@ bool InstructionCanHaveTypeOperand(const Instruction* inst) {
|
||||
const auto opcode = inst->opcode();
|
||||
bool type_instruction = spvOpcodeGeneratesType(opcode);
|
||||
bool debug_instruction = spvOpcodeIsDebug(opcode) || inst->IsDebugInfo();
|
||||
bool coop_matrix_spec_constant_op_length =
|
||||
bool spec_constant_op_with_type_operand =
|
||||
(opcode == spv::Op::OpSpecConstantOp) &&
|
||||
(spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthNV ||
|
||||
spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthKHR);
|
||||
spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthKHR ||
|
||||
spvOpcodeGeneratesUntypedPointer(spv::Op(inst->word(3))));
|
||||
return type_instruction || debug_instruction || inst->IsNonSemantic() ||
|
||||
spvOpcodeIsDecoration(opcode) || instruction_allow_set.count(opcode) ||
|
||||
spvOpcodeGeneratesUntypedPointer(opcode) ||
|
||||
coop_matrix_spec_constant_op_length;
|
||||
spec_constant_op_with_type_operand;
|
||||
}
|
||||
|
||||
bool InstructionRequiresTypeOperand(const Instruction* inst) {
|
||||
|
||||
+55
-6
@@ -637,7 +637,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
||||
if (opcode != spv::Op::OpImageGather &&
|
||||
opcode != spv::Op::OpImageDrefGather &&
|
||||
opcode != spv::Op::OpImageSparseGather &&
|
||||
opcode != spv::Op::OpImageSparseDrefGather) {
|
||||
opcode != spv::Op::OpImageSparseDrefGather &&
|
||||
opcode != spv::Op::OpImageGatherQCOM) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(10213)
|
||||
<< "Image Operand Offset can only be used with "
|
||||
@@ -652,7 +653,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
||||
if (opcode != spv::Op::OpImageGather &&
|
||||
opcode != spv::Op::OpImageDrefGather &&
|
||||
opcode != spv::Op::OpImageSparseGather &&
|
||||
opcode != spv::Op::OpImageSparseDrefGather) {
|
||||
opcode != spv::Op::OpImageSparseDrefGather &&
|
||||
opcode != spv::Op::OpImageGatherQCOM) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Image Operand ConstOffsets can only be used with "
|
||||
"OpImageGather and OpImageDrefGather";
|
||||
@@ -908,7 +910,9 @@ bool IsSparse(spv::Op opcode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default: { return false; }
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1192,6 +1196,7 @@ bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) {
|
||||
case spv::Op::OpImageBlockMatchWindowSSDQCOM:
|
||||
case spv::Op::OpImageBlockMatchGatherSADQCOM:
|
||||
case spv::Op::OpImageBlockMatchGatherSSDQCOM:
|
||||
case spv::Op::OpImageGatherQCOM:
|
||||
case spv::Op::OpImageSampleFootprintNV:
|
||||
return true;
|
||||
case spv::Op::OpStore:
|
||||
@@ -1218,6 +1223,7 @@ spv_result_t ValidateImageCoordinate(ValidationState_t& _,
|
||||
opcode == spv::Op::OpImageSampleProjDrefImplicitLod ||
|
||||
opcode == spv::Op::OpImageSampleProjDrefExplicitLod ||
|
||||
opcode == spv::Op::OpImageGather ||
|
||||
opcode == spv::Op::OpImageGatherQCOM ||
|
||||
opcode == spv::Op::OpImageDrefGather ||
|
||||
opcode == spv::Op::OpImageQueryLod ||
|
||||
opcode == spv::Op::OpImageSparseSampleImplicitLod ||
|
||||
@@ -1846,8 +1852,10 @@ spv_result_t ValidateImageGather(ValidationState_t& _,
|
||||
ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
|
||||
return result;
|
||||
|
||||
unsigned n_additional_operands = 0;
|
||||
if (opcode == spv::Op::OpImageGather ||
|
||||
opcode == spv::Op::OpImageSparseGather) {
|
||||
opcode == spv::Op::OpImageSparseGather ||
|
||||
opcode == spv::Op::OpImageGatherQCOM) {
|
||||
const uint32_t component = inst->GetOperandAs<uint32_t>(4);
|
||||
const uint32_t component_index_type = _.GetTypeId(component);
|
||||
if (!_.IsIntScalarType(component_index_type, 32)) {
|
||||
@@ -1862,14 +1870,53 @@ spv_result_t ValidateImageGather(ValidationState_t& _,
|
||||
"environment";
|
||||
}
|
||||
}
|
||||
if (opcode == spv::Op::OpImageGatherQCOM) {
|
||||
const uint32_t mode = inst->GetOperandAs<uint32_t>(5);
|
||||
const uint32_t mode_index_type = _.GetTypeId(mode);
|
||||
if (!_.IsIntScalarType(mode_index_type) ||
|
||||
_.GetBitWidth(mode_index_type) != 32) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Mode to be 32-bit int scalar";
|
||||
}
|
||||
const spv::Op modeOpc = _.GetIdOpcode(mode);
|
||||
if (spvOpcodeIsConstant(modeOpc) && !spvOpcodeIsSpecConstant(modeOpc)) {
|
||||
uint64_t m_val = -1;
|
||||
if (_.EvalConstantValUint64(mode, &m_val)) {
|
||||
if (m_val == 0) {
|
||||
if (!_.HasCapability(spv::Capability::ImageGatherLinearQCOM)) {
|
||||
return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
|
||||
<< "Mode GatherModesGather4x1QCOM (== 0) requires "
|
||||
"capability ImageGatherLinearQCOM.";
|
||||
}
|
||||
} else if (m_val < 4) {
|
||||
if (!_.HasCapability(
|
||||
spv::Capability::ImageGatherExtendedModesQCOM)) {
|
||||
return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
|
||||
<< "Mode GatherModesGatherDQCOM (== "
|
||||
"1)/GatherModesGatherH2QCOM (== 2"
|
||||
")/GatherModesGatherV2QCOM (== 3)"
|
||||
" requires capability ImageGatherExtendedModesQCOM.";
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "GatherModesGather4x1QCOM (== 0)/"
|
||||
"GatherModesGatherDQCOM (== 1)/"
|
||||
"GatherModesGatherH2QCOM (== 2)/"
|
||||
"GatherModesGatherV2QCOM (== 3) "
|
||||
"are the only supported modes.";
|
||||
}
|
||||
}
|
||||
}
|
||||
n_additional_operands += 1;
|
||||
}
|
||||
} else {
|
||||
assert(opcode == spv::Op::OpImageDrefGather ||
|
||||
opcode == spv::Op::OpImageSparseDrefGather);
|
||||
if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
|
||||
}
|
||||
|
||||
if (spv_result_t result =
|
||||
ValidateImageOperands(_, inst, info, /* word_index = */ 7))
|
||||
if (spv_result_t result = ValidateImageOperands(
|
||||
_, inst, info, /* word_index = */ (7 + n_additional_operands)))
|
||||
return result;
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@@ -2682,6 +2729,7 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
|
||||
case spv::Op::OpImageDrefGather:
|
||||
case spv::Op::OpImageSparseGather:
|
||||
case spv::Op::OpImageSparseDrefGather:
|
||||
case spv::Op::OpImageGatherQCOM:
|
||||
return ValidateImageGather(_, inst);
|
||||
|
||||
case spv::Op::OpImageRead:
|
||||
@@ -2787,6 +2835,7 @@ bool IsImageInstruction(const spv::Op opcode) {
|
||||
case spv::Op::OpImageBlockMatchWindowSSDQCOM:
|
||||
case spv::Op::OpImageBlockMatchGatherSADQCOM:
|
||||
case spv::Op::OpImageBlockMatchGatherSSDQCOM:
|
||||
case spv::Op::OpImageGatherQCOM:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -516,6 +516,16 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32";
|
||||
}
|
||||
_.set_samplerimage_variable_address_mode(bitwidth);
|
||||
} else if (opcode == spv::Op::OpSourceContinued) {
|
||||
const auto& instructions = _.ordered_instructions();
|
||||
size_t idx = inst - &instructions.front();
|
||||
const Instruction* prev_inst = &instructions[idx - 1];
|
||||
if (prev_inst->opcode() != spv::Op::OpSource &&
|
||||
prev_inst->opcode() != spv::Op::OpSourceContinued) {
|
||||
return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
|
||||
<< "OpSourceContinued must be preceded by OpSource or "
|
||||
"OpSourceContinued.";
|
||||
}
|
||||
}
|
||||
|
||||
if (auto error = ReservedCheck(_, inst)) return error;
|
||||
|
||||
+9
-2
@@ -740,7 +740,13 @@ spv_result_t ValidateStorageClass(ValidationState_t& _,
|
||||
if (inst->words().size() > 3) {
|
||||
auto encoding = inst->GetOperandAs<spv::FPEncoding>(2);
|
||||
if ((encoding == spv::FPEncoding::Float8E4M3EXT) ||
|
||||
(encoding == spv::FPEncoding::Float8E5M2EXT)) {
|
||||
(encoding == spv::FPEncoding::Float8E5M2EXT) ||
|
||||
(encoding == spv::FPEncoding::Float6E2M3EXT) ||
|
||||
(encoding == spv::FPEncoding::Float6E3M2EXT) ||
|
||||
(encoding == spv::FPEncoding::Float4E2M1EXT) ||
|
||||
(encoding ==
|
||||
spv::FPEncoding::Float8UnsignedE8M0EXT) ||
|
||||
(encoding == spv::FPEncoding::MXInt8EXT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -748,7 +754,8 @@ spv_result_t ValidateStorageClass(ValidationState_t& _,
|
||||
return false;
|
||||
})) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, interface_var)
|
||||
<< _.VkErrorID(10823) << "FP8 E4M3/E5M2 OpVariable <id> "
|
||||
<< _.VkErrorID(10823)
|
||||
<< "FP8 or OCP microscaling OpVariable <id> "
|
||||
<< _.getIdName(interface_var->id()) << " must not be declared "
|
||||
<< "with a Storage Class of Input or Output.";
|
||||
}
|
||||
|
||||
+30
-2
@@ -56,6 +56,11 @@ bool IsFP8ScalarOrCompositeType(ValidationState_t& _, uint32_t type_id) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOCPMicroscalingScalarOrCompositeType(ValidationState_t& _,
|
||||
uint32_t type_id) {
|
||||
return _.ContainsOCPMicroscalingType(type_id);
|
||||
}
|
||||
|
||||
spv_result_t CheckInvalidScalarOrCompositeType(ValidationState_t& _,
|
||||
const Instruction* inst,
|
||||
uint32_t type_id) {
|
||||
@@ -69,6 +74,11 @@ spv_result_t CheckInvalidScalarOrCompositeType(ValidationState_t& _,
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support FP8 E4M3/E5M2 types.";
|
||||
}
|
||||
if (IsOCPMicroscalingScalarOrCompositeType(_, type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support OCP microscaling types.";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@@ -80,8 +90,6 @@ spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
const spv::Op opcode = inst->opcode();
|
||||
|
||||
switch (opcode) {
|
||||
// OpExtInst
|
||||
case spv::Op::OpExtInst:
|
||||
// Arithmetic Instructions
|
||||
case spv::Op::OpFAdd:
|
||||
case spv::Op::OpFSub:
|
||||
@@ -156,6 +164,11 @@ spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support FP8 E4M3/E5M2 types.";
|
||||
}
|
||||
if (_.ContainsOCPMicroscalingType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support OCP microscaling types.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -171,6 +184,11 @@ spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support FP8 E4M3/E5M2 types.";
|
||||
}
|
||||
if (_.ContainsOCPMicroscalingType(data_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support OCP microscaling types.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Relational and Logical Instructions
|
||||
@@ -204,6 +222,11 @@ spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support FP8 E4M3/E5M2 types.";
|
||||
}
|
||||
if (_.ContainsOCPMicroscalingType(operand_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support OCP microscaling types.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -218,6 +241,11 @@ spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support FP8 E4M3/E5M2 types.";
|
||||
}
|
||||
if (_.ContainsOCPMicroscalingType(value_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< " doesn't support OCP microscaling types.";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
+78
-11
@@ -247,6 +247,8 @@ int MemoryAccessNumWords(uint32_t mask) {
|
||||
if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++result;
|
||||
if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) ++result;
|
||||
if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) ++result;
|
||||
if (mask & uint32_t(spv::MemoryAccessMask::AliasScopeINTELMask)) ++result;
|
||||
if (mask & uint32_t(spv::MemoryAccessMask::NoAliasINTELMask)) ++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -684,6 +686,7 @@ spv_result_t ValidateVariablePointer(ValidationState_t& _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1074,6 +1077,25 @@ spv_result_t ValidateVariableShader(ValidationState_t& _,
|
||||
<< sc_name << " storage class requires an additional capability";
|
||||
}
|
||||
}
|
||||
|
||||
if (_.ContainsOCPMicroscalingNonByteType(value_id)) {
|
||||
auto underlying_type = value_type;
|
||||
auto sc = storage_class;
|
||||
while (underlying_type &&
|
||||
underlying_type->opcode() == spv::Op::OpTypePointer) {
|
||||
sc = underlying_type->GetOperandAs<spv::StorageClass>(1u);
|
||||
underlying_type = _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
|
||||
}
|
||||
if (sc != spv::StorageClass::Function && sc != spv::StorageClass::Private) {
|
||||
std::string sc_name = _.grammar().lookupOperandName(
|
||||
SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(sc));
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Cannot allocate a variable containing a Float4EXT or "
|
||||
"Float6EXT type in "
|
||||
<< sc_name << " storage class";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -2413,7 +2435,7 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Pointer must be an untyped pointer object";
|
||||
}
|
||||
} else if (pointer_ty->opcode() != spv::Op::OpTypePointer) {
|
||||
} else if (!pointer_ty || pointer_ty->opcode() != spv::Op::OpTypePointer) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Structure's type in Op" << spvOpcodeString(opcode)
|
||||
<< " <id> " << state.getIdName(inst->id())
|
||||
@@ -2498,6 +2520,41 @@ spv_result_t ValidateCooperativeMatrixLength(ValidationState_t& state,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCooperativeMatrixGetCoordinateEXT(
|
||||
ValidationState_t& state, const Instruction* inst) {
|
||||
std::string instr_name = "OpCooperativeMatrixGetCoordinateEXT";
|
||||
|
||||
// Result type must be a uvec2
|
||||
if (!state.IsIntVectorType(inst->type_id(), 32, 2)) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< instr_name << " Result Type <id> "
|
||||
<< state.getIdName(inst->type_id())
|
||||
<< " must be OpTypeVector with two 32-bit integer components.";
|
||||
}
|
||||
|
||||
// Matrix operand must be a cooperative matrix
|
||||
auto matrix_type_id =
|
||||
state.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id();
|
||||
if (!state.IsCooperativeMatrixKHRType(matrix_type_id)) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< instr_name << " Matrix <id> "
|
||||
<< state.getIdName(inst->GetOperandAs<uint32_t>(2))
|
||||
<< " must be OpTypeCooperativeMatrixKHR.";
|
||||
}
|
||||
|
||||
// Index operand must be a 32-bit int.
|
||||
auto index_type_id =
|
||||
state.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id();
|
||||
if (!state.IsIntScalarType(index_type_id, 32)) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< instr_name << " Index <id> "
|
||||
<< state.getIdName(inst->GetOperandAs<uint32_t>(3))
|
||||
<< " must be OpTypeInt with width 32.";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
uint32_t type_id;
|
||||
@@ -2802,21 +2859,29 @@ spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _,
|
||||
|
||||
spv_result_t ValidateBufferPointerEXT(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto storage_class_ptr = _.FindDef(inst->GetOperandAs<uint32_t>(0));
|
||||
const auto storage_class_ptr = _.FindDef(inst->type_id());
|
||||
if (storage_class_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR &&
|
||||
storage_class_ptr->opcode() != spv::Op::OpTypePointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpBufferPointerEXT's Result Type should be "
|
||||
<< "a pointer type.";
|
||||
} else {
|
||||
// Buffer operand
|
||||
auto buffer =
|
||||
_.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
|
||||
if (!buffer || !_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpBufferPointerEXT's buffer must be an untyped pointer"
|
||||
<< " into a variable declared with the ResourceHeapEXT built-in";
|
||||
}
|
||||
}
|
||||
|
||||
auto sc = storage_class_ptr->GetOperandAs<spv::StorageClass>(1u);
|
||||
if (sc != spv::StorageClass::StorageBuffer &&
|
||||
sc != spv::StorageClass::Uniform) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpBufferPointerEXT Result Type must be a pointer type "
|
||||
<< "with a Storage Class of Uniform or StorageBuffer.";
|
||||
}
|
||||
|
||||
// Buffer operand
|
||||
auto buffer =
|
||||
_.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
|
||||
if (!buffer || !_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpBufferPointerEXT's buffer must be an untyped pointer"
|
||||
<< " into a variable declared with the ResourceHeapEXT built-in";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@@ -3802,6 +3867,8 @@ spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
||||
return ValidateCooperativeMatrixLength(_, inst, true);
|
||||
case spv::Op::OpCooperativeMatrixLengthNV:
|
||||
return ValidateCooperativeMatrixLength(_, inst, false);
|
||||
case spv::Op::OpCooperativeMatrixGetCoordinateEXT:
|
||||
return ValidateCooperativeMatrixGetCoordinateEXT(_, inst);
|
||||
case spv::Op::OpCooperativeMatrixLoadKHR:
|
||||
case spv::Op::OpCooperativeMatrixStoreKHR:
|
||||
return ValidateCooperativeMatrixLoadStoreKHR(_, inst);
|
||||
|
||||
@@ -194,7 +194,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
|
||||
}
|
||||
if (!spvOpcodeIsAtomicOp(inst->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(10874) << spvOpcodeString(opcode)
|
||||
<< _.VkErrorID(13551) << spvOpcodeString(opcode)
|
||||
<< ": Memory Semantics with Volatile bit set must not be used "
|
||||
"with barrier instructions";
|
||||
}
|
||||
@@ -286,6 +286,36 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
|
||||
}
|
||||
}
|
||||
|
||||
if (is_vulkan && (opcode == spv::Op::OpControlBarrierArriveEXT ||
|
||||
opcode == spv::Op::OpControlBarrierWaitEXT)) {
|
||||
auto aquire_release_mask = uint32_t(
|
||||
spv::MemorySemanticsMask::Acquire | spv::MemorySemanticsMask::Release |
|
||||
spv::MemorySemanticsMask::AcquireRelease);
|
||||
|
||||
if (opcode == spv::Op::OpControlBarrierArriveEXT) {
|
||||
if ((value & aquire_release_mask) !=
|
||||
uint32_t(spv::MemorySemanticsMask::Release) &&
|
||||
((value & aquire_release_mask) !=
|
||||
uint32_t(spv::MemorySemanticsMask::MaskNone))) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(13556) << spvOpcodeString(opcode)
|
||||
<< " Memory Semantics must not have any non-relaxed memory "
|
||||
"order set other than Release";
|
||||
}
|
||||
}
|
||||
if (opcode == spv::Op::OpControlBarrierWaitEXT) {
|
||||
if ((value & aquire_release_mask) !=
|
||||
uint32_t(spv::MemorySemanticsMask::Acquire) &&
|
||||
((value & aquire_release_mask) !=
|
||||
uint32_t(spv::MemorySemanticsMask::MaskNone))) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(13557) << spvOpcodeString(opcode)
|
||||
<< " Memory Semantics must not have any non-relaxed memory "
|
||||
"order set other than Acquire";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
|
||||
|
||||
return SPV_SUCCESS;
|
||||
|
||||
+19
-2
@@ -139,6 +139,15 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
||||
});
|
||||
}
|
||||
|
||||
if (opcode == spv::Op::OpControlBarrierArriveEXT ||
|
||||
opcode == spv::Op::OpControlBarrierWaitEXT) {
|
||||
if (value != spv::Scope::Workgroup && value != spv::Scope::Subgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(13553)
|
||||
<< "The execution Scope for OpControlBarrierArriveEXT and "
|
||||
"OpControlBarrierWaitEXT must be Workgroup or Subgroup";
|
||||
}
|
||||
}
|
||||
// Only subset of execution models support Workgroup.
|
||||
if (value == spv::Scope::Workgroup) {
|
||||
std::string errorVUID = _.VkErrorID(4637);
|
||||
@@ -177,11 +186,19 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
||||
// TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
|
||||
|
||||
// General SPIRV rules
|
||||
// Scope for execution must be limited to Workgroup or Subgroup for
|
||||
// non-uniform operations
|
||||
// OpGroupNonUniformRotateKHR is the only non-uniform group operation that
|
||||
// allows Workgroup scope; all others are limited to Subgroup.
|
||||
if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
|
||||
opcode != spv::Op::OpGroupNonUniformQuadAllKHR &&
|
||||
opcode != spv::Op::OpGroupNonUniformQuadAnyKHR &&
|
||||
opcode != spv::Op::OpGroupNonUniformRotateKHR &&
|
||||
value != spv::Scope::Subgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode) << ": Execution scope is limited to "
|
||||
<< "Subgroup";
|
||||
}
|
||||
|
||||
if (opcode == spv::Op::OpGroupNonUniformRotateKHR &&
|
||||
value != spv::Scope::Subgroup && value != spv::Scope::Workgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
|
||||
+36
-2
@@ -148,6 +148,37 @@ spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (num_bits == 4) {
|
||||
if (!encoding.has_value()) {
|
||||
// we don't support fp4 without encoding
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "4-bit floating point type requires an encoding.";
|
||||
}
|
||||
if (encoding.value() != spv::FPEncoding::Float4E2M1EXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Unsupported 4-bit floating point encoding ("
|
||||
<< static_cast<uint32_t>(encoding.value()) << ").";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
if (num_bits == 6) {
|
||||
if (!encoding.has_value()) {
|
||||
// we don't support fp6 without encoding
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "6-bit floating point type requires an encoding.";
|
||||
}
|
||||
const auto enc = encoding.value();
|
||||
if (enc != spv::FPEncoding::Float6E2M3EXT &&
|
||||
enc != spv::FPEncoding::Float6E3M2EXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Unsupported 6-bit floating point encoding ("
|
||||
<< static_cast<uint32_t>(enc) << ").";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (num_bits == 16) {
|
||||
// An absence of FP encoding implies IEEE 754. The Float16 and Float16Buffer
|
||||
// capabilities only enable IEEE 754 binary 16
|
||||
@@ -170,12 +201,15 @@ spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!_.features().declare_float8_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using a 8-bit floating point "
|
||||
<< "type requires the Float8EXT capability.";
|
||||
<< "type requires one of the following capabilities: Float8EXT, "
|
||||
"Float8UnsignedE8M0EXT, MXInt8EXT.";
|
||||
}
|
||||
if (encoding.has_value()) {
|
||||
const auto enc = encoding.value();
|
||||
if (enc != spv::FPEncoding::Float8E4M3EXT &&
|
||||
enc != spv::FPEncoding::Float8E5M2EXT) {
|
||||
enc != spv::FPEncoding::Float8E5M2EXT &&
|
||||
enc != spv::FPEncoding::Float8UnsignedE8M0EXT &&
|
||||
enc != spv::FPEncoding::MXInt8EXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Unsupported 8-bit floating point encoding ("
|
||||
<< static_cast<uint32_t>(enc) << ").";
|
||||
|
||||
+182
-16
@@ -441,6 +441,8 @@ void ValidationState_t::RegisterCapability(spv::Capability cap) {
|
||||
features_.declare_float16_type = true;
|
||||
break;
|
||||
case spv::Capability::Float8EXT:
|
||||
case spv::Capability::Float8UnsignedE8M0EXT:
|
||||
case spv::Capability::MXInt8EXT:
|
||||
features_.declare_float8_type = true;
|
||||
break;
|
||||
case spv::Capability::StorageUniformBufferBlock16:
|
||||
@@ -1176,6 +1178,48 @@ bool ValidationState_t::IsFP8Type(uint32_t id) const {
|
||||
return IsFP8ScalarType(id) || IsFP8VectorType(id) || IsFP8CoopMatType(id);
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsOCPMicroscalingScalarType(uint32_t id) const {
|
||||
const Instruction* inst = FindDef(id);
|
||||
if (inst && inst->opcode() == spv::Op::OpTypeFloat &&
|
||||
inst->words().size() > 3) {
|
||||
const auto encoding = inst->GetOperandAs<spv::FPEncoding>(2);
|
||||
return encoding == spv::FPEncoding::Float6E2M3EXT ||
|
||||
encoding == spv::FPEncoding::Float6E3M2EXT ||
|
||||
encoding == spv::FPEncoding::Float4E2M1EXT ||
|
||||
encoding == spv::FPEncoding::Float8UnsignedE8M0EXT ||
|
||||
encoding == spv::FPEncoding::MXInt8EXT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsOCPMicroscalingNonByteScalarType(uint32_t id) const {
|
||||
const Instruction* inst = FindDef(id);
|
||||
if (inst && inst->opcode() == spv::Op::OpTypeFloat &&
|
||||
inst->words().size() > 3) {
|
||||
const auto encoding = inst->GetOperandAs<spv::FPEncoding>(2);
|
||||
return encoding == spv::FPEncoding::Float6E2M3EXT ||
|
||||
encoding == spv::FPEncoding::Float6E3M2EXT ||
|
||||
encoding == spv::FPEncoding::Float4E2M1EXT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsOCPMicroscalingType(uint32_t id) const {
|
||||
return ContainsOCPMicroscalingType(id);
|
||||
}
|
||||
|
||||
bool ValidationState_t::ContainsOCPMicroscalingType(uint32_t id) const {
|
||||
return ContainsType(id, [this](const Instruction* inst) {
|
||||
return IsOCPMicroscalingScalarType(inst->id());
|
||||
});
|
||||
}
|
||||
|
||||
bool ValidationState_t::ContainsOCPMicroscalingNonByteType(uint32_t id) const {
|
||||
return ContainsType(id, [this](const Instruction* inst) {
|
||||
return IsOCPMicroscalingNonByteScalarType(inst->id());
|
||||
});
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsFloatScalarType(uint32_t id, uint32_t width) const {
|
||||
const Instruction* inst = FindDef(id);
|
||||
bool is_float = inst && inst->opcode() == spv::Op::OpTypeFloat;
|
||||
@@ -1269,6 +1313,20 @@ bool ValidationState_t::IsIntVectorType(uint32_t id) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsIntVectorType(uint32_t id, uint32_t width,
|
||||
uint32_t components) const {
|
||||
if (!IsIntVectorType(id)) {
|
||||
return false;
|
||||
}
|
||||
if (GetBitWidth(id) != width) {
|
||||
return false;
|
||||
}
|
||||
if (GetDimension(id) != components) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const {
|
||||
const Instruction* inst = FindDef(id);
|
||||
if (!inst) {
|
||||
@@ -1743,15 +1801,45 @@ spv_result_t ValidationState_t::CooperativeMatrixShapesMatch(
|
||||
std::tie(m2_is_int32, m2_is_const_int32, m2_value) =
|
||||
EvalInt32IfConst(m2_use_id);
|
||||
|
||||
if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value &&
|
||||
// CooperativeMatrixConversionsNV allows conversions from Acc->A/B
|
||||
!(is_conversion &&
|
||||
HasCapability(spv::Capability::CooperativeMatrixConversionsNV) &&
|
||||
m2_value ==
|
||||
(uint32_t)spv::CooperativeMatrixUse::MatrixAccumulatorKHR)) {
|
||||
const auto is_accumulator = [](uint32_t use) {
|
||||
return use == uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR);
|
||||
};
|
||||
const auto is_a_or_b = [](uint32_t use) {
|
||||
return use == uint32_t(spv::CooperativeMatrixUse::MatrixAKHR) ||
|
||||
use == uint32_t(spv::CooperativeMatrixUse::MatrixBKHR);
|
||||
};
|
||||
|
||||
if (swap_row_col &&
|
||||
HasCapability(spv::Capability::CooperativeMatrixConversionsEXT) &&
|
||||
(m1_use_id == m2_use_id ||
|
||||
(m1_is_const_int32 && !is_a_or_b(m1_value)) ||
|
||||
(m2_is_const_int32 && !is_accumulator(m2_value)))) {
|
||||
return diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Use of Matrix type and Result Type to be "
|
||||
<< "identical";
|
||||
<< "A conversion decorated with CooperativeMatrixTransposeEXT "
|
||||
"must have a MatrixAccumulatorKHR Matrix and a MatrixAKHR or "
|
||||
"MatrixBKHR Result Type: "
|
||||
<< spvOpcodeString(inst->opcode());
|
||||
}
|
||||
|
||||
if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) {
|
||||
bool allow_conv = false;
|
||||
// Conversion from Acc->A,B allowed for NV or EXT
|
||||
if ((HasCapability(spv::Capability::CooperativeMatrixConversionsNV) ||
|
||||
HasCapability(spv::Capability::CooperativeMatrixConversionsEXT)) &&
|
||||
is_accumulator(m2_value) && is_a_or_b(m1_value)) {
|
||||
allow_conv = true;
|
||||
}
|
||||
// Conversion from A,B->Acc allowed for EXT
|
||||
if (HasCapability(spv::Capability::CooperativeMatrixConversionsEXT) &&
|
||||
is_accumulator(m1_value) && is_a_or_b(m2_value)) {
|
||||
allow_conv = true;
|
||||
}
|
||||
|
||||
if (!(is_conversion && allow_conv)) {
|
||||
return diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Use of Matrix type and Result Type to be "
|
||||
<< "identical";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1805,16 +1893,39 @@ bool ValidationState_t::EvalConstantValUint64(uint32_t id,
|
||||
|
||||
if (inst->opcode() == spv::Op::OpConstantNull) {
|
||||
*val = 0;
|
||||
} else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) {
|
||||
auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u));
|
||||
*val = 0;
|
||||
switch (type_op) {
|
||||
case spv::Op::OpTypeBufferEXT:
|
||||
case spv::Op::OpTypeAccelerationStructureKHR:
|
||||
*val = options()->buffer_descriptor_layout.size;
|
||||
break;
|
||||
case spv::Op::OpTypeSampler:
|
||||
*val = options()->sampler_descriptor_layout.size;
|
||||
break;
|
||||
case spv::Op::OpTypeImage:
|
||||
*val = options()->image_descriptor_layout.size;
|
||||
break;
|
||||
case spv::Op::OpTypeTensorARM:
|
||||
*val = options()->tensor_descriptor_layout.size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *val > 0;
|
||||
} else if (inst->opcode() != spv::Op::OpConstant) {
|
||||
// Spec constant values cannot be evaluated so don't consider constant for
|
||||
// static validation
|
||||
return false;
|
||||
} else if (inst->words().size() == 4) {
|
||||
*val = inst->word(3);
|
||||
} else {
|
||||
assert(inst->words().size() == 5);
|
||||
} else if (inst->words().size() == 5) {
|
||||
*val = inst->word(3);
|
||||
*val |= uint64_t(inst->word(4)) << 32;
|
||||
} else {
|
||||
// Literal value wider than 64 bits does not fit in a uint64_t.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1830,17 +1941,40 @@ bool ValidationState_t::EvalConstantValInt64(uint32_t id, int64_t* val) const {
|
||||
|
||||
if (inst->opcode() == spv::Op::OpConstantNull) {
|
||||
*val = 0;
|
||||
} else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) {
|
||||
auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u));
|
||||
*val = 0;
|
||||
switch (type_op) {
|
||||
case spv::Op::OpTypeBufferEXT:
|
||||
case spv::Op::OpTypeAccelerationStructureKHR:
|
||||
*val = static_cast<int64_t>(options()->buffer_descriptor_layout.size);
|
||||
break;
|
||||
case spv::Op::OpTypeSampler:
|
||||
*val = static_cast<int64_t>(options()->sampler_descriptor_layout.size);
|
||||
break;
|
||||
case spv::Op::OpTypeImage:
|
||||
*val = static_cast<int64_t>(options()->image_descriptor_layout.size);
|
||||
break;
|
||||
case spv::Op::OpTypeTensorARM:
|
||||
*val = static_cast<int64_t>(options()->tensor_descriptor_layout.size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *val > 0;
|
||||
} else if (inst->opcode() != spv::Op::OpConstant) {
|
||||
// Spec constant values cannot be evaluated so don't consider constant for
|
||||
// static validation
|
||||
return false;
|
||||
} else if (inst->words().size() == 4) {
|
||||
*val = int32_t(inst->word(3));
|
||||
} else {
|
||||
assert(inst->words().size() == 5);
|
||||
} else if (inst->words().size() == 5) {
|
||||
const uint32_t lo_word = inst->word(3);
|
||||
const uint32_t hi_word = inst->word(4);
|
||||
*val = static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32);
|
||||
} else {
|
||||
// Literal value wider than 64 bits does not fit in an int64_t.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -3154,6 +3288,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710);
|
||||
case 4711:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711);
|
||||
case 4716:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Offset-04716);
|
||||
case 4734:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
|
||||
case 4744:
|
||||
@@ -3422,8 +3558,6 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10872);
|
||||
case 10873:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10873);
|
||||
case 10874:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10874);
|
||||
case 10875:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10875);
|
||||
case 10876:
|
||||
@@ -3451,14 +3585,24 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Result-11337);
|
||||
case 11339:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Result-11339);
|
||||
case 11346:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Result-11346);
|
||||
case 11347:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11347);
|
||||
case 11416:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedImageTexelPointerEXT-11416);
|
||||
case 11417:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeUntypedPointerKHR-11417);
|
||||
// These RuntimeSpirv are here because now we can pass in the size/alignment
|
||||
// which in turns mean we can validate things based on runtime properties
|
||||
case 11476:
|
||||
return VUID_WRAP(VUID-RuntimeSpirv-samplerDescriptorAlignment-11476);
|
||||
case 11477:
|
||||
return VUID_WRAP(VUID-RuntimeSpirv-imageDescriptorAlignment-11477);
|
||||
case 11478:
|
||||
return VUID_WRAP(VUID-RuntimeSpirv-bufferDescriptorAlignment-11478);
|
||||
case 11479:
|
||||
return VUID_WRAP(VUID-RuntimeSpirv-bufferDescriptorAlignment-11479);
|
||||
case 11480:
|
||||
return VUID_WRAP(VUID-RuntimeSpirv-tensorDescriptorAlignment-11480);
|
||||
case 11482:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-DescriptorHeapEXT-11482);
|
||||
case 11805:
|
||||
@@ -3471,6 +3615,28 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-None-12295);
|
||||
case 12297:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Type-12297);
|
||||
case 13551:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-13551);
|
||||
case 13552:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-SplitBarrierEXT-13552);
|
||||
case 13553:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrierArriveEXT-13553);
|
||||
case 13556:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-13556);
|
||||
case 13557:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-13557);
|
||||
case 12465:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpConvertFToU-12465);
|
||||
case 12466:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpFConvert-12466);
|
||||
case 12467:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpFConvert-12467);
|
||||
case 12468:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Result-12468);
|
||||
case 12469:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Base-12469);
|
||||
case 12509:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Offset-12509);
|
||||
default:
|
||||
return ""; // unknown id
|
||||
}
|
||||
|
||||
+7
-1
@@ -713,6 +713,11 @@ class ValidationState_t {
|
||||
bool IsFP8VectorType(uint32_t id) const;
|
||||
bool IsFP8CoopMatType(uint32_t id) const;
|
||||
bool IsFP8Type(uint32_t id) const;
|
||||
bool IsOCPMicroscalingScalarType(uint32_t id) const;
|
||||
bool IsOCPMicroscalingNonByteScalarType(uint32_t id) const;
|
||||
bool IsOCPMicroscalingType(uint32_t id) const;
|
||||
bool ContainsOCPMicroscalingType(uint32_t id) const;
|
||||
bool ContainsOCPMicroscalingNonByteType(uint32_t id) const;
|
||||
bool IsFloatScalarType(uint32_t id, uint32_t width = 0) const;
|
||||
bool IsFloatArrayType(uint32_t id) const;
|
||||
bool IsFloatVectorType(uint32_t id) const;
|
||||
@@ -722,6 +727,7 @@ class ValidationState_t {
|
||||
bool IsIntScalarType(uint32_t id, uint32_t width = 0) const;
|
||||
bool IsIntScalarTypeWithSignedness(uint32_t id, uint32_t signedness) const;
|
||||
bool IsIntVectorType(uint32_t id) const;
|
||||
bool IsIntVectorType(uint32_t id, uint32_t width, uint32_t components) const;
|
||||
bool IsIntScalarOrVectorType(uint32_t id) const;
|
||||
bool IsUnsignedIntScalarType(uint32_t id) const;
|
||||
bool IsUnsignedIntVectorType(uint32_t id) const;
|
||||
@@ -956,7 +962,7 @@ class ValidationState_t {
|
||||
spv_result_t CooperativeMatrixShapesMatch(const Instruction* inst,
|
||||
uint32_t result_type_id,
|
||||
uint32_t m2, bool is_conversion,
|
||||
bool swap_row_col = false);
|
||||
bool swap_row_col);
|
||||
|
||||
spv_result_t CooperativeVectorDimensionsMatch(const Instruction* inst,
|
||||
uint32_t v1, uint32_t v2);
|
||||
|
||||
Reference in New Issue
Block a user