#include "source/binary.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iterator>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
#include "source/assembly_grammar.h"
#include "source/diagnostic.h"
#include "source/ext_inst.h"
#include "source/latest_version_spirv_header.h"
#include "source/opcode.h"
#include "source/operand.h"
#include "source/spirv_constant.h"
#include "source/spirv_endian.h"
#include "source/util/string_utils.h"
spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
const spv_endianness_t endian,
spv_header_t* pHeader) {
if (!binary->code) return SPV_ERROR_INVALID_BINARY;
if (binary->wordCount < SPV_INDEX_INSTRUCTION)
return SPV_ERROR_INVALID_BINARY;
if (!pHeader) return SPV_ERROR_INVALID_POINTER;
pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian);
pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian);
if ((pHeader->version & 0x000000ff) || pHeader->version & 0xff000000)
return SPV_ERROR_INVALID_BINARY;
if (pHeader->version < SPV_SPIRV_VERSION_WORD(1, 0) ||
pHeader->version > SPV_VERSION)
return SPV_ERROR_INVALID_BINARY;
pHeader->generator =
spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian);
pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian);
pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian);
pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION];
return SPV_SUCCESS;
}
std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst,
const uint16_t operand_index) {
assert(operand_index < inst.num_operands);
const spv_parsed_operand_t& operand = inst.operands[operand_index];
return spvtools::utils::MakeString(inst.words + operand.offset,
operand.num_words);
}
namespace {
class Parser {
public:
Parser(const spv_const_context context, void* user_data,
spv_parsed_header_fn_t parsed_header_fn,
spv_parsed_instruction_fn_t parsed_instruction_fn)
: grammar_(context),
consumer_(context->consumer),
user_data_(user_data),
parsed_header_fn_(parsed_header_fn),
parsed_instruction_fn_(parsed_instruction_fn) {}
spv_result_t parse(const uint32_t* words, size_t num_words,
spv_diagnostic* diagnostic);
private:
spv_result_t parseModule();
spv_result_t parseInstruction();
spv_result_t parseOperand(size_t inst_offset, spv_parsed_instruction_t* inst,
const spv_operand_type_t type,
std::vector<uint32_t>* endian_converted_inst_words,
std::vector<spv_parsed_operand_t>* operands,
spv_operand_pattern_t* expected_operands);
spv_result_t setNumericTypeInfoForType(spv_parsed_operand_t* parsed_operand,
uint32_t type_id);
void recordNumberType(size_t inst_offset,
const spv_parsed_instruction_t* inst);
spvtools::DiagnosticStream diagnostic(spv_result_t error) {
return spvtools::DiagnosticStream({0, 0, _.instruction_count}, consumer_,
"", error);
}
spvtools::DiagnosticStream diagnostic() {
return diagnostic(SPV_ERROR_INVALID_BINARY);
}
spv_result_t exhaustedInputDiagnostic(size_t inst_offset, spv::Op opcode,
spv_operand_type_t type) {
return diagnostic() << "End of input reached while decoding Op"
<< spvOpcodeString(opcode) << " starting at word "
<< inst_offset
<< ((_.word_index < _.num_words) ? ": truncated "
: ": missing ")
<< spvOperandTypeStr(type) << " operand at word offset "
<< _.word_index - inst_offset << ".";
}
uint32_t peek() const { return peekAt(_.word_index); }
uint32_t peekAt(size_t index) const {
assert(index < _.num_words);
return spvFixWord(_.words[index], _.endian);
}
const spvtools::AssemblyGrammar grammar_;
const spvtools::MessageConsumer& consumer_;
void* const user_data_;
const spv_parsed_header_fn_t parsed_header_fn_;
const spv_parsed_instruction_fn_t
parsed_instruction_fn_;
struct NumberType {
spv_number_kind_t type;
uint32_t bit_width;
};
struct State {
State(const uint32_t* words_arg, size_t num_words_arg,
spv_diagnostic* diagnostic_arg)
: words(words_arg),
num_words(num_words_arg),
diagnostic(diagnostic_arg),
word_index(0),
instruction_count(0),
endian(),
requires_endian_conversion(false) {
operands.reserve(25);
endian_converted_words.reserve(25);
expected_operands.reserve(25);
}
State() : State(0, 0, nullptr) {}
const uint32_t* words;
size_t num_words;
spv_diagnostic* diagnostic;
size_t word_index;
size_t instruction_count;
spv_endianness_t endian;
bool requires_endian_conversion;
std::unordered_map<uint32_t, uint32_t> id_to_type_id;
std::unordered_map<uint32_t, NumberType> type_id_to_number_type_info;
std::unordered_map<uint32_t, spv_ext_inst_type_t>
import_id_to_ext_inst_type;
std::vector<spv_parsed_operand_t> operands;
std::vector<uint32_t> endian_converted_words;
spv_operand_pattern_t expected_operands;
} _;
};
spv_result_t Parser::parse(const uint32_t* words, size_t num_words,
spv_diagnostic* diagnostic_arg) {
_ = State(words, num_words, diagnostic_arg);
const spv_result_t result = parseModule();
_ = State();
return result;
}
spv_result_t Parser::parseModule() {
if (!_.words) return diagnostic() << "Missing module.";
if (_.num_words < SPV_INDEX_INSTRUCTION)
return diagnostic() << "Module has incomplete header: only " << _.num_words
<< " words instead of " << SPV_INDEX_INSTRUCTION;
spv_const_binary_t binary{_.words, _.num_words};
if (spvBinaryEndianness(&binary, &_.endian)) {
return diagnostic() << "Invalid SPIR-V magic number '" << std::hex
<< _.words[0] << "'.";
}
_.requires_endian_conversion = !spvIsHostEndian(_.endian);
spv_header_t header;
if (spvBinaryHeaderGet(&binary, _.endian, &header)) {
return diagnostic(SPV_ERROR_INTERNAL)
<< "Internal error: unhandled header parse failure";
}
if (parsed_header_fn_) {
if (auto error = parsed_header_fn_(user_data_, _.endian, header.magic,
header.version, header.generator,
header.bound, header.schema)) {
return error;
}
}
_.word_index = SPV_INDEX_INSTRUCTION;
while (_.word_index < _.num_words)
if (auto error = parseInstruction()) return error;
assert(_.word_index == _.num_words);
return SPV_SUCCESS;
}
spv_result_t Parser::parseInstruction() {
_.instruction_count++;
spv_parsed_instruction_t inst = {};
const uint32_t first_word = peek();
_.endian_converted_words.clear();
_.endian_converted_words.push_back(first_word);
_.operands.clear();
assert(_.word_index < _.num_words);
uint16_t inst_word_count = 0;
spvOpcodeSplit(first_word, &inst_word_count, &inst.opcode);
if (inst_word_count < 1) {
return diagnostic() << "Invalid instruction word count: "
<< inst_word_count;
}
spv_opcode_desc opcode_desc;
if (grammar_.lookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc))
return diagnostic() << "Invalid opcode: " << inst.opcode;
const size_t inst_offset = _.word_index;
_.word_index++;
_.expected_operands.clear();
for (auto i = 0; i < opcode_desc->numTypes; i++)
_.expected_operands.push_back(
opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]);
while (_.word_index < inst_offset + inst_word_count) {
const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset);
if (_.expected_operands.empty()) {
return diagnostic() << "Invalid instruction Op" << opcode_desc->name
<< " starting at word " << inst_offset
<< ": expected no more operands after "
<< inst_word_index
<< " words, but stated word count is "
<< inst_word_count << ".";
}
spv_operand_type_t type =
spvTakeFirstMatchableOperand(&_.expected_operands);
if (auto error =
parseOperand(inst_offset, &inst, type, &_.endian_converted_words,
&_.operands, &_.expected_operands)) {
return error;
}
}
if (!_.expected_operands.empty() &&
!spvOperandIsOptional(_.expected_operands.back())) {
return diagnostic() << "End of input reached while decoding Op"
<< opcode_desc->name << " starting at word "
<< inst_offset << ": expected more operands after "
<< inst_word_count << " words.";
}
if ((inst_offset + inst_word_count) != _.word_index) {
return diagnostic() << "Invalid word count: Op" << opcode_desc->name
<< " starting at word " << inst_offset
<< " says it has " << inst_word_count
<< " words, but found " << _.word_index - inst_offset
<< " words instead.";
}
assert(!_.requires_endian_conversion ||
(inst_word_count == _.endian_converted_words.size()));
assert(_.requires_endian_conversion ||
(_.endian_converted_words.size() == 1));
recordNumberType(inst_offset, &inst);
if (_.requires_endian_conversion) {
inst.words = _.endian_converted_words.data();
} else {
inst.words = _.words + inst_offset;
}
inst.num_words = inst_word_count;
inst.operands = _.operands.data();
inst.num_operands = uint16_t(_.operands.size());
if (parsed_instruction_fn_) {
if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error;
}
return SPV_SUCCESS;
}
spv_result_t Parser::parseOperand(size_t inst_offset,
spv_parsed_instruction_t* inst,
const spv_operand_type_t type,
std::vector<uint32_t>* words,
std::vector<spv_parsed_operand_t>* operands,
spv_operand_pattern_t* expected_operands) {
const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
spv_parsed_operand_t parsed_operand;
parsed_operand.offset = uint16_t(_.word_index - inst_offset);
parsed_operand.num_words = 1;
parsed_operand.type = type;
parsed_operand.number_kind = SPV_NUMBER_NONE;
parsed_operand.number_bit_width = 0;
if (_.word_index >= _.num_words)
return exhaustedInputDiagnostic(inst_offset, opcode, type);
const uint32_t word = peek();
bool convert_operand_endianness = true;
switch (type) {
case SPV_OPERAND_TYPE_TYPE_ID:
if (!word)
return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Type Id is 0";
inst->type_id = word;
break;
case SPV_OPERAND_TYPE_RESULT_ID:
if (!word)
return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Result Id is 0";
inst->result_id = word;
if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end())
return diagnostic(SPV_ERROR_INVALID_ID)
<< "Id " << inst->result_id << " is defined more than once";
_.id_to_type_id[inst->result_id] =
spvOpcodeGeneratesType(opcode) ? inst->result_id : inst->type_id;
break;
case SPV_OPERAND_TYPE_ID:
case SPV_OPERAND_TYPE_OPTIONAL_ID:
if (!word) return diagnostic(SPV_ERROR_INVALID_ID) << "Id is 0";
parsed_operand.type = SPV_OPERAND_TYPE_ID;
if (opcode == spv::Op::OpExtInst && parsed_operand.offset == 3) {
auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word);
if (ext_inst_type_iter == _.import_id_to_ext_inst_type.end()) {
return diagnostic(SPV_ERROR_INVALID_ID)
<< "OpExtInst set Id " << word
<< " does not reference an OpExtInstImport result Id";
}
inst->ext_inst_type = ext_inst_type_iter->second;
}
break;
case SPV_OPERAND_TYPE_SCOPE_ID:
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
if (!word) return diagnostic() << spvOperandTypeStr(type) << " is 0";
break;
case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
assert(spv::Op::OpExtInst == opcode);
assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
spv_ext_inst_desc ext_inst;
if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
SPV_SUCCESS) {
spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
} else {
if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
return diagnostic()
<< "Invalid extended instruction number: " << word;
} else {
expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
}
}
} break;
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
assert(spv::Op::OpSpecConstantOp == opcode);
if (word > static_cast<uint32_t>(spv::Op::Max) ||
grammar_.lookupSpecConstantOpcode(spv::Op(word))) {
return diagnostic()
<< "Invalid " << spvOperandTypeStr(type) << ": " << word;
}
spv_opcode_desc opcode_entry = nullptr;
if (grammar_.lookupOpcode(spv::Op(word), &opcode_entry)) {
return diagnostic(SPV_ERROR_INTERNAL)
<< "OpSpecConstant opcode table out of sync";
}
assert(opcode_entry->hasType);
assert(opcode_entry->hasResult);
assert(opcode_entry->numTypes >= 2);
spvPushOperandTypes(opcode_entry->operandTypes + 2, expected_operands);
} break;
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER;
parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT;
parsed_operand.number_bit_width = 32;
break;
case SPV_OPERAND_TYPE_LITERAL_FLOAT:
parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_FLOAT;
parsed_operand.number_kind = SPV_NUMBER_FLOATING;
parsed_operand.number_bit_width = 32;
break;
case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER;
if (opcode == spv::Op::OpSwitch) {
const uint32_t selector_id = peekAt(inst_offset + 1);
const auto type_id_iter = _.id_to_type_id.find(selector_id);
if (type_id_iter == _.id_to_type_id.end() ||
type_id_iter->second == 0) {
return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
<< " has no type";
}
uint32_t type_id = type_id_iter->second;
if (selector_id == type_id) {
return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
<< " is a type, not a value";
}
if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id))
return error;
if (parsed_operand.number_kind != SPV_NUMBER_UNSIGNED_INT &&
parsed_operand.number_kind != SPV_NUMBER_SIGNED_INT) {
return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
<< " is not a scalar integer";
}
} else {
assert(opcode == spv::Op::OpConstant ||
opcode == spv::Op::OpSpecConstant);
assert(inst->type_id);
if (auto error =
setNumericTypeInfoForType(&parsed_operand, inst->type_id))
return error;
}
break;
case SPV_OPERAND_TYPE_LITERAL_STRING:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
const size_t max_words = _.num_words - _.word_index;
std::string string =
spvtools::utils::MakeString(_.words + _.word_index, max_words, false);
if (string.length() == max_words * 4)
return exhaustedInputDiagnostic(inst_offset, opcode, type);
const size_t string_num_words = string.length() / 4 + 1;
if (string_num_words > std::numeric_limits<uint16_t>::max()) {
return diagnostic() << "Literal string is longer than "
<< std::numeric_limits<uint16_t>::max()
<< " words: " << string_num_words << " words long";
}
parsed_operand.num_words = uint16_t(string_num_words);
parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING;
if (spv::Op::OpExtInstImport == opcode) {
const spv_ext_inst_type_t ext_inst_type =
spvExtInstImportTypeGet(string.c_str());
if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
return diagnostic()
<< "Invalid extended instruction import '" << string << "'";
}
assert(inst->result_id);
_.import_id_to_ext_inst_type[inst->result_id] = ext_inst_type;
}
} break;
case SPV_OPERAND_TYPE_CAPABILITY:
case SPV_OPERAND_TYPE_EXECUTION_MODEL:
case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
case SPV_OPERAND_TYPE_MEMORY_MODEL:
case SPV_OPERAND_TYPE_EXECUTION_MODE:
case SPV_OPERAND_TYPE_STORAGE_CLASS:
case SPV_OPERAND_TYPE_DIMENSIONALITY:
case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT:
case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
case SPV_OPERAND_TYPE_LINKAGE_TYPE:
case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
case SPV_OPERAND_TYPE_DECORATION:
case SPV_OPERAND_TYPE_BUILT_IN:
case SPV_OPERAND_TYPE_GROUP_OPERATION:
case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
case SPV_OPERAND_TYPE_RAY_FLAGS:
case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
case SPV_OPERAND_TYPE_DEBUG_OPERATION:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
case SPV_OPERAND_TYPE_FPDENORM_MODE:
case SPV_OPERAND_TYPE_FPOPERATION_MODE:
case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
case SPV_OPERAND_TYPE_OVERFLOW_MODES:
case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: {
if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER)
parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER;
if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT)
parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT;
spv_operand_desc entry;
if (grammar_.lookupOperand(type, word, &entry)) {
return diagnostic()
<< "Invalid " << spvOperandTypeStr(parsed_operand.type)
<< " operand: " << word;
}
spvPushOperandTypes(entry->operandTypes, expected_operands);
} break;
case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: {
spv_operand_desc entry;
if (grammar_.lookupOperand(type, word, &entry)) {
return diagnostic()
<< "Invalid " << spvOperandTypeStr(parsed_operand.type)
<< " operand: " << word
<< ", if you are creating a new source language please use "
"value 0 "
"(Unknown) and when ready, add your source language to "
"SPRIV-Headers";
}
spvPushOperandTypes(entry->operandTypes, expected_operands);
} break;
case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
case SPV_OPERAND_TYPE_LOOP_CONTROL:
case SPV_OPERAND_TYPE_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_SELECTION_CONTROL:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: {
if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE)
parsed_operand.type = SPV_OPERAND_TYPE_IMAGE;
else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)
parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS;
if (type == SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS)
parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS;
uint32_t remaining_word = word;
for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) {
if (remaining_word & mask) {
spv_operand_desc entry;
if (grammar_.lookupOperand(type, mask, &entry)) {
return diagnostic()
<< "Invalid " << spvOperandTypeStr(parsed_operand.type)
<< " operand: " << word << " has invalid mask component "
<< mask;
}
remaining_word ^= mask;
spvPushOperandTypes(entry->operandTypes, expected_operands);
}
}
if (word == 0) {
spv_operand_desc entry;
if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) {
spvPushOperandTypes(entry->operandTypes, expected_operands);
}
}
} break;
default:
return diagnostic() << "Internal error: Unhandled operand type: " << type;
}
assert(spvOperandIsConcrete(parsed_operand.type));
operands->push_back(parsed_operand);
const size_t index_after_operand = _.word_index + parsed_operand.num_words;
if (_.num_words < index_after_operand)
return exhaustedInputDiagnostic(inst_offset, opcode, type);
if (_.requires_endian_conversion) {
if (convert_operand_endianness) {
const spv_endianness_t endianness = _.endian;
std::transform(_.words + _.word_index, _.words + index_after_operand,
std::back_inserter(*words),
[endianness](const uint32_t raw_word) {
return spvFixWord(raw_word, endianness);
});
} else {
words->insert(words->end(), _.words + _.word_index,
_.words + index_after_operand);
}
}
_.word_index = index_after_operand;
return SPV_SUCCESS;
}
spv_result_t Parser::setNumericTypeInfoForType(
spv_parsed_operand_t* parsed_operand, uint32_t type_id) {
assert(type_id != 0);
auto type_info_iter = _.type_id_to_number_type_info.find(type_id);
if (type_info_iter == _.type_id_to_number_type_info.end()) {
return diagnostic() << "Type Id " << type_id << " is not a type";
}
const NumberType& info = type_info_iter->second;
if (info.type == SPV_NUMBER_NONE) {
return diagnostic() << "Type Id " << type_id
<< " is not a scalar numeric type";
}
parsed_operand->number_kind = info.type;
parsed_operand->number_bit_width = info.bit_width;
parsed_operand->num_words = static_cast<uint16_t>((info.bit_width + 31) / 32);
return SPV_SUCCESS;
}
void Parser::recordNumberType(size_t inst_offset,
const spv_parsed_instruction_t* inst) {
const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
if (spvOpcodeGeneratesType(opcode)) {
NumberType info = {SPV_NUMBER_NONE, 0};
if (spv::Op::OpTypeInt == opcode) {
const bool is_signed = peekAt(inst_offset + 3) != 0;
info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
info.bit_width = peekAt(inst_offset + 2);
} else if (spv::Op::OpTypeFloat == opcode) {
info.type = SPV_NUMBER_FLOATING;
info.bit_width = peekAt(inst_offset + 2);
}
_.type_id_to_number_type_info[inst->result_id] = info;
}
}
}
spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
const uint32_t* code, const size_t num_words,
spv_parsed_header_fn_t parsed_header,
spv_parsed_instruction_fn_t parsed_instruction,
spv_diagnostic* diagnostic) {
spv_context_t hijack_context = *context;
if (diagnostic) {
*diagnostic = nullptr;
spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic);
}
Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction);
return parser.parse(code, num_words, diagnostic);
}
void spvBinaryDestroy(spv_binary binary) {
if (binary) {
if (binary->code) delete[] binary->code;
delete binary;
}
}
size_t spv_strnlen_s(const char* str, size_t strsz) {
if (!str) return 0;
for (size_t i = 0; i < strsz; i++) {
if (!str[i]) return i;
}
return strsz;
}