All values now represent symbolic names instead of mixed with numeric.

Also removed un-necessary heap-allocation of spv_named_id_table.
This removed the necessity to expose a function to create/destroy it
and simplified the interface.
This commit is contained in:
Andrew Woloszyn 2015-09-22 15:50:33 -04:00 committed by David Neto
parent a66952d38c
commit 13804e5d63
12 changed files with 241 additions and 229 deletions

View File

@ -28,6 +28,7 @@
#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@ -47,9 +48,7 @@ using spvutils::BitwiseCast;
// Structures
struct spv_named_id_table_t {
std::unordered_map<std::string, uint32_t> namedIds;
};
using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
// Text API
@ -72,39 +71,12 @@ std::string spvGetWord(const char *str) {
return ""; // Make certain compilers happy.
}
spv_named_id_table spvNamedIdTableCreate() {
return new spv_named_id_table_t();
}
void spvNamedIdTableDestory(spv_named_id_table table) { delete table; }
uint32_t spvNamedIdAssignOrGet(spv_named_id_table table, const char *textValue,
uint32_t spvNamedIdAssignOrGet(spv_named_id_table* table, const char *textValue,
uint32_t *pBound) {
if (table->namedIds.end() == table->namedIds.find(textValue)) {
table->namedIds[textValue] = *pBound;
if (table->end() == table->find(textValue)) {
(*table)[std::string(textValue)] = *pBound;
}
return table->namedIds[textValue];
}
int32_t spvTextIsNamedId(const char *textValue) {
// TODO: Strengthen the parsing of textValue to only include allow names that
// match: ([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*
switch (textValue[0]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return false;
default:
break;
}
return true;
return (*table)[textValue];
}
spv_result_t spvTextAdvanceLine(const spv_text text, spv_position position) {
@ -125,6 +97,22 @@ spv_result_t spvTextAdvanceLine(const spv_text text, spv_position position) {
}
}
bool spvIsValidIDCharacter(const char value) {
return value == '_' || 0 != ::isalnum(value);
}
// Returns true if the given string represents a valid ID name.
bool spvIsValidID(const char* textValue) {
const char* c = textValue;
for (; *c != '\0'; ++c) {
if (!spvIsValidIDCharacter(*c)) {
return false;
}
}
// If the string was empty, then the ID also is not valid.
return c != textValue;
}
spv_result_t spvTextAdvance(const spv_text text, spv_position position) {
// NOTE: Consume white space, otherwise don't advance.
switch (text->str[position->index]) {
@ -409,10 +397,25 @@ spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
return SPV_SUCCESS;
}
/// @brief Translate an Opcode operand to binary form
///
/// @param[in] type of the operand
/// @param[in] textValue word of text to be parsed
/// @param[in] operandTable operand lookup table
/// @param[in,out] namedIdTable table of named ID's
/// @param[out] pInst return binary Opcode
/// @param[in,out] pExpectedOperands the operand types expected
/// @param[in,out] pBound current highest defined ID value
/// @param[in] pPosition used in diagnostic on error
/// @param[out] pDiagnostic populated on error
///
/// @return result code
spv_result_t spvTextEncodeOperand(
const spv_operand_type_t type, const char *textValue,
const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
spv_named_id_table namedIdTable, spv_instruction_t *pInst,
spv_named_id_table* namedIdTable, spv_instruction_t *pInst,
spv_operand_pattern_t *pExpectedOperands, uint32_t *pBound,
const spv_position position, spv_diagnostic *pDiagnostic) {
// NOTE: Handle immediate int in the stream
@ -430,7 +433,6 @@ spv_result_t spvTextEncodeOperand(
position->index += size;
pInst->words[pInst->wordCount] = immediateInt;
pInst->wordCount += 1;
if (isIdType(type)) *pBound = std::max(*pBound, immediateInt + 1);
return SPV_SUCCESS;
}
@ -442,22 +444,16 @@ spv_result_t spvTextEncodeOperand(
case SPV_OPERAND_TYPE_EXECUTION_SCOPE: {
if ('%' == textValue[0]) {
textValue++;
}
// TODO: Force all ID's to be prefixed with '%'.
uint32_t id = 0;
if (spvTextIsNamedId(textValue)) {
id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
} else {
if (spvTextToUInt32(textValue, &id) != SPV_SUCCESS) {
if (spvOperandIsOptional(type)) {
return SPV_FAILED_MATCH;
} else {
DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
<< textValue << "'.";
return SPV_ERROR_INVALID_TEXT;
}
}
DIAGNOSTIC << "Expected id to start with %.";
return SPV_ERROR_INVALID_TEXT;
}
if (!spvIsValidID(textValue)) {
DIAGNOSTIC << "Invalid ID " << textValue;
return SPV_ERROR_INVALID_TEXT;
}
const uint32_t id =
spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
pInst->words[pInst->wordCount++] = id;
*pBound = std::max(*pBound, id + 1);
} break;
@ -609,7 +605,7 @@ namespace {
/// leaves position pointing to the error in text.
spv_result_t encodeInstructionStartingWithImmediate(
const spv_text text, const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable, spv_named_id_table namedIdTable,
const spv_ext_inst_table extInstTable, spv_named_id_table* namedIdTable,
uint32_t *pBound, spv_instruction_t *pInst, spv_position position,
spv_diagnostic *pDiagnostic) {
std::string firstWord;
@ -684,10 +680,23 @@ spv_result_t encodeInstructionStartingWithImmediate(
} // anonymous namespace
/// @brief Translate single Opcode and operands to binary form
///
/// @param[in] text stream to translate
/// @param[in] format the assembly syntax format of text
/// @param[in] opcodeTable Opcode lookup table
/// @param[in] operandTable operand lookup table
/// @param[in,out] namedIdTable table of named ID's
/// @param[in,out] pBound current highest defined ID value
/// @param[out] pInst returned binary Opcode
/// @param[in,out] pPosition in the text stream
/// @param[out] pDiagnostic populated on failure
///
/// @return result code
spv_result_t spvTextEncodeOpcode(
const spv_text text, spv_assembly_syntax_format_t format,
const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable, spv_named_id_table namedIdTable,
const spv_ext_inst_table extInstTable, spv_named_id_table* namedIdTable,
uint32_t *pBound, spv_instruction_t *pInst, spv_position position,
spv_diagnostic *pDiagnostic) {
@ -898,29 +907,28 @@ spv_result_t spvTextToBinaryInternal(const spv_text text,
return SPV_ERROR_INVALID_TEXT;
}
spv_named_id_table namedIdTable = spvNamedIdTableCreate();
if (!namedIdTable) return SPV_ERROR_OUT_OF_MEMORY;
// This causes namedIdTable to get cleaned up as soon as it is no
// longer necessary.
{
spv_named_id_table namedIdTable;
spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
while (text->length > position.index) {
spv_instruction_t inst = {};
inst.extInstType = extInstType;
spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
while (text->length > position.index) {
spv_instruction_t inst = {};
inst.extInstType = extInstType;
if (spvTextEncodeOpcode(text, format, opcodeTable, operandTable,
extInstTable, &namedIdTable, &bound, &inst,
&position, pDiagnostic)) {
return SPV_ERROR_INVALID_TEXT;
}
extInstType = inst.extInstType;
if (spvTextEncodeOpcode(text, format, opcodeTable, operandTable,
extInstTable, namedIdTable, &bound, &inst,
&position, pDiagnostic)) {
spvNamedIdTableDestory(namedIdTable);
return SPV_ERROR_INVALID_TEXT;
instructions.push_back(inst);
if (spvTextAdvance(text, &position)) break;
}
extInstType = inst.extInstType;
instructions.push_back(inst);
if (spvTextAdvance(text, &position)) break;
}
spvNamedIdTableDestory(namedIdTable);
size_t totalSize = SPV_INDEX_INSTRUCTION;
for (auto &inst : instructions) {
totalSize += inst.wordCount;

View File

@ -58,11 +58,6 @@ typedef struct spv_literal_t {
} value;
} spv_literal_t;
struct spv_named_id_table_t;
// Types
typedef spv_named_id_table_t *spv_named_id_table;
// Functions
@ -151,33 +146,6 @@ spv_result_t spvTextToUInt32(const char *textValue, uint32_t *pValue);
/// @return result code
spv_result_t spvTextToLiteral(const char *textValue, spv_literal_t *pLiteral);
/// @brief Create a named ID table
///
/// @return named ID table
spv_named_id_table spvNamedIdTableCreate();
/// @brief Free a named ID table
///
/// @param table named ID table
void spvNamedIdTableDestory(spv_named_id_table table);
/// @brief Lookup or assign a named ID
///
/// @param table named ID table
/// @param textValue name value
/// @param pBound upper ID bound, used for assigning new ID's
///
/// @return the new ID assossiated with the named ID
uint32_t spvNamedIdAssignOrGet(spv_named_id_table table, const char *textValue,
uint32_t *pBound);
/// @brief Determine if a name has an assossiated ID
///
/// @param textValue name value
///
/// @return zero on failure, non-zero otherwise
int32_t spvTextIsNamedId(const char *textValue);
/// @brief Parses a mask expression string for the given operand type.
///
/// A mask expression is a sequence of one or more terms separated by '|',
@ -195,45 +163,4 @@ int32_t spvTextIsNamedId(const char *textValue);
spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
const spv_operand_type_t type,
const char *textValue, uint32_t *pValue);
/// @brief Translate an Opcode operand to binary form
///
/// @param[in] type of the operand
/// @param[in] textValue word of text to be parsed
/// @param[in] operandTable operand lookup table
/// @param[in,out] namedIdTable table of named ID's
/// @param[out] pInst return binary Opcode
/// @param[in,out] pExpectedOperands the operand types expected
/// @param[in,out] pBound current highest defined ID value
/// @param[in] pPosition used in diagnostic on error
/// @param[out] pDiagnostic populated on error
///
/// @return result code
spv_result_t spvTextEncodeOperand(
const spv_operand_type_t type, const char *textValue,
const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
spv_named_id_table namedIdTable, spv_instruction_t *pInst,
spv_operand_pattern_t *pExpectedOperands, uint32_t *pBound,
const spv_position_t *pPosition, spv_diagnostic *pDiagnostic);
/// @brief Translate single Opcode and operands to binary form
///
/// @param[in] text stream to translate
/// @param[in] format the assembly syntax format of text
/// @param[in] opcodeTable Opcode lookup table
/// @param[in] operandTable operand lookup table
/// @param[in,out] namedIdTable table of named ID's
/// @param[in,out] pBound current highest defined ID value
/// @param[out] pInst returned binary Opcode
/// @param[in,out] pPosition in the text stream
/// @param[out] pDiagnostic populated on failure
///
/// @return result code
spv_result_t spvTextEncodeOpcode(
const spv_text text, spv_assembly_syntax_format_t format,
const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable, spv_named_id_table namedIdTable,
uint32_t *pBound, spv_instruction_t *pInst, spv_position_t *pPosition,
spv_diagnostic *pDiagnostic);
#endif

View File

@ -75,12 +75,12 @@ An ID definition pertains to the `<result-id>` of an OpCode, and ID usage is any
input to an OpCode. All IDs are prefixed with `%`. To differentiate between
defs and uses, we suggest using the second format shown in the above example.
## Named IDs
The assembler also supports named IDs, or virtual IDs, which greatly improves
the readability of the assembly. The same ID definition and usage prefixes
apply. Names must begin with an character in the range `[a-z|A-Z]`. The
following example will result in identical SPIR-V binary as the example above.
The ID names do not necessarily have to be numerical. Furthermore to avoid
aliasing names, if a name is numerical, it will not necessarily map to the
corresponding numerical id in the generated spirv. The same ID definition and
usage prefixes apply. Names may contain any character in
['0-9|a-z|A-Z|\_'] The following example will result in identical SPIR-V binary
as the example above.
```
OpCapability Shader
@ -95,6 +95,8 @@ following example will result in identical SPIR-V binary as the example above.
OpFunctionEnd
```
## Arbitrary Integers
<a name="immediate"></a>

View File

@ -57,7 +57,7 @@ class BinaryToText : public ::testing::Test {
%12 = OpTypeFloat 16
%13 = OpTypeFloat 32
%14 = OpTypeFloat 64
%15 = OpTypeVector 4 2
%15 = OpTypeVector %4 2
)";
spv_text_t text = {textStr, strlen(textStr)};
spv_diagnostic diagnostic = nullptr;

View File

@ -69,8 +69,8 @@ OpEntryPoint Vertex %2 "main"
"\n" + std::string(GetParam().constGenInst) + R"(
%6 = OpTypeFunction %3
%2 = OpFunction %3 None %6
%8 = OpLabel
%9 = OpExtInst )" + GetParam().extInstRetType +
%7 = OpLabel
%8 = OpExtInst )" + GetParam().extInstRetType +
" %1 " + GetParam().extInstOpName + " " +
GetParam().extInstOperandVars + R"(
OpReturn
@ -80,7 +80,7 @@ OpFunctionEnd
R"(; SPIR-V
; Version: 99
; Generator: Khronos
; Bound: 10
; Bound: 9
; Schema: 0)";
spv_binary binary;
spv_diagnostic diagnostic;
@ -100,7 +100,7 @@ OpFunctionEnd
// the generated SPIR-V binary.
std::vector<uint32_t> expected_contains(
{12 /*OpExtInst*/ | GetParam().extInstLength << 16, 4 /*return type*/,
9 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode});
8 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode});
for (uint32_t operand : GetParam().extInstOperandIds) {
expected_contains.push_back(operand);
}

View File

@ -34,8 +34,10 @@
namespace {
using spvtest::MakeInstruction;
using spvtest::TextToBinaryTest;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::StrEq;
@ -66,27 +68,34 @@ TEST_F(TextToBinaryTest, ImmediateIntOperand) {
using ImmediateIntTest = TextToBinaryTest;
TEST_F(ImmediateIntTest, AnyWordInSimpleStatement) {
const SpirvVector original =
CompileSuccessfully("OpConstant %1 %2 123", kCAF);
// TODO(deki): uncomment assertions below and make them pass.
EXPECT_EQ(original, CompileSuccessfully("!0x0004002B %1 %2 123", kCAF));
EXPECT_EQ(original, CompileSuccessfully("OpConstant !1 %2 123", kCAF));
EXPECT_EQ(original, CompileSuccessfully("OpConstant %1 !2 123", kCAF));
EXPECT_EQ(original, CompileSuccessfully("OpConstant %1 %2 !123", kCAF));
EXPECT_THAT(CompiledInstructions("!0x0004002B %a %b 123", kCAF),
Eq(MakeInstruction(spv::OpConstant, {1, 2, 123})));
EXPECT_THAT(CompiledInstructions("OpConstant !1 %b 123", kCAF),
Eq(MakeInstruction(spv::OpConstant, {1, 1, 123})));
EXPECT_THAT(CompiledInstructions("OpConstant %1 !2 123", kCAF),
Eq(MakeInstruction(spv::OpConstant, {1, 2, 123})));
EXPECT_THAT(CompiledInstructions("OpConstant %a %b !123", kCAF),
Eq(MakeInstruction(spv::OpConstant, {1, 2, 123})));
// EXPECT_EQ(original, CompileSuccessfully("!0x0004002B %1 !2 123", kCAF));
EXPECT_EQ(original, CompileSuccessfully("OpConstant !1 %2 !123", kCAF));
EXPECT_THAT(CompiledInstructions("OpConstant !1 %b !123", kCAF),
Eq(MakeInstruction(spv::OpConstant, {1, 1, 123})));
// EXPECT_EQ(original, CompileSuccessfully("!0x0004002B !1 !2 !123", kCAF));
}
TEST_F(ImmediateIntTest, AnyWordAfterEqualsAndOpCode) {
const SpirvVector original =
CompileSuccessfully("%2 = OpArrayLength %12 %1 123");
EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength !12 %1 123"));
EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength %12 !1 123"));
EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength %12 %1 !123"));
EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength %12 !1 !123"));
EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength !12 !1 123"));
EXPECT_EQ(original, CompileSuccessfully("%2 = OpArrayLength !12 !1 !123"));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 %c 123"),
Eq(MakeInstruction(spv::OpArrayLength, {2, 1, 2, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 123"),
Eq(MakeInstruction(spv::OpArrayLength, {1, 2, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b %c !123"),
Eq(MakeInstruction(spv::OpArrayLength, {1, 2, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 !123"),
Eq(MakeInstruction(spv::OpArrayLength, {1, 2, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 123"),
Eq(MakeInstruction(spv::OpArrayLength, {2, 1, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 !123"),
Eq(MakeInstruction(spv::OpArrayLength, {2, 1, 3, 123})));
}
TEST_F(ImmediateIntTest, ResultIdInAssignment) {
@ -103,19 +112,19 @@ TEST_F(ImmediateIntTest, OpCodeInAssignment) {
// Literal integers after !<integer> are handled correctly.
TEST_F(ImmediateIntTest, IntegerFollowingImmediate) {
const SpirvVector original = CompileSuccessfully(
const SpirvVector original = CompiledInstructions(
"OpTypeInt %1 8 1", kCAF);
// TODO(deki): uncomment assertions below and make them pass.
// EXPECT_EQ(original, CompileSuccessfully("!0x00040015 1 8 1", kCAF));
EXPECT_EQ(original, CompileSuccessfully("OpTypeInt !1 8 1", kCAF));
EXPECT_EQ(original, CompiledInstructions("OpTypeInt !1 8 1", kCAF));
// 64-bit integer literal.
EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 5000000000", kCAF),
CompileSuccessfully("OpConstant %10 !1 5000000000", kCAF));
EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 5000000000", kCAF),
CompiledInstructions("OpConstant %10 !2 5000000000", kCAF));
// Negative integer.
EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 -123", kCAF),
CompileSuccessfully("OpConstant %10 !1 -123", kCAF));
EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 -123", kCAF),
CompiledInstructions("OpConstant %10 !2 -123", kCAF));
// Hex value(s).
// EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 0x12345678", kCAF),
@ -127,16 +136,16 @@ TEST_F(ImmediateIntTest, IntegerFollowingImmediate) {
// Literal floats after !<integer> are handled correctly.
TEST_F(ImmediateIntTest, FloatFollowingImmediate) {
EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 0.123", kCAF),
CompileSuccessfully("OpConstant %10 !1 0.123", kCAF));
EXPECT_EQ(CompileSuccessfully("OpConstant %10 %1 -0.5", kCAF),
CompileSuccessfully("OpConstant %10 !1 -0.5", kCAF));
EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 0.123", kCAF),
CompiledInstructions("OpConstant %10 !2 0.123", kCAF));
EXPECT_EQ(CompiledInstructions("OpConstant %10 %2 -0.5", kCAF),
CompiledInstructions("OpConstant %10 !2 -0.5", kCAF));
// 64-bit float.
EXPECT_EQ(
CompileSuccessfully(
"OpConstant %10 %1 9999999999999999999999999999999999999999.9", kCAF),
CompileSuccessfully(
"OpConstant %10 !1 9999999999999999999999999999999999999999.9",
CompiledInstructions(
"OpConstant %10 %2 9999999999999999999999999999999999999999.9", kCAF),
CompiledInstructions(
"OpConstant %10 !2 9999999999999999999999999999999999999999.9",
kCAF));
}
@ -145,17 +154,18 @@ TEST_F(ImmediateIntTest, StringFollowingImmediate) {
// Try a variety of strings, including empty and single-character.
for (std::string name : {"", "s", "longish", "really looooooooooooooooong"}) {
const SpirvVector original =
CompileSuccessfully("OpMemberName %10 4 \"" + name + "\"", kCAF);
EXPECT_EQ(original,
CompileSuccessfully("OpMemberName %10 !4 \"" + name + "\"", kCAF))
CompiledInstructions("OpMemberName %10 4 \"" + name + "\"", kCAF);
EXPECT_EQ(original, CompiledInstructions(
"OpMemberName %10 !4 \"" + name + "\"", kCAF))
<< name;
EXPECT_EQ(original,
CompileSuccessfully("OpMemberName !10 !4 \"" + name + "\"", kCAF))
CompiledInstructions("OpMemberName !1 !4 \"" + name + "\"", kCAF))
<< name;
const uint32_t wordCount = 4 + name.size() / 4;
const uint32_t firstWord = spvOpcodeMake(wordCount, spv::OpMemberName);
EXPECT_EQ(original, CompileSuccessfully("!" + std::to_string(firstWord) +
" %10 !4 \"" + name + "\"", kCAF))
EXPECT_EQ(original, CompiledInstructions("!" + std::to_string(firstWord) +
" %10 !4 \"" + name + "\"",
kCAF))
<< name;
}
}
@ -171,9 +181,9 @@ TEST_F(ImmediateIntTest, IdFollowingImmediate) {
// !<integer> after !<integer> is handled correctly.
TEST_F(ImmediateIntTest, ImmediateFollowingImmediate) {
const SpirvVector original =
CompileSuccessfully("OpTypeMatrix %11 %10 7", kCAF);
EXPECT_EQ(original, CompileSuccessfully("OpTypeMatrix %11 !10 !7", kCAF));
EXPECT_EQ(original, CompileSuccessfully("!0x00040018 %11 !10 !7", kCAF));
CompiledInstructions("OpTypeMatrix %a %b 7", kCAF);
EXPECT_EQ(original, CompiledInstructions("OpTypeMatrix %a !2 !7", kCAF));
EXPECT_EQ(original, CompiledInstructions("!0x00040018 %a !2 !7", kCAF));
}
TEST_F(ImmediateIntTest, InvalidStatement) {
@ -186,7 +196,7 @@ TEST_F(ImmediateIntTest, InvalidStatementBetweenValidOnes) {
EXPECT_THAT(Subvector(CompileSuccessfully(
"OpTypeFloat %10 32 !5 !6 !7 OpEmitVertex", kCAF),
kFirstInstruction),
ElementsAre(spvOpcodeMake(3, spv::OpTypeFloat), 10, 32, 5, 6, 7,
ElementsAre(spvOpcodeMake(3, spv::OpTypeFloat), 1, 32, 5, 6, 7,
spvOpcodeMake(1, spv::OpEmitVertex)));
}
@ -224,11 +234,11 @@ OpCopyMemorySized %3 %4 %1
TEST_F(ImmediateIntTest, NextAssignmentRecognized) {
const SpirvVector original = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 None
%4 = OpFunctionCall %10 %3 123
%4 = OpFunctionCall %10 %3 %123
)");
const SpirvVector alternate = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 !0
%4 = OpFunctionCall %10 %3 123
%4 = OpFunctionCall %10 %3 %123
)");
EXPECT_EQ(original, alternate);
}

View File

@ -25,6 +25,9 @@
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include "UnitSPIRV.h"
#include "TestFixture.h"
#include <vector>
namespace {
@ -72,4 +75,58 @@ TEST(NamedId, Default) {
spvBinaryDestroy(binary);
}
struct IdCheckCase {
std::string id;
bool valid;
};
using IdValidityTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<IdCheckCase>>;
TEST_P(IdValidityTest, IdTypes) {
std::string input = GetParam().id + " = OpTypeVoid";
SetText(input);
if (GetParam().valid) {
CompileSuccessfully(input);
} else {
CompileFailure(input);
}
}
INSTANTIATE_TEST_CASE_P(
ValidAndInvalidIds, IdValidityTest,
::testing::ValuesIn(std::vector<IdCheckCase>({{"%1", true},
{"%2abc", true},
{"%3Def", true},
{"%4GHI", true},
{"%5_j_k", true},
{"%6J_M", true},
{"%n", true},
{"%O", true},
{"%p7", true},
{"%Q8", true},
{"%R_S", true},
{"%T_10_U", true},
{"%V_11", true},
{"%W_X_13", true},
{"%_A", true},
{"%_", true},
{"%__", true},
{"%A_", true},
{"%_A_", true},
{"%@", false},
{"%!", false},
{"%ABC!", false},
{"%__A__@", false},
{"%%", false},
{"%-", false},
{"%foo_@_bar", false},
{"%", false},
{"5", false},
{"32", false},
{"foo", false},
{"a%bar", false}})));
} // anonymous namespace

View File

@ -129,8 +129,9 @@ class TextToBinaryTestBase : public T {
// 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) {
const SpirvVector code = CompileSuccessfully(text);
SpirvVector CompiledInstructions(const std::string& text,
spv_assembly_syntax_format_t format) {
const SpirvVector code = CompileSuccessfully(text, format);
SpirvVector result;
// Extract just the instructions.
// If the code fails to compile, then return the empty vector.
@ -140,6 +141,13 @@ class TextToBinaryTestBase : public T {
return result;
}
// Compiles SPIR-V text with the default assembly format, 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) {
return CompiledInstructions(text, SPV_ASSEMBLY_SYNTAX_FORMAT_DEFAULT);
}
void SetText(const std::string& code) {
textString = code;
text.str = textString.c_str();

View File

@ -44,22 +44,22 @@ TEST(TextDestroy, Default) {
OpSource OpenCL 12
OpMemoryModel Physical64 OpenCL
OpSourceExtension "PlaceholderExtensionName"
OpEntryPoint Kernel 0 ""
OpExecutionMode 0 LocalSizeHint 1 1 1
OpTypeVoid 1
OpTypeBool 2
OpTypeInt 3 8 0
OpTypeInt 4 8 1
OpTypeInt 5 16 0
OpTypeInt 6 16 1
OpTypeInt 7 32 0
OpTypeInt 8 32 1
OpTypeInt 9 64 0
OpTypeInt 10 64 1
OpTypeFloat 11 16
OpTypeFloat 12 32
OpTypeFloat 13 64
OpTypeVector 14 3 2
OpEntryPoint Kernel %0 ""
OpExecutionMode %0 LocalSizeHint 1 1 1
OpTypeVoid %1
OpTypeBool %2
OpTypeInt %3 8 0
OpTypeInt %4 8 1
OpTypeInt %5 16 0
OpTypeInt %6 16 1
OpTypeInt %7 32 0
OpTypeInt %8 32 1
OpTypeInt %9 64 0
OpTypeInt %10 64 1
OpTypeFloat %11 16
OpTypeFloat %12 32
OpTypeFloat %13 64
OpTypeVector %14 %3 2
)";
spv_binary binary = nullptr;

View File

@ -70,12 +70,12 @@ INSTANTIATE_TEST_CASE_P(
{"", {}},
// Test each kind, alone.
{"Bias %5", {MASK(Bias), 5}},
{"Lod %10", {MASK(Lod), 10}},
{"Grad %11 %12", {MASK(Grad), 11, 12}},
{"ConstOffset %13", {MASK(ConstOffset), 13}},
{"Offset %14", {MASK(Offset), 14}},
{"ConstOffsets %15", {MASK(ConstOffsets), 15}},
{"Sample %16", {MASK(Sample), 16}},
{"Lod %10", {MASK(Lod), 5}},
{"Grad %11 %12", {MASK(Grad), 5, 6}},
{"ConstOffset %13", {MASK(ConstOffset), 5}},
{"Offset %14", {MASK(Offset), 5}},
{"ConstOffsets %15", {MASK(ConstOffsets), 5}},
{"Sample %16", {MASK(Sample), 5}},
}));
#undef MASK
#define MASK(NAME) static_cast<uint32_t>(spv::ImageOperands##NAME##Mask)
@ -85,28 +85,28 @@ INSTANTIATE_TEST_CASE_P(
// TODO(dneto): Rev32 adds many more values, and rearranges their
// values.
// Test adjacent pairs, so we can easily debug the values when it fails.
{"Bias|Lod %10 %11", {MASK(Bias) | MASK(Lod), 10, 11}},
{"Lod|Grad %12 %13 %14", {MASK(Lod) | MASK(Grad), 12, 13, 14}},
{"Bias|Lod %10 %11", {MASK(Bias) | MASK(Lod), 5, 6}},
{"Lod|Grad %12 %13 %14", {MASK(Lod) | MASK(Grad), 5, 6, 7}},
{"Grad|ConstOffset %15 %16 %17",
{MASK(Grad) | MASK(ConstOffset), 15, 16, 17}},
{MASK(Grad) | MASK(ConstOffset), 5, 6, 7}},
{"ConstOffset|Offset %18 %19",
{MASK(ConstOffset) | MASK(Offset), 18, 19}},
{MASK(ConstOffset) | MASK(Offset), 5, 6}},
{"Offset|ConstOffsets %20 %21",
{MASK(Offset) | MASK(ConstOffsets), 20, 21}},
{MASK(Offset) | MASK(ConstOffsets), 5, 6}},
{"ConstOffsets|Sample %22 %23",
{MASK(ConstOffsets) | MASK(Sample), 22, 23}},
{MASK(ConstOffsets) | MASK(Sample), 5, 6}},
// Test all masks together.
{"Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample"
" %5 %10 %11 %12 %13 %14 %15 %16",
{MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) |
MASK(Offset) | MASK(ConstOffsets) | MASK(Sample),
5, 10, 11, 12, 13, 14, 15, 16}},
5, 6, 7, 8, 9, 10, 11, 12}},
// The same, but with mask value names reversed.
{"Sample|ConstOffsets|Offset|ConstOffset|Grad|Lod|Bias"
" %5 %10 %11 %12 %13 %14 %15 %16",
{MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) |
MASK(Offset) | MASK(ConstOffsets) | MASK(Sample),
5, 10, 11, 12, 13, 14, 15, 16}}}));
5, 6, 7, 8, 9, 10, 11, 12}}}));
#undef MASK
} // anonymous namespace

View File

@ -81,7 +81,7 @@ using StorageClassTest = spvtest::TextToBinaryTestBase<
TEST_P(StorageClassTest, AnyStorageClass) {
std::string input = "%1 = OpVariable %2 " + GetParam().name();
EXPECT_THAT(CompiledInstructions(input),
Eq(MakeInstruction(spv::OpVariable, {2, 1, GetParam().value()})));
Eq(MakeInstruction(spv::OpVariable, {1, 2, GetParam().value()})));
}
// clang-format off

View File

@ -152,7 +152,7 @@ TEST(TextToBinary, Default) {
%12 = OpTypeFloat 16
%13 = OpTypeFloat 32
%14 = OpTypeFloat 64
%15 = OpTypeVector 4 2
%15 = OpTypeVector %4 2
)";
spv_opcode_table opcodeTable;