Handle operands when OpCode is !<integer>.

This commit is contained in:
Dejan Mircevski 2015-09-11 00:43:11 -04:00 committed by David Neto
parent e994e2e283
commit f79519c926
2 changed files with 111 additions and 31 deletions

View File

@ -389,13 +389,17 @@ spv_result_t spvTextEncodeOperand(
if (spvTextIsNamedId(textValue)) {
id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
} else {
spvCheck(spvTextToUInt32(textValue, &id),
DIAGNOSTIC
<< "Invalid "
<< ((type == SPV_OPERAND_TYPE_RESULT_ID) ? "result " : "")
<< "ID '" << textValue << "'.";
return (spvOperandIsOptional(type) ? SPV_FAILED_MATCH
: SPV_ERROR_INVALID_TEXT));
if (spvTextToUInt32(textValue, &id) != SPV_SUCCESS) {
if (spvOperandIsOptional(type)) {
return SPV_FAILED_MATCH;
} else {
DIAGNOSTIC << "Invalid "
<< ((type == SPV_OPERAND_TYPE_RESULT_ID) ? "result "
: "")
<< "ID '" << textValue << "'.";
return SPV_ERROR_INVALID_TEXT;
}
}
}
pInst->words[pInst->wordCount++] = id;
if (*pBound <= id) {
@ -431,10 +435,14 @@ spv_result_t spvTextEncodeOperand(
case SPV_OPERAND_TYPE_LITERAL_IN_OPTIONAL_TUPLE:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL: {
spv_literal_t literal = {};
// TODO(dneto): Is return code different for optional operands?
spvCheck(spvTextToLiteral(textValue, &literal),
DIAGNOSTIC << "Invalid literal '" << textValue << "'.";
return SPV_ERROR_INVALID_TEXT);
if (spvTextToLiteral(textValue, &literal) != SPV_SUCCESS) {
if (spvOperandIsOptional(type)) {
return SPV_FAILED_MATCH;
} else {
DIAGNOSTIC << "Invalid literal '" << textValue << "'.";
return SPV_ERROR_INVALID_TEXT;
}
}
switch (literal.type) {
// We do not have to print diagnostics here because spvBinaryEncode*
// prints diagnostic messages on failure.
@ -521,37 +529,100 @@ spv_result_t spvTextEncodeOperand(
return SPV_SUCCESS;
}
namespace {
/// Encodes an instruction started by !<integer> at the given position in text.
///
/// Puts the encoded words into *pInst. If successful, moves position past the
/// instruction and returns SPV_SUCCESS. Otherwise, returns an error code and
/// 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,
uint32_t *pBound, spv_instruction_t *pInst, spv_position position,
spv_diagnostic *pDiagnostic) {
std::string firstWord;
spv_position_t nextPosition = {};
auto error = spvTextWordGet(text, position, firstWord, &nextPosition);
spvCheck(error, DIAGNOSTIC << "Internal Error"; return error);
assert(firstWord[0] == '!');
const char *begin = firstWord.data() + 1;
char *end = nullptr;
uint32_t immediateInt = strtoul(begin, &end, 0);
spvCheck((begin + firstWord.size() - 1) != end,
DIAGNOSTIC << "Invalid immediate integer '" << firstWord << "'.";
return SPV_ERROR_INVALID_TEXT);
position->column += firstWord.size();
position->index += firstWord.size();
pInst->words[0] = immediateInt;
pInst->wordCount = 1;
while (spvTextAdvance(text, position) != SPV_END_OF_STREAM) {
// A beginning of a new instruction means we're done.
if (spvTextIsStartOfNewInst(text, position)) return SPV_SUCCESS;
// Otherwise, there must be an operand that's either a literal, an ID, or
// an immediate.
std::string operandValue;
if ((error = spvTextWordGet(text, position, operandValue, &nextPosition))) {
DIAGNOSTIC << "Internal Error";
return error;
}
// Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be
// expanded.
spv_operand_pattern_t dummyExpectedOperands;
error = spvTextEncodeOperand(
SPV_OPERAND_TYPE_OPTIONAL_LITERAL, operandValue.c_str(), operandTable,
extInstTable, namedIdTable, pInst, &dummyExpectedOperands, pBound,
position, pDiagnostic);
if (error == SPV_FAILED_MATCH) {
// It's not a literal -- is it an ID?
error = spvTextEncodeOperand(
SPV_OPERAND_TYPE_OPTIONAL_ID, operandValue.c_str(), operandTable,
extInstTable, namedIdTable, pInst, &dummyExpectedOperands, pBound,
position, pDiagnostic);
if (error) {
DIAGNOSTIC << "Invalid word following " << firstWord << ": "
<< operandValue;
}
}
spvCheck(error, return error);
*position = nextPosition;
}
return SPV_SUCCESS;
}
} // anonymous namespace
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 position,
spv_diagnostic *pDiagnostic) {
// Check for !<integer> first.
if ('!' == text->str[position->index]) {
return encodeInstructionStartingWithImmediate(
text, operandTable, extInstTable, namedIdTable, pBound, pInst, position,
pDiagnostic);
}
// An assembly instruction has two possible formats:
// 1(CAF): <opcode> <operand>..., e.g., "OpTypeVoid %void".
// 2(AAF): <result-id> = <opcode> <operand>..., e.g., "%void = OpTypeVoid".
if ('!' == text->str[position->index]) {
return encodeInstructionStartingWithImmediate(
text, operandTable, extInstTable, namedIdTable, pBound, pInst, position,
pDiagnostic);
}
std::string firstWord;
spv_position_t nextPosition = {};
spv_result_t error = spvTextWordGet(text, position, firstWord, &nextPosition);
spvCheck(error, DIAGNOSTIC << "Internal Error"; return error);
// NOTE: Handle insertion of an immediate integer into the binary stream
if ('!' == text->str[position->index]) {
const char *begin = firstWord.data() + 1;
char *end = nullptr;
uint32_t immediateInt = strtoul(begin, &end, 0);
size_t size = firstWord.size() - 1;
spvCheck(size != (size_t)(end - begin),
DIAGNOSTIC << "Invalid immediate integer '" << firstWord << "'.";
return SPV_ERROR_INVALID_TEXT);
position->column += firstWord.size();
position->index += firstWord.size();
pInst->words[0] = immediateInt;
pInst->wordCount = 1;
return SPV_SUCCESS;
}
std::string opcodeName;
std::string result_id;
spv_position_t result_id_position = {};

View File

@ -33,8 +33,9 @@
namespace {
using ::testing::HasSubstr;
using ::testing::ElementsAre;
using ::testing::HasSubstr;
using ::testing::StrEq;
using test_fixture::TextToBinaryTest;
TEST_F(TextToBinaryTest, ImmediateIntOpCode) {
@ -64,7 +65,7 @@ using ImmediateIntTest = TextToBinaryTest;
TEST_F(ImmediateIntTest, AnyWordInSimpleStatement) {
const SpirvVector original = CompileCAFSuccessfully("OpConstant %1 %2 123");
// TODO(deki): uncomment assertions below and make them pass.
// EXPECT_EQ(original, CompileCAFSuccessfully("!0x0004002B %1 %2 123"));
EXPECT_EQ(original, CompileCAFSuccessfully("!0x0004002B %1 %2 123"));
EXPECT_EQ(original, CompileCAFSuccessfully("OpConstant !1 %2 123"));
// EXPECT_EQ(original, CompileCAFSuccessfully("OpConstant %1 !2 123"));
EXPECT_EQ(original, CompileCAFSuccessfully("OpConstant %1 %2 !123"));
@ -152,8 +153,7 @@ TEST_F(ImmediateIntTest, IdFollowingImmediate) {
TEST_F(ImmediateIntTest, ImmediateFollowingImmediate) {
const SpirvVector original = CompileCAFSuccessfully("OpTypeMatrix %11 %10 7");
EXPECT_EQ(original, CompileCAFSuccessfully("OpTypeMatrix %11 !10 !7"));
// TODO(deki): uncomment assertions below and make them pass.
// EXPECT_EQ(original, CompileCAFSuccessfully("!0x00040018 %11 !10 !7"));
EXPECT_EQ(original, CompileCAFSuccessfully("!0x00040018 %11 !10 !7"));
}
TEST_F(ImmediateIntTest, InvalidStatement) {
@ -246,4 +246,13 @@ TEST_F(ImmediateIntTest, ForbiddenOperands) {
HasSubstr("random_bareword"));
}
TEST_F(ImmediateIntTest, NotInteger) {
EXPECT_THAT(CompileFailure("!abc"),
StrEq("Invalid immediate integer '!abc'."));
EXPECT_THAT(CompileFailure("!12.3"),
StrEq("Invalid immediate integer '!12.3'."));
EXPECT_THAT(CompileFailure("!12K"),
StrEq("Invalid immediate integer '!12K'."));
}
} // anonymous namespace