set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/include/libspirv/libspirv.h
- ${CMAKE_CURRENT_SOURCE_DIR}/source/bitwisecast.h
${CMAKE_CURRENT_SOURCE_DIR}/source/binary.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/source/bitwisecast.h
${CMAKE_CURRENT_SOURCE_DIR}/source/diagnostic.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/source/ext_inst.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/source/instruction.h
${CMAKE_CURRENT_SOURCE_DIR}/source/opcode.h
${CMAKE_CURRENT_SOURCE_DIR}/source/operand.h
${CMAKE_CURRENT_SOURCE_DIR}/source/print.h
${CMAKE_CURRENT_SOURCE_DIR}/test/TestFixture.h
${CMAKE_CURRENT_SOURCE_DIR}/test/UnitSPIRV.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/AssemblyContext.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/AssemblyFormat.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/BinaryDestroy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/BinaryEndianness.cpp
// Universal limits
+// SPIR-V 1.0 limits
+#define SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX 0xffff
+
// NOTE: These are set to the minimum maximum values
+// TODO(dneto): Check these.
+
+// libspirv limits.
#define SPV_LIMIT_LITERAL_NAME_MAX 0x00000400
#define SPV_LIMIT_LITERAL_STRING_MAX 0x00010000
-#define SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX 0x00000108
#define SPV_LIMIT_RESULT_ID_BOUND 0x00400000
#define SPV_LIMIT_CONTROL_FLOW_NEST_DEPTH 0x00000400
#define SPV_LIMIT_GLOBAL_VARIABLES_MAX 0x00010000
uint64_t length;
} spv_text_t;
-// Describes an instruction.
-//
-// The wordCount and words[0..wordCount-1] always contain valid data.
-//
-// Normally, both opcode and extInstType contain valid data.
-// However, when the assembler parses !<number> as the first word in
-// an instruction, then opcode and extInstType are invalid, and
-// wordCount == 1
-// words[0] == <number>
-typedef struct spv_instruction_t {
- uint16_t wordCount;
- Op opcode;
- spv_ext_inst_type_t extInstType;
- uint32_t words[SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX];
-} spv_instruction_t;
-
typedef struct spv_position_t {
uint64_t line;
uint64_t column;
#include "binary.h"
#include "diagnostic.h"
#include "ext_inst.h"
+#include "instruction.h"
#include "opcode.h"
#include "operand.h"
}
if (spvBinaryDecodeOperand(
- opcodeEntry->opcode, type, pInst->words + index, numWords, endian,
+ opcodeEntry->opcode, type, &pInst->words[index], numWords, endian,
options, operandTable, extInstTable, &expectedOperands,
&pInst->extInstType,
(isAssigmentFormat && !currentIsResultId ? no_result_id_stream
#define _LIBSPIRV_UTIL_BINARY_H_
#include <libspirv/libspirv.h>
+#include "instruction.h"
#include "operand.h"
#include "print.h"
--- /dev/null
+// Copyright (c) 2015 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+// https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef _LIBSPIRV_UTIL_INSTRUCTION_H_
+#define _LIBSPIRV_UTIL_INSTRUCTION_H_
+
+#include <cstdint>
+#include <vector>
+
+#include <headers/spirv.hpp>
+
+// Describes an instruction.
+struct spv_instruction_t {
+ // Normally, both opcode and extInstType contain valid data.
+ // However, when the assembler parses !<number> as the first word in
+ // an instruction and opcode and extInstType are invalid.
+ Op opcode;
+ spv_ext_inst_type_t extInstType;
+
+ // The instruction, as a sequence of 32-bit words.
+ // For a regular instruction the opcode and word count are combined
+ // in words[0], as described in the SPIR-V spec.
+ // Otherwise, the first token was !<number>, and that number appears
+ // in words[0]. Subsequent elements are the result of parsing
+ // tokens in the alternate parsing mode as described in syntax.md.
+ std::vector<uint32_t> words;
+};
+
+// Appends a word to an instruction, without checking for overflow.
+inline void spvInstructionAddWord(spv_instruction_t* inst, uint32_t value) {
+ inst->words.push_back(value);
+}
+
+#endif // _LIBSPIRV_UTIL_INSTRUCTION_H_
#include <libspirv/libspirv.h>
#include "binary.h"
+#include "instruction.h"
#include "opcode.h"
#include <assert.h>
const uint16_t wordCount, const spv_endianness_t endian,
spv_instruction_t *pInst) {
pInst->opcode = opcode;
- pInst->wordCount = wordCount;
+ pInst->words.resize(wordCount);
for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
if (!wordIndex) {
#ifndef _LIBSPIRV_UTIL_OPCODE_H_
#define _LIBSPIRV_UTIL_OPCODE_H_
+#include "instruction.h"
#include <libspirv/libspirv.h>
// Functions
#include "bitwisecast.h"
#include "diagnostic.h"
#include "ext_inst.h"
+#include "instruction.h"
#include <libspirv/libspirv.h>
#include "opcode.h"
#include "operand.h"
return SPV_ERROR_INVALID_TEXT;
}
const uint32_t id = context->spvNamedIdAssignOrGet(textValue);
- pInst->words[pInst->wordCount++] = id;
+ spvInstructionAddWord(pInst, id);
} break;
case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
// NOTE: Special case for extension instruction lookup
<< textValue << "'.";
return SPV_ERROR_INVALID_TEXT;
}
- pInst->words[pInst->wordCount++] = extInst->ext_inst;
+ spvInstructionAddWord(pInst, extInst->ext_inst);
// Prepare to parse the operands for the extended instructions.
spvPrependOperandTypes(extInst->operandTypes, pExpectedOperands);
}
pInst->opcode = opcodeEntry->opcode;
context->setPosition(nextPosition);
- pInst->wordCount++;
+ // Reserve the first word for the instruction.
+ spvInstructionAddWord(pInst, 0);
// Maintains the ordered list of expected operand types.
// For many instructions we only need the {numTypes, operandTypes}
}
}
- pInst->words[0] = spvOpcodeMake(pInst->wordCount, opcodeEntry->opcode);
+ if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
+ context->diagnostic() << "Instruction too long: " << pInst->words.size()
+ << " words, but the limit is "
+ << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX;
+ return SPV_ERROR_INVALID_TEXT;
+ }
+
+ pInst->words[0] = spvOpcodeMake(pInst->words.size(), opcodeEntry->opcode);
return SPV_SUCCESS;
}
spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
while (context.hasText()) {
- spv_instruction_t inst = {};
+ instructions.push_back({});
+ spv_instruction_t& inst = instructions.back();
inst.extInstType = extInstType;
if (spvTextEncodeOpcode(grammar, &context, format, &inst)) {
}
extInstType = inst.extInstType;
- instructions.push_back(inst);
-
if (context.advance()) break;
}
size_t totalSize = SPV_INDEX_INSTRUCTION;
for (auto& inst : instructions) {
- totalSize += inst.wordCount;
+ totalSize += inst.words.size();
}
uint32_t* data = new uint32_t[totalSize];
if (!data) return SPV_ERROR_OUT_OF_MEMORY;
uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
for (auto& inst : instructions) {
- memcpy(data + currentIndex, inst.words, sizeof(uint32_t) * inst.wordCount);
- currentIndex += inst.wordCount;
+ memcpy(data + currentIndex, inst.words.data(), sizeof(uint32_t) * inst.words.size());
+ currentIndex += inst.words.size();
}
spv_binary binary = new spv_binary_t();
uint64_t u64;
float f;
double d;
- char str[SPV_LIMIT_LITERAL_STRING_MAX];
+ // Allow room for the null terminator, and two surrounding quotes.
+ char str[SPV_LIMIT_LITERAL_STRING_MAX + 3];
} value;
} spv_literal_t;
#include "binary.h"
#include "ext_inst.h"
+#include "instruction.h"
#include "opcode.h"
#include "text.h"
spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
spv_instruction_t *pInst) {
- if (pInst->wordCount + 1 > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
- diagnostic() << "Instruction word count '"
- << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "' exceeded.";
- return SPV_ERROR_INVALID_TEXT;
- }
-
- pInst->words[pInst->wordCount++] = (uint32_t)value;
+ spvInstructionAddWord(pInst, value);
return SPV_SUCCESS;
}
spv_result_t AssemblyContext::binaryEncodeString(
const char *value, spv_instruction_t *pInst) {
- size_t length = strlen(value);
- size_t wordCount = (length / 4) + 1;
- if ((sizeof(uint32_t) * pInst->wordCount) + length >
- sizeof(uint32_t) * SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
+ const size_t length = strlen(value);
+ const size_t wordCount = (length / 4) + 1;
+ const size_t oldWordCount = pInst->words.size();
+ const size_t newWordCount = oldWordCount + wordCount;
+
+ // TODO(dneto): We can just defer this check until later.
+ if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
diagnostic() << "Instruction word count '"
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "'exceeded.";
return SPV_ERROR_INVALID_TEXT;
}
- char *dest = (char *)&pInst->words[pInst->wordCount];
+ pInst->words.resize(newWordCount);
+
+ // Make sure all the bytes in the last word are 0, in case we only
+ // write a partial word at the end.
+ pInst->words.back() = 0;
+
+ char *dest = (char *)&pInst->words[oldWordCount];
strncpy(dest, value, length);
- pInst->wordCount += (uint16_t)wordCount;
return SPV_SUCCESS;
}
#include <unordered_map>
#include "diagnostic.h"
+#include "instruction.h"
#include "operand.h"
namespace libspirv {
// Structures
-
// This is a lattice for tracking types.
enum class IdTypeClass {
kBottom, // We have no information yet.
#include <libspirv/libspirv.h>
#include "binary.h"
#include "diagnostic.h"
+#include "instruction.h"
#include "opcode.h"
#include "operand.h"
#include "validate.h"
spv_position position,
spv_diagnostic *pDiagnostic) {
for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
- const uint32_t *words = pInsts[instIndex].words;
+ const uint32_t *words = pInsts[instIndex].words.data();
uint16_t wordCount;
Op opcode;
spvOpcodeSplit(words[0], &wordCount, &opcode);
}
spv_operand_desc operandEntry = nullptr;
- for (uint16_t index = 1; index < pInsts[instIndex].wordCount;
+ for (uint16_t index = 1; index < pInsts[instIndex].words.size();
++index, position->index++) {
const uint32_t word = words[index];
std::vector<spv_id_info_t> idDefs;
for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
- const uint32_t *words = pInsts[instIndex].words;
+ const uint32_t *words = pInsts[instIndex].words.data();
Op opcode;
spvOpcodeSplit(words[0], nullptr, &opcode);
spv_operand_desc operandEntry = nullptr;
position->index++; // NOTE: Account for Opcode word
- for (uint16_t index = 1; index < pInsts[instIndex].wordCount;
+ for (uint16_t index = 1; index < pInsts[instIndex].words.size();
++index, position->index++) {
const uint32_t word = words[index];
#ifndef _LIBSPIRV_UTIL_VALIDATE_H_
#define _LIBSPIRV_UTIL_VALIDATE_H_
+#include "instruction.h"
#include <libspirv/libspirv.h>
// Structures
#include <libspirv/libspirv.h>
#include "diagnostic.h"
+#include "instruction.h"
#include "opcode.h"
#include "validate.h"
return false);
auto memberIndex = 2;
auto member = inst->words[memberIndex];
- auto memberCount = (uint32_t)(type->second.inst->wordCount - 2);
+ auto memberCount = (uint32_t)(type->second.inst->words.size() - 2);
spvCheck(memberCount <= member, DIAG(memberIndex)
<< "OpMemberName Member <id> '"
<< inst->words[memberIndex]
return false);
auto memberIndex = 2;
auto member = inst->words[memberIndex];
- auto memberCount = (uint32_t)(structType->second.inst->wordCount - 2);
+ auto memberCount = (uint32_t)(structType->second.inst->words.size() - 2);
spvCheck(memberCount < member, DIAG(memberIndex)
<< "OpMemberDecorate Structure type <id> '"
<< inst->words[memberIndex]
<< inst->words[decorationGroupIndex]
<< "' is not a decoration group.";
return false);
- for (uint64_t targetIndex = 2; targetIndex < inst->wordCount; ++targetIndex) {
+ for (uint64_t targetIndex = 2; targetIndex < inst->words.size(); ++targetIndex) {
auto target = find(inst->words[targetIndex]);
spvCheck(!found(target), DIAG(targetIndex)
<< "OpGroupDecorate Target <id> '"
// to change
auto entryPointType = find(entryPoint->second.inst->words[4]);
spvCheck(!found(entryPointType), assert(0 && "Unreachable!"));
- spvCheck(3 != entryPointType->second.inst->wordCount,
+ spvCheck(3 != entryPointType->second.inst->words.size(),
DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
<< inst->words[entryPointIndex]
<< "'s function parameter count is not zero.";
<< inst->words[lengthIndex]
<< "' is not a constant integer type.";
return false);
- if (4 == constInst->wordCount) {
+ if (4 == constInst->words.size()) {
spvCheck(1 > constInst->words[3], DIAG(lengthIndex)
<< "OpTypeArray Length <id> '"
<< inst->words[lengthIndex]
<< "' value must be at least 1.";
return false);
- } else if (5 == constInst->wordCount) {
+ } else if (5 == constInst->words.size()) {
uint64_t value =
constInst->words[3] | ((uint64_t)constInst->words[4]) << 32;
bool signedness = constResultType->second.inst->words[3];
template <>
bool idUsage::isValid<OpTypeStruct>(const spv_instruction_t *inst,
const spv_opcode_desc) {
- for (uint64_t memberTypeIndex = 2; memberTypeIndex < inst->wordCount;
+ for (uint64_t memberTypeIndex = 2; memberTypeIndex < inst->words.size();
++memberTypeIndex) {
auto memberType = find(inst->words[memberTypeIndex]);
spvCheck(!found(memberType), DIAG(memberTypeIndex)
<< inst->words[returnTypeIndex]
<< "' is not a type.";
return false);
- for (uint64_t paramTypeIndex = 3; paramTypeIndex < inst->wordCount;
+ for (uint64_t paramTypeIndex = 3; paramTypeIndex < inst->words.size();
++paramTypeIndex) {
auto paramType = find(inst->words[paramTypeIndex]);
spvCheck(!found(paramType), DIAG(paramTypeIndex)
<< "' is not a composite type.";
return false);
- uint32_t constituentCount = inst->wordCount - 3;
+ uint32_t constituentCount = inst->words.size() - 3;
switch (resultType->second.opcode) {
case OpTypeVector: {
auto componentCount = resultType->second.inst->words[3];
spvCheck(
componentCount != constituentCount,
// TODO: Output ID's on diagnostic
- DIAG(inst->wordCount - 1)
+ DIAG(inst->words.size() - 1)
<< "OpConstantComposite Constituent <id> count does not match "
"Result Type <id> '"
<< resultType->second.id << "'s vector component count.";
return false);
auto componentType = find(resultType->second.inst->words[2]);
spvCheck(!found(componentType), assert(0 && "Unreachable!"));
- for (uint64_t constituentIndex = 3; constituentIndex < inst->wordCount;
+ for (uint64_t constituentIndex = 3; constituentIndex < inst->words.size();
constituentIndex++) {
auto constituent = find(inst->words[constituentIndex]);
spvCheck(!found(constituent), assert(0 && "Unreachable!"));
spvCheck(
columnCount != constituentCount,
// TODO: Output ID's on diagnostic
- DIAG(inst->wordCount - 1)
+ DIAG(inst->words.size() - 1)
<< "OpConstantComposite Constituent <id> count does not match "
"Result Type <id> '"
<< resultType->second.id << "'s matrix column count.";
auto componentType = find(columnType->second.inst->words[2]);
spvCheck(!found(componentType), assert(0 && "Unreachable!"));
- for (uint64_t constituentIndex = 3; constituentIndex < inst->wordCount;
+ for (uint64_t constituentIndex = 3; constituentIndex < inst->words.size();
constituentIndex++) {
auto constituent = find(inst->words[constituentIndex]);
spvCheck(!found(constituent),
auto length = find(resultType->second.inst->words[3]);
spvCheck(!found(length), assert(0 && "Unreachable!"));
spvCheck(length->second.inst->words[3] != constituentCount,
- DIAG(inst->wordCount - 1)
+ DIAG(inst->words.size() - 1)
<< "OpConstantComposite Constituent count does not match "
"Result Type <id> '"
<< resultType->second.id << "'s array length.";
return false);
- for (uint64_t constituentIndex = 3; constituentIndex < inst->wordCount;
+ for (uint64_t constituentIndex = 3; constituentIndex < inst->words.size();
constituentIndex++) {
auto constituent = find(inst->words[constituentIndex]);
spvCheck(!found(constituent),
}
} break;
case OpTypeStruct: {
- uint32_t memberCount = resultType->second.inst->wordCount - 2;
+ uint32_t memberCount = resultType->second.inst->words.size() - 2;
spvCheck(memberCount != constituentCount,
DIAG(resultTypeIndex)
<< "OpConstantComposite Constituent <id> '"
<< resultType->second.id << "'s struct member count.";
return false);
for (uint32_t constituentIndex = 3, memberIndex = 2;
- constituentIndex < inst->wordCount;
+ constituentIndex < inst->words.size();
constituentIndex++, memberIndex++) {
auto constituent = find(inst->words[constituentIndex]);
spvCheck(!found(constituent),
} break;
case OpTypeStruct: {
for (uint64_t elementIndex = 2;
- elementIndex < resultType->second.inst->wordCount; ++elementIndex) {
+ elementIndex < resultType->second.inst->words.size(); ++elementIndex) {
auto element = find(resultType->second.inst->words[elementIndex]);
spvCheck(!found(element), assert(0 && "Unreachable!"));
spvCheck(!spvOpcodeIsBasicTypeNullable(element->second.inst->opcode),
<< inst->words[resultTypeIndex]
<< "' is not a pointer type.";
return false);
- if (opcodeEntry->numTypes < inst->wordCount) {
+ if (opcodeEntry->numTypes < inst->words.size()) {
auto initialiserIndex = 4;
auto initialiser = find(inst->words[initialiserIndex]);
spvCheck(!found(initialiser), DIAG(initialiserIndex)
return false);
auto functionType = find(function->second.inst->words[4]);
spvCheck(!found(functionType), assert(0 && "Unreachable!"));
- auto functionCallArgCount = inst->wordCount - 4;
- auto functionParamCount = functionType->second.inst->wordCount - 3;
+ auto functionCallArgCount = inst->words.size() - 4;
+ auto functionParamCount = functionType->second.inst->words.size() - 3;
spvCheck(
functionParamCount != functionCallArgCount,
- DIAG(inst->wordCount - 1)
+ DIAG(inst->words.size() - 1)
<< "OpFunctionCall Function <id>'s parameter count does not match "
"the argument count.";
return false);
for (uint64_t argumentIndex = 4, paramIndex = 3;
- argumentIndex < inst->wordCount; argumentIndex++, paramIndex++) {
+ argumentIndex < inst->words.size(); argumentIndex++, paramIndex++) {
auto argument = find(inst->words[argumentIndex]);
spvCheck(!found(argument), DIAG(argumentIndex)
<< "OpFunctionCall Argument <id> '"
pIdDefs, idDefsCount, pInsts, instCount, position, pDiag);
for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
spvCheck(!idUsage.isValid(&pInsts[instIndex]), return SPV_ERROR_INVALID_ID);
- position->index += pInsts[instIndex].wordCount;
+ position->index += pInsts[instIndex].words.size();
}
return SPV_SUCCESS;
}
--- /dev/null
+// Copyright (c) 2015 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+// https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "UnitSPIRV.h"
+
+#include <vector>
+#include <gmock/gmock.h>
+
+#include "../source/instruction.h"
+
+using libspirv::AssemblyContext;
+using spvtest::AutoText;
+using spvtest::Concatenate;
+using ::testing::Eq;
+
+namespace {
+
+struct EncodeStringCase {
+ std::string str;
+ std::vector<uint32_t> initial_contents;
+};
+
+using EncodeStringTest = ::testing::TestWithParam<EncodeStringCase>;
+
+TEST_P(EncodeStringTest, Sample) {
+ AssemblyContext context(AutoText(""), nullptr);
+ spv_instruction_t inst;
+ inst.words = GetParam().initial_contents;
+ ASSERT_EQ(SPV_SUCCESS,
+ context.binaryEncodeString(GetParam().str.c_str(), &inst));
+ // We already trust MakeVector
+ EXPECT_THAT(inst.words,
+ Eq(Concatenate({GetParam().initial_contents,
+ spvtest::MakeVector(GetParam().str)})));
+}
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+ BinaryEncodeString, EncodeStringTest,
+ ::testing::ValuesIn(std::vector<EncodeStringCase>{
+ // Use cases that exercise at least one to two words,
+ // and both empty and non-empty initial contents.
+ {"", {}},
+ {"", {1,2,3}},
+ {"a", {}},
+ {"a", {4}},
+ {"ab", {4}},
+ {"abc", {}},
+ {"abc", {18}},
+ {"abcd", {}},
+ {"abcd", {22}},
+ {"abcde", {4}},
+ {"abcdef", {}},
+ {"abcdef", {99,42}},
+ {"abcdefg", {}},
+ {"abcdefg", {101}},
+ {"abcdefgh", {}},
+ {"abcdefgh", {102, 103, 104}},
+ // A very long string, encoded after an initial word.
+ // SPIR-V limits strings to 65535 characters.
+ {std::string(65535, 'a'), {1}},
+ }));
+// clang-format on
+
+} // anonymous namespace
spvDiagnosticDestroy(diagnostic);
}
EXPECT_EQ(SPV_SUCCESS, error);
+ if (!binary) return "";
spv_text decoded_text;
error = spvBinaryToText(
TEST(TextLiteral, StringTooLong) {
spv_literal_t l;
std::string too_long = std::string("\"") +
- std::string(SPV_LIMIT_LITERAL_STRING_MAX - 2, 'a') +
+ std::string(SPV_LIMIT_LITERAL_STRING_MAX + 1, 'a') +
"\"";
EXPECT_EQ(SPV_ERROR_OUT_OF_MEMORY, spvTextToLiteral(too_long.data(), &l));
}
TEST(TextLiteral, GoodLongString) {
spv_literal_t l;
- std::string unquoted(SPV_LIMIT_LITERAL_STRING_MAX - 3, 'a');
+ std::string unquoted(SPV_LIMIT_LITERAL_STRING_MAX, 'a');
std::string good_long = std::string("\"") + unquoted + "\"";
EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
CompileFailure(R"(OpSourceExtension 1000)"));
}
-// TODO(antiagainst): libspirv.h defines SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX
-// to be 0x108. Lift that limit and enable the following test.
-TEST_F(TextToBinaryTest, DISABLED_LiteralStringTooLong) {
+TEST_F(TextToBinaryTest, LiteralStringTooLong) {
+ // SPIR-V allows strings up to 65535 characters.
const std::string code =
- "OpSourceExtension \"" + std::string(65534, 'o') + "\"\n";
+ "OpSourceExtension \"" + std::string(65535, 'o') + "\"\n";
EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code));
}
return MakeInstruction(opcode, args);
}
+// Returns the vector of words representing the concatenation
+// of all input vectors.
+inline std::vector<uint32_t> Concatenate(
+ const std::vector<std::vector<uint32_t>>& instructions) {
+ std::vector<uint32_t> result;
+ for (const auto& instruction : instructions) {
+ result.insert(result.end(), instruction.begin(), instruction.end());
+ }
+ return result;
+}
+
// Encodes a string as a sequence of words, using the SPIR-V encoding.
inline std::vector<uint32_t> MakeVector(std::string input) {
std::vector<uint32_t> result;