spv_operand_type_t cleanup.

- Concrete operand types are never optional.
  Split them to make this so, e.g. add SPV_OPERAND_TYPE_IMAGE
  since there was SPV_OPERAND_TYPE_OPTIONAL_IMAGE.
  Similarly for SPV_OPERAND_TYPE_MEMORY_ACCESS.
  This entails duplicating two operand table entries.

- The above, plus some rearranging of enums, allows us to define
  first and last optional operand types, and first and last
  variable operand types.
  This lets us simplify the code for spvOperandIsOptional, and
  spvOperandIsVariable.

- Replace SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER with the
  more accurately named SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER.
  Its special characteristic is that the type of the literal
  number is determined by some previous operand in the instruction.
  This is used for literals in OpSwitch, OpConstant, and OpSpecConstant.
  This lets us refactor operand parsing cases in the assembler.

- Remove the special required-thing-in-optional-tuple in favour of
  the corresponding concrete operand type:
        SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE
    --> SPV_OPERAND_TYPE_ID
        SPV_OPERAND_TYPE_INTEGER_LITERAL_IN_OPTIONAL_TUPLE
    --> SPV_OPERAND_TYPE_INTEGER_LITERAL

- Constrain spvOpeandTypeStr to only have to work for non-variable
  operand types.  Add a test for this.
This commit is contained in:
David Neto 2015-11-04 17:38:17 -05:00
parent 97e4a5d83d
commit 201caf7001
9 changed files with 218 additions and 167 deletions

View File

@ -189,30 +189,30 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
${CMAKE_CURRENT_SOURCE_DIR}/test/Comment.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/DiagnosticPrint.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/DiagnosticStream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/ExtInst.OpenCL.std.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/ExtInstGLSLstd450.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/ExtInst.OpenCL.std.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/FixWord.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/GeneratorMagicNumber.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/HexFloat.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/ImmediateInt.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/LibspirvMacros.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/NamedId.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OperandCapabilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OperandPattern.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OpcodeMake.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OpcodeRequiresCapabilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OpcodeSplit.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OpcodeTableGet.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OperandTableGet.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OperandCapabilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/Operand.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OperandPattern.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextAdvance.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextDestroy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextLiteral.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextStartsNewInst.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.Annotation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.Barrier.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.Constant.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.ControlFlow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.Debug.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.DeviceSideEnqueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/TextToBinary.Function.cpp
@ -228,6 +228,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
${CMAKE_CURRENT_SOURCE_DIR}/test/Validate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/ValidateID.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp)
target_link_libraries(UnitSPIRV SPIRV-TOOLS gmock)
else()
message(STATUS "Did not find googletest, tests will not be built."

View File

@ -159,6 +159,7 @@ typedef enum spv_endianness_t {
// is a member of an optional tuple of values. In that case the first member
// would be optional, and the subsequent members would be required.
typedef enum spv_operand_type_t {
// A sentinel value.
SPV_OPERAND_TYPE_NONE = 0,
#define FIRST_CONCRETE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_CONCRETE_TYPE = ENUM
@ -181,8 +182,12 @@ typedef enum spv_operand_type_t {
// number indicating which instruction to use from an extended instruction
// set.
SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
// A literal number that occupies one or more words in binary form.
SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER,
// A literal number whose format and size are determined by a previous operand
// in the same instruction. It's a signed integer, an unsigned integer, or a
// floating point number. It also has a specified bit width. The width
// may be larger than 32, which would require such a typed literal value to
// occupy multiple SPIR-V words.
SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
// Set 3: The literal string operand type.
SPV_OPERAND_TYPE_LITERAL_STRING,
@ -213,54 +218,67 @@ typedef enum spv_operand_type_t {
// Set 5: Operands that are a single word bitmask.
// Sometimes a set bit indicates the instruction requires still more operands.
SPV_OPERAND_TYPE_OPTIONAL_IMAGE, // SPIR-V Sec 3.14
SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, // SPIR-V Sec 3.15
SPV_OPERAND_TYPE_SELECTION_CONTROL, // SPIR-V Sec 3.22
SPV_OPERAND_TYPE_LOOP_CONTROL, // SPIR-V Sec 3.23
SPV_OPERAND_TYPE_FUNCTION_CONTROL, // SPIR-V Sec 3.24
LAST_CONCRETE(SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS), // SPIR-V Sec 3.26
SPV_OPERAND_TYPE_IMAGE, // SPIR-V Sec 3.14
SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, // SPIR-V Sec 3.15
SPV_OPERAND_TYPE_SELECTION_CONTROL, // SPIR-V Sec 3.22
SPV_OPERAND_TYPE_LOOP_CONTROL, // SPIR-V Sec 3.23
SPV_OPERAND_TYPE_FUNCTION_CONTROL, // SPIR-V Sec 3.24
LAST_CONCRETE(SPV_OPERAND_TYPE_MEMORY_ACCESS), // SPIR-V Sec 3.26
#undef FIRST_CONCRETE
#undef LAST_CONCRETE
// The remaining operand types are only used internally by the assembler.
// There are two categories:
// Optional : expands to 0 or 1 operand, like ? in regular expressions.
// Variable : expands to 0, 1 or many operands or pairs of operands.
// This is similar to * in regular expressions.
// Macros for defining bounds on optional and variable operand types.
// Any variable operand type is also optional.
#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM
#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM
#define LAST_VARIABLE(ENUM) \
ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \
SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM
// An optional operand represents zero or one logical operands.
// In an instruction definition, this may only appear at the end of the
// operand types.
SPV_OPERAND_TYPE_OPTIONAL_ID,
// An optional literal number. This can expand to either a literal integer or
// a literal floating-point number.
SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER,
FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID),
// An optional image operand type.
SPV_OPERAND_TYPE_OPTIONAL_IMAGE,
// An optional memory access type.
SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
// An optional literal integer.
SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER,
// An optional literal number, which may be either integer or floating point.
SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER,
// Like SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, but optional, and integral.
SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER,
// An optional literal string.
SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING,
// An optional execution mode.
SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE,
// A variable operand represents zero or more logical operands.
// In an instruction definition, this may only appear at the end of the
// operand types.
SPV_OPERAND_TYPE_VARIABLE_ID,
SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER,
// A sequence of zero or more pairs of (Literal integer, Id)
SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID,
// A sequence of zero or more pairs of (Id, Literal integer)
SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER,
// A sequence of zero or more execution modes
SPV_OPERAND_TYPE_VARIABLE_EXECUTION_MODE,
// An Id that is second or later in an optional tuple of operands.
// This must be present if the first operand in the tuple is present.
SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE,
// A Literal integer that is second or later in an optional tuple of operands.
// This must be present if the first operand in the tuple is present.
SPV_OPERAND_TYPE_LITERAL_INTEGER_IN_OPTIONAL_TUPLE,
// An optional context-independent value, or CIV. CIVs are tokens that we can
// assemble regardless of where they occur -- literals, IDs, immediate
// integers, etc.
SPV_OPERAND_TYPE_OPTIONAL_CIV,
// A variable operand represents zero or more logical operands.
// In an instruction definition, this may only appear at the end of the
// operand types.
FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID),
SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER,
// A sequence of zero or more pairs of (typed literal integer, Id).
// Expands to zero or more:
// (SPV_OPERAND_TYPE_TYPED_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID)
// where the literal number must always be an integer of some sort.
SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID,
// A sequence of zero or more pairs of (Id, Literal integer)
SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER,
// A sequence of zero or more execution modes
LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_EXECUTION_MODE),
// This is a sentinel value, and does not represent an operand type.
// It should come last.
SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,

View File

@ -408,7 +408,6 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
break;
case SPV_OPERAND_TYPE_ID:
case SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE:
case SPV_OPERAND_TYPE_OPTIONAL_ID:
if (!word) return diagnostic() << "Id is 0";
parsed_operand.type = SPV_OPERAND_TYPE_ID;
@ -442,9 +441,16 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_LITERAL_INTEGER_IN_OPTIONAL_TUPLE:
// TODO(dneto): Type checking and validation?
// These are regular single-word literal integer operands.
// Post-parsing validation should check the range of the parsed value.
parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER;
// It turns out they are always unsigned integers!
parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT;
parsed_operand.number_bit_width = 32;
break;
case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
if (inst->opcode == SpvOpSwitch) {
// The literal operands have the same type as the value
// referenced by the selector Id.
@ -470,23 +476,17 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
<< " is not a scalar integer";
}
} else {
// These are regular single-word literal integer operands.
// Post-parsing validation should check the range.
parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT;
parsed_operand.number_bit_width = 32;
assert(inst->opcode == SpvOpConstant ||
inst->opcode == SpvOpSpecConstant);
// The literal number type is determined by the type Id for the
// constant.
assert(inst->type_id);
if (auto error =
setNumericTypeInfoForType(&parsed_operand, inst->type_id))
return error;
}
break;
case SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER:
// TODO(dneto): Consider creating a SPV_OPERAND_TYPE_LITERAL_FLOATING
// for the floating point OpConstant/OpSpecConstant case.
assert(inst->opcode == SpvOpConstant ||
inst->opcode == SpvOpSpecConstant);
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: {
// TODO(dneto): Make and use spvFixupString();
@ -547,7 +547,8 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
// A single word that is a plain enum value.
spv_operand_desc entry;
if (grammar_.lookupOperand(type, word, &entry)) {
return diagnostic() << "Invalid " << spvOperandTypeStr(type)
return diagnostic() << "Invalid "
<< spvOperandTypeStr(parsed_operand.type)
<< " operand: " << word;
}
// Prepare to accept operands to this operand, if needed.
@ -561,6 +562,13 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_SELECTION_CONTROL: {
// This operand is a mask.
// Map an optional operand type to its corresponding concrete type.
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;
// Check validity of set mask bits. Also prepare for operands for those
// masks if they have any. To get operand order correct, scan from
// MSB to LSB since we can only prepend operands to a pattern.
@ -572,9 +580,10 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
if (remaining_word & mask) {
spv_operand_desc entry;
if (grammar_.lookupOperand(type, mask, &entry)) {
return diagnostic() << "Invalid " << spvOperandTypeStr(type)
<< " operand: " << word
<< " has invalid mask component " << mask;
return diagnostic()
<< "Invalid " << spvOperandTypeStr(parsed_operand.type)
<< " operand: " << word << " has invalid mask component "
<< mask;
}
remaining_word ^= mask;
spvPrependOperandTypes(entry->operandTypes, expected_operands);
@ -605,7 +614,7 @@ spv_result_t Parser::parseOperand(spv_parsed_instruction_t* inst,
spv_result_t Parser::setNumericTypeInfoForType(
spv_parsed_operand_t* parsed_operand, uint32_t type_id) {
assert(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";

View File

@ -191,7 +191,7 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
stream_ << ext_inst->name;
} break;
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER: {
case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: {
SetRed();
// TODO(dneto): Emit values according to type.
if (operand.num_words == 1)
@ -221,7 +221,6 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
case SPV_OPERAND_TYPE_MEMORY_MODEL:
case SPV_OPERAND_TYPE_EXECUTION_MODE:
case SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE:
case SPV_OPERAND_TYPE_STORAGE_CLASS:
case SPV_OPERAND_TYPE_DIMENSIONALITY:
case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
@ -243,8 +242,8 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
case SPV_OPERAND_TYPE_LOOP_CONTROL:
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_IMAGE:
case SPV_OPERAND_TYPE_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_SELECTION_CONTROL:
EmitMaskOperand(operand.type, word);
break;

View File

@ -52,17 +52,13 @@ spv_opcode_desc_t opcodeTableEntries[] = {
#define Capability2(X, Y) Capability(X) | Capability(Y)
#define SpvCapabilityNone \
0 // Needed so Capability(None) still expands to valid syntax.
#define Instruction(Name, HasResult, HasType, NumLogicalOperands, \
NumCapabilities, CapabilityRequired, IsVariable, \
LogicalArgsList) \
{ \
#Name, SpvOp##Name, \
(NumCapabilities) ? (CapabilityRequired) : 0, 0, \
{}, /* Filled in later. Operand list, including \
result id and type id, if needed */ \
HasResult, HasType, LogicalArgsList \
} \
,
#define Instruction(Name, HasResult, HasType, NumLogicalOperands, \
NumCapabilities, CapabilityRequired, IsVariable, \
LogicalArgsList) \
{#Name, SpvOp##Name, (NumCapabilities) ? (CapabilityRequired) : 0, \
0, {}, /* Filled in later. Operand list, including \
result id and type id, if needed */ \
HasResult, HasType, LogicalArgsList},
#include "opcode.inc"
#undef EmptyList
#undef List
@ -101,8 +97,10 @@ spv_operand_type_t convertOperandClassToType(SpvOp opcode,
break;
}
} else if (operandClass == OperandVariableLiterals) {
if (opcode == SpvOpConstant || opcode == SpvOpSpecConstant)
return SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER;
if (opcode == SpvOpConstant || opcode == SpvOpSpecConstant) {
// The number type is determined by the type Id operand.
return SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER;
}
}
switch (operandClass) {
@ -384,7 +382,7 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
}
const char* spvOpcodeString(const SpvOp opcode) {
// Use the syntax table so it's sure to be complete.
// Use the syntax table so it's sure to be complete.
#define Instruction(Name, ...) \
case SpvOp##Name: \
return #Name;

View File

@ -1053,7 +1053,10 @@ static const spv_operand_desc_group_t opcodeEntryTypes[] = {
{SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE,
sizeof(imageChannelDataTypeEntries) / sizeof(spv_operand_desc_t),
imageChannelDataTypeEntries},
{SPV_OPERAND_TYPE_OPTIONAL_IMAGE,
{SPV_OPERAND_TYPE_IMAGE,
sizeof(imageOperandEntries) / sizeof(spv_operand_desc_t),
imageOperandEntries},
{SPV_OPERAND_TYPE_OPTIONAL_IMAGE, // Same as *_IMAGE
sizeof(imageOperandEntries) / sizeof(spv_operand_desc_t),
imageOperandEntries},
{SPV_OPERAND_TYPE_FP_FAST_MATH_MODE,
@ -1087,7 +1090,10 @@ static const spv_operand_desc_group_t opcodeEntryTypes[] = {
{SPV_OPERAND_TYPE_MEMORY_SEMANTICS,
sizeof(memorySemanticsEntries) / sizeof(spv_operand_desc_t),
memorySemanticsEntries},
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
{SPV_OPERAND_TYPE_MEMORY_ACCESS,
sizeof(memoryAccessEntries) / sizeof(spv_operand_desc_t),
memoryAccessEntries},
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, // Same as *_MEMORY_ACCESS
sizeof(memoryAccessEntries) / sizeof(spv_operand_desc_t),
memoryAccessEntries},
{SPV_OPERAND_TYPE_EXECUTION_SCOPE,
@ -1170,15 +1176,23 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
switch (type) {
case SPV_OPERAND_TYPE_ID:
case SPV_OPERAND_TYPE_OPTIONAL_ID:
case SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE:
return "ID";
case SPV_OPERAND_TYPE_TYPE_ID:
return "type ID";
case SPV_OPERAND_TYPE_RESULT_ID:
return "result ID";
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
return "literal number";
case SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER:
return "multiple word literal number";
case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
return "possibly multi-word literal integer";
case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
return "possibly multi-word literal number";
case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER:
return "extension instruction number";
case SPV_OPERAND_TYPE_LITERAL_STRING:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING:
return "literal string";
case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
return "source language";
@ -1189,6 +1203,7 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
case SPV_OPERAND_TYPE_MEMORY_MODEL:
return "memory model";
case SPV_OPERAND_TYPE_EXECUTION_MODE:
case SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE:
return "execution mode";
case SPV_OPERAND_TYPE_STORAGE_CLASS:
return "storage class";
@ -1222,6 +1237,7 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
return "function control";
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
return "memory semantics";
case SPV_OPERAND_TYPE_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
return "memory access";
case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
@ -1234,8 +1250,20 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
return "kernel profiling info";
case SPV_OPERAND_TYPE_CAPABILITY:
return "capability";
case SPV_OPERAND_TYPE_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
return "image operand";
case SPV_OPERAND_TYPE_OPTIONAL_CIV:
return "context-insensitive value";
// The next values are for values returned from an instruction, not actually
// an operand. So the specific strings don't matter. But let's add them
// for completeness and ease of testing.
case SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER:
return "image channel order";
case SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE:
return "image channel data type";
case SPV_OPERAND_TYPE_NONE:
return "NONE";
default:
@ -1271,37 +1299,13 @@ void spvPrependOperandTypesForMask(const spv_operand_table operandTable,
}
bool spvOperandIsOptional(spv_operand_type_t type) {
// Variable means zero or more times.
if (spvOperandIsVariable(type)) return true;
switch (type) {
case SPV_OPERAND_TYPE_OPTIONAL_ID:
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING:
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
case SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE:
case SPV_OPERAND_TYPE_OPTIONAL_CIV:
return true;
default:
break;
}
return false;
return SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE <= type &&
type <= SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE;
}
bool spvOperandIsVariable(spv_operand_type_t type) {
switch (type) {
case SPV_OPERAND_TYPE_VARIABLE_ID:
case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID:
case SPV_OPERAND_TYPE_VARIABLE_EXECUTION_MODE:
return true;
default:
break;
}
return false;
return SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE <= type &&
type <= SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE;
}
bool spvExpandOperandSequenceOnce(spv_operand_type_t type,
@ -1315,17 +1319,17 @@ bool spvExpandOperandSequenceOnce(spv_operand_type_t type,
{SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, type});
return true;
case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID:
// Represents Zero or more (Literal number, Id) pairs.
// Represents Zero or more (Literal number, Id) pairs,
// where the literal number must be a scalar integer.
pattern->insert(pattern->begin(),
{SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER,
SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE, type});
{SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER,
SPV_OPERAND_TYPE_ID, type});
return true;
case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER:
// Represents Zero or more (Id, Literal number) pairs.
pattern->insert(
pattern->begin(),
{SPV_OPERAND_TYPE_OPTIONAL_ID,
SPV_OPERAND_TYPE_LITERAL_INTEGER_IN_OPTIONAL_TUPLE, type});
pattern->insert(pattern->begin(),
{SPV_OPERAND_TYPE_OPTIONAL_ID,
SPV_OPERAND_TYPE_LITERAL_INTEGER, type});
return true;
case SPV_OPERAND_TYPE_VARIABLE_EXECUTION_MODE:
pattern->insert(pattern->begin(),

View File

@ -68,7 +68,7 @@ spv_result_t spvOperandTableValueLookup(const spv_operand_table table,
const uint32_t value,
spv_operand_desc* pEntry);
/// @brief Get the name string of the operand type
/// @brief Get the name string of the non-variable operand type
///
/// @param type the type of the operand
///

View File

@ -33,8 +33,8 @@
#include <cstdlib>
#include <cstring>
#include <memory>
#include <string>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
@ -216,13 +216,12 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
spvOperandIsOptional(type) ? SPV_FAILED_MATCH : SPV_ERROR_INVALID_TEXT;
switch (type) {
case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
case SPV_OPERAND_TYPE_ID:
case SPV_OPERAND_TYPE_TYPE_ID:
case SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE:
case SPV_OPERAND_TYPE_OPTIONAL_ID:
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
case SPV_OPERAND_TYPE_RESULT_ID: {
case SPV_OPERAND_TYPE_RESULT_ID:
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
case SPV_OPERAND_TYPE_SCOPE_ID:
case SPV_OPERAND_TYPE_OPTIONAL_ID: {
if ('%' == textValue[0]) {
textValue++;
} else {
@ -235,6 +234,7 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
if (type == SPV_OPERAND_TYPE_TYPE_ID) pInst->resultTypeId = id;
spvInstructionAddWord(pInst, id);
} break;
case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
// The assembler accepts the symbolic name for an extended instruction,
// and emits its corresponding number.
@ -250,52 +250,67 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
return SPV_SUCCESS;
} break;
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER:
case SPV_OPERAND_TYPE_LITERAL_INTEGER_IN_OPTIONAL_TUPLE:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: {
libspirv::IdType expected_type = libspirv::kUnknownType;
if (type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER) {
// From now, it's safe to assume that the current operand is an unsigned
// integer, apart from the special cases handled below.
expected_type = {32, false, libspirv::IdTypeClass::kScalarIntegerType};
// The encoding for OpConstant, OpSpecConstant and OpSwitch all
// depend on either their own result-id or the result-id of
// one of their parameters.
if (SpvOpConstant == pInst->opcode ||
SpvOpSpecConstant == pInst->opcode) {
// Special cases for encoding possibly non-32-bit literals here.
expected_type =
context->getTypeOfTypeGeneratingValue(pInst->resultTypeId);
if (!libspirv::isScalarFloating(expected_type) &&
!libspirv::isScalarIntegral(expected_type)) {
spv_opcode_desc d;
const char* opcode_name = "opcode";
if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) {
opcode_name = d->name;
}
return context->diagnostic()
<< "Type for " << opcode_name
<< " must be a scalar floating point or integer type";
}
} else if (pInst->opcode == SpvOpSwitch) {
// We need to know the type of the selector.
expected_type = context->getTypeOfValueInstruction(pInst->words[1]);
if (!libspirv::isScalarIntegral(expected_type)) {
context->diagnostic()
<< "The selector operand for OpSwitch must be the result"
" of an instruction that generates an integer scalar";
return SPV_ERROR_INVALID_TEXT;
}
}
}
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: {
// The current operand is an *unsigned* 32-bit integer.
// That's just how the grammar works.
libspirv::IdType expected_type = {
32, false, libspirv::IdTypeClass::kScalarIntegerType};
if (auto error = context->binaryEncodeNumericLiteral(
textValue, error_code_for_literals, expected_type, pInst)) {
return error;
}
} break;
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
// This is a context-independent literal number which can be a 32-bit
// number of floating point value.
if (auto error = context->binaryEncodeNumericLiteral(
textValue, error_code_for_literals, libspirv::kUnknownType,
pInst)) {
return error;
}
break;
case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: {
libspirv::IdType expected_type = libspirv::kUnknownType;
// The encoding for OpConstant, OpSpecConstant and OpSwitch all
// depend on either their own result-id or the result-id of
// one of their parameters.
if (SpvOpConstant == pInst->opcode ||
SpvOpSpecConstant == pInst->opcode) {
// The type of the literal is determined by the type Id of the
// instruction.
expected_type =
context->getTypeOfTypeGeneratingValue(pInst->resultTypeId);
if (!libspirv::isScalarFloating(expected_type) &&
!libspirv::isScalarIntegral(expected_type)) {
spv_opcode_desc d;
const char* opcode_name = "opcode";
if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) {
opcode_name = d->name;
}
return context->diagnostic()
<< "Type for " << opcode_name
<< " must be a scalar floating point or integer type";
}
} else if (pInst->opcode == SpvOpSwitch) {
// The type of the literal is the same as the type of the selector.
expected_type = context->getTypeOfValueInstruction(pInst->words[1]);
if (!libspirv::isScalarIntegral(expected_type)) {
return context->diagnostic()
<< "The selector operand for OpSwitch must be the result"
" of an instruction that generates an integer scalar";
}
}
if (auto error = context->binaryEncodeNumericLiteral(
textValue, error_code_for_literals, expected_type, pInst)) {
return error;
}
} break;
case SPV_OPERAND_TYPE_LITERAL_STRING:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
spv_literal_t literal = {};
@ -641,8 +656,7 @@ spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar,
spv_diagnostic* pDiagnostic) {
if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
libspirv::AssemblyContext context(text, pDiagnostic);
if (!text->str)
return context.diagnostic() << "Missing assembly text.";
if (!text->str) return context.diagnostic() << "Missing assembly text.";
if (!grammar.isValid()) {
return SPV_ERROR_INVALID_TABLE;
@ -685,8 +699,7 @@ spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar,
currentIndex += inst.words.size();
}
if (auto error = SetHeader(data, context.getBound()))
return error;
if (auto error = SetHeader(data, context.getBound())) return error;
spv_binary binary = new spv_binary_t();
if (!binary) {

View File

@ -39,4 +39,13 @@ TEST(OperandTableGet, InvalidPointerTable) {
ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOperandTableGet(nullptr));
}
TEST(OperandString, AllAreDefinedExceptVariable) {
EXPECT_EQ(0, SPV_OPERAND_TYPE_NONE); // None has no string, so don't test it.
// Start testing at enum with value 1, skipping None.
for (int i = 1; i < int(SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE); i++) {
EXPECT_NE(nullptr, spvOperandTypeStr(static_cast<spv_operand_type_t>(i)))
<< " Operand type " << i;
}
}
} // anonymous namespace