mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-23 07:20:28 +00:00
Added type tracking to the disassembler.
TODO: Actually use the tracked types to make sure that we print out values correctly.
This commit is contained in:
parent
d16ee3b428
commit
157e41bf57
@ -37,6 +37,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
// Binary API
|
||||
|
||||
@ -50,6 +51,9 @@ static const union {
|
||||
uint32_t value;
|
||||
} o32_host_order = {{0, 1, 2, 3}};
|
||||
|
||||
using id_to_type_id_map = std::unordered_map<uint32_t, uint32_t>;
|
||||
using type_id_to_type_map = std::unordered_map<uint32_t, libspirv::IdType>;
|
||||
|
||||
#define I32_ENDIAN_HOST (o32_host_order.value)
|
||||
|
||||
spv_result_t spvBinaryEndianness(const spv_binary binary,
|
||||
@ -363,6 +367,62 @@ spv_result_t spvBinaryDecodeOperand(
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Regsiters the given instruction with the type and id tracking
|
||||
/// tables.
|
||||
///
|
||||
/// @param[in] pInst the Opcode instruction stream
|
||||
/// @param[in] pOpcodeEntry the Opcode Entry describing the instruction
|
||||
/// @param[in, out] type_map the map of Ids to Types to be filled in
|
||||
/// @param[in, out] id_map the map of Ids to type Ids to be filled in
|
||||
/// @param[in, out] position position in the stream
|
||||
/// @param[out] pDiag return diagnostic on error
|
||||
///
|
||||
/// @return result code
|
||||
spv_result_t spvRegisterIdForOpcode(const spv_instruction_t* pInst,
|
||||
const spv_opcode_desc_t* pOpcodeEntry,
|
||||
type_id_to_type_map* type_map,
|
||||
id_to_type_id_map* id_map,
|
||||
spv_position position,
|
||||
spv_diagnostic* pDiagnostic) {
|
||||
libspirv::IdType detected_type = libspirv::kUnknownType;
|
||||
if (spvOpcodeIsType(pOpcodeEntry->opcode)) {
|
||||
if (spv::OpTypeInt == pOpcodeEntry->opcode) {
|
||||
detected_type.type_class = libspirv::IdTypeClass::kScalarIntegerType;
|
||||
detected_type.bitwidth = pInst->words[2];
|
||||
detected_type.isSigned = (pInst->words[3] != 0);
|
||||
} else if (spv::OpTypeFloat == pOpcodeEntry->opcode) {
|
||||
detected_type.type_class = libspirv::IdTypeClass::kScalarIntegerType;
|
||||
detected_type.bitwidth = pInst->words[2];
|
||||
detected_type.isSigned = true;
|
||||
} else {
|
||||
detected_type.type_class = libspirv::IdTypeClass::kOtherType;
|
||||
}
|
||||
}
|
||||
|
||||
// We do not use else-if here so that we can still catch the case where an
|
||||
// OpType* instruction shares the same ID as a non OpType* instruction.
|
||||
if (pOpcodeEntry->hasResult) {
|
||||
uint32_t value_id =
|
||||
pOpcodeEntry->hasType ? pInst->words[2] : pInst->words[1];
|
||||
if (id_map->find(value_id) != id_map->end()) {
|
||||
DIAGNOSTIC << "Id " << value_id << " is defined more than once";
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
(*id_map)[value_id] = pOpcodeEntry->hasType ? pInst->words[1] : 0;
|
||||
}
|
||||
|
||||
if (detected_type != libspirv::kUnknownType) {
|
||||
// This defines a new type.
|
||||
uint32_t id = pInst->words[1];
|
||||
(*type_map)[id] = detected_type;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
/// @brief Translate binary Opcode stream to textual form
|
||||
///
|
||||
/// @param[in] pInst the Opcode instruction stream
|
||||
@ -379,6 +439,8 @@ spv_result_t spvBinaryDecodeOpcode(spv_instruction_t* pInst,
|
||||
const spv_endianness_t endian,
|
||||
const uint32_t options,
|
||||
const libspirv::AssemblyGrammar& grammar,
|
||||
type_id_to_type_map* type_map,
|
||||
id_to_type_id_map* id_map,
|
||||
spv_assembly_syntax_format_t format,
|
||||
out_stream &stream, spv_position position,
|
||||
spv_diagnostic *pDiagnostic) {
|
||||
@ -499,7 +561,10 @@ spv_result_t spvBinaryDecodeOpcode(spv_instruction_t* pInst,
|
||||
}
|
||||
|
||||
stream.get() << no_result_id_strstream.str();
|
||||
|
||||
if (spv_result_t error = spvRegisterIdForOpcode(
|
||||
pInst, opcodeEntry, type_map, id_map, position, pDiagnostic)) {
|
||||
return error;
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@ -574,6 +639,10 @@ spv_result_t spvBinaryToTextWithFormat(
|
||||
const uint32_t *words = binary.code;
|
||||
position.index = SPV_INDEX_INSTRUCTION;
|
||||
spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
|
||||
|
||||
id_to_type_id_map id_map;
|
||||
type_id_to_type_map type_map;
|
||||
|
||||
while (position.index < binary.wordCount) {
|
||||
uint64_t index = position.index;
|
||||
uint16_t wordCount;
|
||||
@ -586,8 +655,8 @@ spv_result_t spvBinaryToTextWithFormat(
|
||||
spvInstructionCopy(&words[position.index], opcode, wordCount, endian,
|
||||
&inst);
|
||||
|
||||
if (spvBinaryDecodeOpcode(&inst, endian, options, grammar, format, stream,
|
||||
&position, pDiagnostic))
|
||||
if (spvBinaryDecodeOpcode(&inst, endian, options, grammar, &type_map,
|
||||
&id_map, format, stream, &position, pDiagnostic))
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
extInstType = inst.extInstType;
|
||||
|
||||
|
@ -587,6 +587,7 @@ int32_t spvOpcodeIsType(const Op opcode) {
|
||||
case OpTypeVector:
|
||||
case OpTypeMatrix:
|
||||
case OpTypeSampler:
|
||||
case OpTypeSampledImage:
|
||||
case OpTypeArray:
|
||||
case OpTypeRuntimeArray:
|
||||
case OpTypeStruct:
|
||||
|
@ -60,6 +60,18 @@ struct IdType {
|
||||
IdTypeClass type_class;
|
||||
};
|
||||
|
||||
// Default equality operator for IdType. Tests if all members are the same.
|
||||
inline bool operator==(const IdType &first, const IdType &second) {
|
||||
return (first.bitwidth == second.bitwidth) &&
|
||||
(first.isSigned == second.isSigned) &&
|
||||
(first.type_class == second.type_class);
|
||||
}
|
||||
|
||||
// Tests whether any member of the IdTypes do not match.
|
||||
inline bool operator!=(const IdType &first, const IdType &second) {
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
// A value representing an unknown type.
|
||||
extern const IdType kUnknownType;
|
||||
|
||||
|
@ -138,6 +138,37 @@ TEST_F(BinaryToText, InvalidDiagnostic) {
|
||||
operandTable, extInstTable, &text, nullptr));
|
||||
}
|
||||
|
||||
struct FailedDecodeCase {
|
||||
std::string source_text;
|
||||
std::vector<uint32_t> appended_instruction;
|
||||
std::string expected_error_message;
|
||||
};
|
||||
|
||||
using BinaryToTextFail =
|
||||
spvtest::TextToBinaryTestBase <
|
||||
::testing::TestWithParam<FailedDecodeCase>>;
|
||||
|
||||
TEST_P(BinaryToTextFail, EncodeSuccessfullyDecodeFailed) {
|
||||
EXPECT_THAT(EncodeSuccessfullyDecodeFailed(GetParam().source_text,
|
||||
GetParam().appended_instruction),
|
||||
Eq(GetParam().expected_error_message));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InvalidIds, BinaryToTextFail,
|
||||
::testing::ValuesIn(std::vector<FailedDecodeCase>{
|
||||
{"%1 = OpTypeVoid",
|
||||
spvtest::MakeInstruction(spv::OpTypeVoid, {1}),
|
||||
"Id 1 is defined more than once"},
|
||||
{"%1 = OpTypeVoid\n"
|
||||
"%2 = OpNot %1 %foo",
|
||||
spvtest::MakeInstruction(spv::OpNot, {1, 2, 3}),
|
||||
"Id 2 is defined more than once"},
|
||||
{"%1 = OpTypeVoid\n"
|
||||
"%2 = OpNot %1 %foo",
|
||||
spvtest::MakeInstruction(spv::OpNot, {1, 1, 3}),
|
||||
"Id 1 is defined more than once"},
|
||||
}));
|
||||
|
||||
TEST(BinaryToTextSmall, OneInstruction) {
|
||||
// TODO(dneto): This test could/should be refactored.
|
||||
spv_opcode_table opcodeTable;
|
||||
|
@ -131,6 +131,32 @@ class TextToBinaryTestBase : public T {
|
||||
return decoded_string.substr(preamble_end + schema0.size());
|
||||
}
|
||||
|
||||
// Encodes SPIR-V text into binary. This is expected to succeed.
|
||||
// The given words are then appended to the binary, and the result
|
||||
// is then decoded. This is expected to fail.
|
||||
// Returns the error message.
|
||||
std::string EncodeSuccessfullyDecodeFailed(
|
||||
const std::string& text,
|
||||
const SpirvVector& words_to_append) {
|
||||
SpirvVector code = spvtest::Concatenate(
|
||||
{CompileSuccessfully(text, SPV_ASSEMBLY_SYNTAX_FORMAT_DEFAULT),
|
||||
words_to_append});
|
||||
|
||||
spv_text decoded_text;
|
||||
EXPECT_NE(SPV_SUCCESS,
|
||||
spvBinaryToText(code.data(), code.size(),
|
||||
SPV_BINARY_TO_TEXT_OPTION_NONE, opcodeTable,
|
||||
operandTable, extInstTable, &decoded_text,
|
||||
&diagnostic));
|
||||
if (diagnostic) {
|
||||
std::string error_message = diagnostic->error;
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
diagnostic = nullptr;
|
||||
return error_message;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Compiles SPIR-V text, asserts success, and returns the words representing
|
||||
// the instructions. In particular, skip the words in the SPIR-V header.
|
||||
SpirvVector CompiledInstructions(const std::string& text,
|
||||
|
Loading…
Reference in New Issue
Block a user