Support strings with up to 65535 chars, and null.
Move the definition of spv_instruction_t to an internal header file, since it now depends on C++ and is not used by the external interface. Use a std::vector<uint32_t> in spv_instruction_t instead of a fixed size array.
This commit is contained in:
Родитель
ee5104286f
Коммит
b5dc8fcd5d
|
@ -96,9 +96,11 @@ include_directories(
|
||||||
|
|
||||||
set(SPIRV_SOURCES
|
set(SPIRV_SOURCES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/libspirv/libspirv.h
|
${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/binary.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/source/bitwisecast.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/diagnostic.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/opcode.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/operand.h
|
${CMAKE_CURRENT_SOURCE_DIR}/source/operand.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/print.h
|
${CMAKE_CURRENT_SOURCE_DIR}/source/print.h
|
||||||
|
@ -164,6 +166,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/TestFixture.h
|
${CMAKE_CURRENT_SOURCE_DIR}/test/TestFixture.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/UnitSPIRV.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/AssemblyFormat.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/BinaryDestroy.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/BinaryDestroy.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/BinaryEndianness.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/BinaryEndianness.cpp
|
||||||
|
|
|
@ -56,10 +56,15 @@ extern "C" {
|
||||||
|
|
||||||
// Universal limits
|
// Universal limits
|
||||||
|
|
||||||
|
// SPIR-V 1.0 limits
|
||||||
|
#define SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX 0xffff
|
||||||
|
|
||||||
// NOTE: These are set to the minimum maximum values
|
// 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_NAME_MAX 0x00000400
|
||||||
#define SPV_LIMIT_LITERAL_STRING_MAX 0x00010000
|
#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_RESULT_ID_BOUND 0x00400000
|
||||||
#define SPV_LIMIT_CONTROL_FLOW_NEST_DEPTH 0x00000400
|
#define SPV_LIMIT_CONTROL_FLOW_NEST_DEPTH 0x00000400
|
||||||
#define SPV_LIMIT_GLOBAL_VARIABLES_MAX 0x00010000
|
#define SPV_LIMIT_GLOBAL_VARIABLES_MAX 0x00010000
|
||||||
|
@ -350,22 +355,6 @@ typedef struct spv_text_t {
|
||||||
uint64_t length;
|
uint64_t length;
|
||||||
} spv_text_t;
|
} 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 {
|
typedef struct spv_position_t {
|
||||||
uint64_t line;
|
uint64_t line;
|
||||||
uint64_t column;
|
uint64_t column;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
#include "ext_inst.h"
|
#include "ext_inst.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
|
|
||||||
|
@ -444,7 +445,7 @@ spv_result_t spvBinaryDecodeOpcode(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spvBinaryDecodeOperand(
|
if (spvBinaryDecodeOperand(
|
||||||
opcodeEntry->opcode, type, pInst->words + index, numWords, endian,
|
opcodeEntry->opcode, type, &pInst->words[index], numWords, endian,
|
||||||
options, operandTable, extInstTable, &expectedOperands,
|
options, operandTable, extInstTable, &expectedOperands,
|
||||||
&pInst->extInstType,
|
&pInst->extInstType,
|
||||||
(isAssigmentFormat && !currentIsResultId ? no_result_id_stream
|
(isAssigmentFormat && !currentIsResultId ? no_result_id_stream
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#define _LIBSPIRV_UTIL_BINARY_H_
|
#define _LIBSPIRV_UTIL_BINARY_H_
|
||||||
|
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
|
#include "instruction.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// 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_
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -312,7 +313,7 @@ void spvInstructionCopy(const uint32_t *words, const Op opcode,
|
||||||
const uint16_t wordCount, const spv_endianness_t endian,
|
const uint16_t wordCount, const spv_endianness_t endian,
|
||||||
spv_instruction_t *pInst) {
|
spv_instruction_t *pInst) {
|
||||||
pInst->opcode = opcode;
|
pInst->opcode = opcode;
|
||||||
pInst->wordCount = wordCount;
|
pInst->words.resize(wordCount);
|
||||||
for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
|
for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
|
||||||
pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
|
pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
|
||||||
if (!wordIndex) {
|
if (!wordIndex) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#ifndef _LIBSPIRV_UTIL_OPCODE_H_
|
#ifndef _LIBSPIRV_UTIL_OPCODE_H_
|
||||||
#define _LIBSPIRV_UTIL_OPCODE_H_
|
#define _LIBSPIRV_UTIL_OPCODE_H_
|
||||||
|
|
||||||
|
#include "instruction.h"
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "bitwisecast.h"
|
#include "bitwisecast.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
#include "ext_inst.h"
|
#include "ext_inst.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
|
@ -223,7 +224,7 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
|
||||||
return SPV_ERROR_INVALID_TEXT;
|
return SPV_ERROR_INVALID_TEXT;
|
||||||
}
|
}
|
||||||
const uint32_t id = context->spvNamedIdAssignOrGet(textValue);
|
const uint32_t id = context->spvNamedIdAssignOrGet(textValue);
|
||||||
pInst->words[pInst->wordCount++] = id;
|
spvInstructionAddWord(pInst, id);
|
||||||
} break;
|
} break;
|
||||||
case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
|
case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
|
||||||
// NOTE: Special case for extension instruction lookup
|
// NOTE: Special case for extension instruction lookup
|
||||||
|
@ -234,7 +235,7 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
|
||||||
<< textValue << "'.";
|
<< textValue << "'.";
|
||||||
return SPV_ERROR_INVALID_TEXT;
|
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.
|
// Prepare to parse the operands for the extended instructions.
|
||||||
spvPrependOperandTypes(extInst->operandTypes, pExpectedOperands);
|
spvPrependOperandTypes(extInst->operandTypes, pExpectedOperands);
|
||||||
|
@ -546,7 +547,8 @@ spv_result_t spvTextEncodeOpcode(const libspirv::AssemblyGrammar& grammar,
|
||||||
}
|
}
|
||||||
pInst->opcode = opcodeEntry->opcode;
|
pInst->opcode = opcodeEntry->opcode;
|
||||||
context->setPosition(nextPosition);
|
context->setPosition(nextPosition);
|
||||||
pInst->wordCount++;
|
// Reserve the first word for the instruction.
|
||||||
|
spvInstructionAddWord(pInst, 0);
|
||||||
|
|
||||||
// Maintains the ordered list of expected operand types.
|
// Maintains the ordered list of expected operand types.
|
||||||
// For many instructions we only need the {numTypes, operandTypes}
|
// For many instructions we only need the {numTypes, operandTypes}
|
||||||
|
@ -622,7 +624,14 @@ spv_result_t spvTextEncodeOpcode(const libspirv::AssemblyGrammar& grammar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -660,7 +669,8 @@ spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar,
|
||||||
|
|
||||||
spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
|
spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
|
||||||
while (context.hasText()) {
|
while (context.hasText()) {
|
||||||
spv_instruction_t inst = {};
|
instructions.push_back({});
|
||||||
|
spv_instruction_t& inst = instructions.back();
|
||||||
inst.extInstType = extInstType;
|
inst.extInstType = extInstType;
|
||||||
|
|
||||||
if (spvTextEncodeOpcode(grammar, &context, format, &inst)) {
|
if (spvTextEncodeOpcode(grammar, &context, format, &inst)) {
|
||||||
|
@ -668,22 +678,20 @@ spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar,
|
||||||
}
|
}
|
||||||
extInstType = inst.extInstType;
|
extInstType = inst.extInstType;
|
||||||
|
|
||||||
instructions.push_back(inst);
|
|
||||||
|
|
||||||
if (context.advance()) break;
|
if (context.advance()) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t totalSize = SPV_INDEX_INSTRUCTION;
|
size_t totalSize = SPV_INDEX_INSTRUCTION;
|
||||||
for (auto& inst : instructions) {
|
for (auto& inst : instructions) {
|
||||||
totalSize += inst.wordCount;
|
totalSize += inst.words.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t* data = new uint32_t[totalSize];
|
uint32_t* data = new uint32_t[totalSize];
|
||||||
if (!data) return SPV_ERROR_OUT_OF_MEMORY;
|
if (!data) return SPV_ERROR_OUT_OF_MEMORY;
|
||||||
uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
|
uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
|
||||||
for (auto& inst : instructions) {
|
for (auto& inst : instructions) {
|
||||||
memcpy(data + currentIndex, inst.words, sizeof(uint32_t) * inst.wordCount);
|
memcpy(data + currentIndex, inst.words.data(), sizeof(uint32_t) * inst.words.size());
|
||||||
currentIndex += inst.wordCount;
|
currentIndex += inst.words.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
spv_binary binary = new spv_binary_t();
|
spv_binary binary = new spv_binary_t();
|
||||||
|
|
|
@ -54,7 +54,8 @@ typedef struct spv_literal_t {
|
||||||
uint64_t u64;
|
uint64_t u64;
|
||||||
float f;
|
float f;
|
||||||
double d;
|
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;
|
} value;
|
||||||
} spv_literal_t;
|
} spv_literal_t;
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "ext_inst.h"
|
#include "ext_inst.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
|
|
||||||
|
@ -317,13 +318,7 @@ void AssemblyContext::seekForward(uint32_t size) {
|
||||||
|
|
||||||
spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
|
spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
|
||||||
spv_instruction_t *pInst) {
|
spv_instruction_t *pInst) {
|
||||||
if (pInst->wordCount + 1 > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
|
spvInstructionAddWord(pInst, value);
|
||||||
diagnostic() << "Instruction word count '"
|
|
||||||
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "' exceeded.";
|
|
||||||
return SPV_ERROR_INVALID_TEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pInst->words[pInst->wordCount++] = (uint32_t)value;
|
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,18 +335,26 @@ spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
|
||||||
|
|
||||||
spv_result_t AssemblyContext::binaryEncodeString(
|
spv_result_t AssemblyContext::binaryEncodeString(
|
||||||
const char *value, spv_instruction_t *pInst) {
|
const char *value, spv_instruction_t *pInst) {
|
||||||
size_t length = strlen(value);
|
const size_t length = strlen(value);
|
||||||
size_t wordCount = (length / 4) + 1;
|
const size_t wordCount = (length / 4) + 1;
|
||||||
if ((sizeof(uint32_t) * pInst->wordCount) + length >
|
const size_t oldWordCount = pInst->words.size();
|
||||||
sizeof(uint32_t) * SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
|
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 '"
|
diagnostic() << "Instruction word count '"
|
||||||
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "'exceeded.";
|
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "'exceeded.";
|
||||||
return SPV_ERROR_INVALID_TEXT;
|
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);
|
strncpy(dest, value, length);
|
||||||
pInst->wordCount += (uint16_t)wordCount;
|
|
||||||
|
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,12 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
|
|
||||||
namespace libspirv {
|
namespace libspirv {
|
||||||
// Structures
|
// Structures
|
||||||
|
|
||||||
|
|
||||||
// This is a lattice for tracking types.
|
// This is a lattice for tracking types.
|
||||||
enum class IdTypeClass {
|
enum class IdTypeClass {
|
||||||
kBottom, // We have no information yet.
|
kBottom, // We have no information yet.
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
#include "validate.h"
|
#include "validate.h"
|
||||||
|
@ -130,7 +131,7 @@ spv_result_t spvValidateBasic(const spv_instruction_t *pInsts,
|
||||||
spv_position position,
|
spv_position position,
|
||||||
spv_diagnostic *pDiagnostic) {
|
spv_diagnostic *pDiagnostic) {
|
||||||
for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
|
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;
|
uint16_t wordCount;
|
||||||
Op opcode;
|
Op opcode;
|
||||||
spvOpcodeSplit(words[0], &wordCount, &opcode);
|
spvOpcodeSplit(words[0], &wordCount, &opcode);
|
||||||
|
@ -150,7 +151,7 @@ spv_result_t spvValidateBasic(const spv_instruction_t *pInsts,
|
||||||
}
|
}
|
||||||
|
|
||||||
spv_operand_desc operandEntry = nullptr;
|
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++) {
|
++index, position->index++) {
|
||||||
const uint32_t word = words[index];
|
const uint32_t word = words[index];
|
||||||
|
|
||||||
|
@ -191,7 +192,7 @@ spv_result_t spvValidateIDs(const spv_instruction_t *pInsts,
|
||||||
std::vector<spv_id_info_t> idDefs;
|
std::vector<spv_id_info_t> idDefs;
|
||||||
|
|
||||||
for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
|
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;
|
Op opcode;
|
||||||
spvOpcodeSplit(words[0], nullptr, &opcode);
|
spvOpcodeSplit(words[0], nullptr, &opcode);
|
||||||
|
|
||||||
|
@ -203,7 +204,7 @@ spv_result_t spvValidateIDs(const spv_instruction_t *pInsts,
|
||||||
|
|
||||||
spv_operand_desc operandEntry = nullptr;
|
spv_operand_desc operandEntry = nullptr;
|
||||||
position->index++; // NOTE: Account for Opcode word
|
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++) {
|
++index, position->index++) {
|
||||||
const uint32_t word = words[index];
|
const uint32_t word = words[index];
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#ifndef _LIBSPIRV_UTIL_VALIDATE_H_
|
#ifndef _LIBSPIRV_UTIL_VALIDATE_H_
|
||||||
#define _LIBSPIRV_UTIL_VALIDATE_H_
|
#define _LIBSPIRV_UTIL_VALIDATE_H_
|
||||||
|
|
||||||
|
#include "instruction.h"
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
|
|
||||||
// Structures
|
// Structures
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include <libspirv/libspirv.h>
|
#include <libspirv/libspirv.h>
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
|
#include "instruction.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "validate.h"
|
#include "validate.h"
|
||||||
|
|
||||||
|
@ -157,7 +158,7 @@ bool idUsage::isValid<OpMemberName>(const spv_instruction_t *inst,
|
||||||
return false);
|
return false);
|
||||||
auto memberIndex = 2;
|
auto memberIndex = 2;
|
||||||
auto member = inst->words[memberIndex];
|
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)
|
spvCheck(memberCount <= member, DIAG(memberIndex)
|
||||||
<< "OpMemberName Member <id> '"
|
<< "OpMemberName Member <id> '"
|
||||||
<< inst->words[memberIndex]
|
<< inst->words[memberIndex]
|
||||||
|
@ -212,7 +213,7 @@ bool idUsage::isValid<OpMemberDecorate>(const spv_instruction_t *inst,
|
||||||
return false);
|
return false);
|
||||||
auto memberIndex = 2;
|
auto memberIndex = 2;
|
||||||
auto member = inst->words[memberIndex];
|
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)
|
spvCheck(memberCount < member, DIAG(memberIndex)
|
||||||
<< "OpMemberDecorate Structure type <id> '"
|
<< "OpMemberDecorate Structure type <id> '"
|
||||||
<< inst->words[memberIndex]
|
<< inst->words[memberIndex]
|
||||||
|
@ -237,7 +238,7 @@ bool idUsage::isValid<OpGroupDecorate>(const spv_instruction_t *inst,
|
||||||
<< inst->words[decorationGroupIndex]
|
<< inst->words[decorationGroupIndex]
|
||||||
<< "' is not a decoration group.";
|
<< "' is not a decoration group.";
|
||||||
return false);
|
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]);
|
auto target = find(inst->words[targetIndex]);
|
||||||
spvCheck(!found(target), DIAG(targetIndex)
|
spvCheck(!found(target), DIAG(targetIndex)
|
||||||
<< "OpGroupDecorate Target <id> '"
|
<< "OpGroupDecorate Target <id> '"
|
||||||
|
@ -279,7 +280,7 @@ bool idUsage::isValid<OpEntryPoint>(const spv_instruction_t *inst,
|
||||||
// to change
|
// to change
|
||||||
auto entryPointType = find(entryPoint->second.inst->words[4]);
|
auto entryPointType = find(entryPoint->second.inst->words[4]);
|
||||||
spvCheck(!found(entryPointType), assert(0 && "Unreachable!"));
|
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> '"
|
DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
|
||||||
<< inst->words[entryPointIndex]
|
<< inst->words[entryPointIndex]
|
||||||
<< "'s function parameter count is not zero.";
|
<< "'s function parameter count is not zero.";
|
||||||
|
@ -406,13 +407,13 @@ bool idUsage::isValid<OpTypeArray>(const spv_instruction_t *inst,
|
||||||
<< inst->words[lengthIndex]
|
<< inst->words[lengthIndex]
|
||||||
<< "' is not a constant integer type.";
|
<< "' is not a constant integer type.";
|
||||||
return false);
|
return false);
|
||||||
if (4 == constInst->wordCount) {
|
if (4 == constInst->words.size()) {
|
||||||
spvCheck(1 > constInst->words[3], DIAG(lengthIndex)
|
spvCheck(1 > constInst->words[3], DIAG(lengthIndex)
|
||||||
<< "OpTypeArray Length <id> '"
|
<< "OpTypeArray Length <id> '"
|
||||||
<< inst->words[lengthIndex]
|
<< inst->words[lengthIndex]
|
||||||
<< "' value must be at least 1.";
|
<< "' value must be at least 1.";
|
||||||
return false);
|
return false);
|
||||||
} else if (5 == constInst->wordCount) {
|
} else if (5 == constInst->words.size()) {
|
||||||
uint64_t value =
|
uint64_t value =
|
||||||
constInst->words[3] | ((uint64_t)constInst->words[4]) << 32;
|
constInst->words[3] | ((uint64_t)constInst->words[4]) << 32;
|
||||||
bool signedness = constResultType->second.inst->words[3];
|
bool signedness = constResultType->second.inst->words[3];
|
||||||
|
@ -453,7 +454,7 @@ bool idUsage::isValid<OpTypeRuntimeArray>(const spv_instruction_t *inst,
|
||||||
template <>
|
template <>
|
||||||
bool idUsage::isValid<OpTypeStruct>(const spv_instruction_t *inst,
|
bool idUsage::isValid<OpTypeStruct>(const spv_instruction_t *inst,
|
||||||
const spv_opcode_desc) {
|
const spv_opcode_desc) {
|
||||||
for (uint64_t memberTypeIndex = 2; memberTypeIndex < inst->wordCount;
|
for (uint64_t memberTypeIndex = 2; memberTypeIndex < inst->words.size();
|
||||||
++memberTypeIndex) {
|
++memberTypeIndex) {
|
||||||
auto memberType = find(inst->words[memberTypeIndex]);
|
auto memberType = find(inst->words[memberTypeIndex]);
|
||||||
spvCheck(!found(memberType), DIAG(memberTypeIndex)
|
spvCheck(!found(memberType), DIAG(memberTypeIndex)
|
||||||
|
@ -501,7 +502,7 @@ bool idUsage::isValid<OpTypeFunction>(const spv_instruction_t *inst,
|
||||||
<< inst->words[returnTypeIndex]
|
<< inst->words[returnTypeIndex]
|
||||||
<< "' is not a type.";
|
<< "' is not a type.";
|
||||||
return false);
|
return false);
|
||||||
for (uint64_t paramTypeIndex = 3; paramTypeIndex < inst->wordCount;
|
for (uint64_t paramTypeIndex = 3; paramTypeIndex < inst->words.size();
|
||||||
++paramTypeIndex) {
|
++paramTypeIndex) {
|
||||||
auto paramType = find(inst->words[paramTypeIndex]);
|
auto paramType = find(inst->words[paramTypeIndex]);
|
||||||
spvCheck(!found(paramType), DIAG(paramTypeIndex)
|
spvCheck(!found(paramType), DIAG(paramTypeIndex)
|
||||||
|
@ -596,21 +597,21 @@ bool idUsage::isValid<OpConstantComposite>(const spv_instruction_t *inst,
|
||||||
<< "' is not a composite type.";
|
<< "' is not a composite type.";
|
||||||
return false);
|
return false);
|
||||||
|
|
||||||
uint32_t constituentCount = inst->wordCount - 3;
|
uint32_t constituentCount = inst->words.size() - 3;
|
||||||
switch (resultType->second.opcode) {
|
switch (resultType->second.opcode) {
|
||||||
case OpTypeVector: {
|
case OpTypeVector: {
|
||||||
auto componentCount = resultType->second.inst->words[3];
|
auto componentCount = resultType->second.inst->words[3];
|
||||||
spvCheck(
|
spvCheck(
|
||||||
componentCount != constituentCount,
|
componentCount != constituentCount,
|
||||||
// TODO: Output ID's on diagnostic
|
// TODO: Output ID's on diagnostic
|
||||||
DIAG(inst->wordCount - 1)
|
DIAG(inst->words.size() - 1)
|
||||||
<< "OpConstantComposite Constituent <id> count does not match "
|
<< "OpConstantComposite Constituent <id> count does not match "
|
||||||
"Result Type <id> '"
|
"Result Type <id> '"
|
||||||
<< resultType->second.id << "'s vector component count.";
|
<< resultType->second.id << "'s vector component count.";
|
||||||
return false);
|
return false);
|
||||||
auto componentType = find(resultType->second.inst->words[2]);
|
auto componentType = find(resultType->second.inst->words[2]);
|
||||||
spvCheck(!found(componentType), assert(0 && "Unreachable!"));
|
spvCheck(!found(componentType), assert(0 && "Unreachable!"));
|
||||||
for (uint64_t constituentIndex = 3; constituentIndex < inst->wordCount;
|
for (uint64_t constituentIndex = 3; constituentIndex < inst->words.size();
|
||||||
constituentIndex++) {
|
constituentIndex++) {
|
||||||
auto constituent = find(inst->words[constituentIndex]);
|
auto constituent = find(inst->words[constituentIndex]);
|
||||||
spvCheck(!found(constituent), assert(0 && "Unreachable!"));
|
spvCheck(!found(constituent), assert(0 && "Unreachable!"));
|
||||||
|
@ -636,7 +637,7 @@ bool idUsage::isValid<OpConstantComposite>(const spv_instruction_t *inst,
|
||||||
spvCheck(
|
spvCheck(
|
||||||
columnCount != constituentCount,
|
columnCount != constituentCount,
|
||||||
// TODO: Output ID's on diagnostic
|
// TODO: Output ID's on diagnostic
|
||||||
DIAG(inst->wordCount - 1)
|
DIAG(inst->words.size() - 1)
|
||||||
<< "OpConstantComposite Constituent <id> count does not match "
|
<< "OpConstantComposite Constituent <id> count does not match "
|
||||||
"Result Type <id> '"
|
"Result Type <id> '"
|
||||||
<< resultType->second.id << "'s matrix column count.";
|
<< resultType->second.id << "'s matrix column count.";
|
||||||
|
@ -648,7 +649,7 @@ bool idUsage::isValid<OpConstantComposite>(const spv_instruction_t *inst,
|
||||||
auto componentType = find(columnType->second.inst->words[2]);
|
auto componentType = find(columnType->second.inst->words[2]);
|
||||||
spvCheck(!found(componentType), assert(0 && "Unreachable!"));
|
spvCheck(!found(componentType), assert(0 && "Unreachable!"));
|
||||||
|
|
||||||
for (uint64_t constituentIndex = 3; constituentIndex < inst->wordCount;
|
for (uint64_t constituentIndex = 3; constituentIndex < inst->words.size();
|
||||||
constituentIndex++) {
|
constituentIndex++) {
|
||||||
auto constituent = find(inst->words[constituentIndex]);
|
auto constituent = find(inst->words[constituentIndex]);
|
||||||
spvCheck(!found(constituent),
|
spvCheck(!found(constituent),
|
||||||
|
@ -698,12 +699,12 @@ bool idUsage::isValid<OpConstantComposite>(const spv_instruction_t *inst,
|
||||||
auto length = find(resultType->second.inst->words[3]);
|
auto length = find(resultType->second.inst->words[3]);
|
||||||
spvCheck(!found(length), assert(0 && "Unreachable!"));
|
spvCheck(!found(length), assert(0 && "Unreachable!"));
|
||||||
spvCheck(length->second.inst->words[3] != constituentCount,
|
spvCheck(length->second.inst->words[3] != constituentCount,
|
||||||
DIAG(inst->wordCount - 1)
|
DIAG(inst->words.size() - 1)
|
||||||
<< "OpConstantComposite Constituent count does not match "
|
<< "OpConstantComposite Constituent count does not match "
|
||||||
"Result Type <id> '"
|
"Result Type <id> '"
|
||||||
<< resultType->second.id << "'s array length.";
|
<< resultType->second.id << "'s array length.";
|
||||||
return false);
|
return false);
|
||||||
for (uint64_t constituentIndex = 3; constituentIndex < inst->wordCount;
|
for (uint64_t constituentIndex = 3; constituentIndex < inst->words.size();
|
||||||
constituentIndex++) {
|
constituentIndex++) {
|
||||||
auto constituent = find(inst->words[constituentIndex]);
|
auto constituent = find(inst->words[constituentIndex]);
|
||||||
spvCheck(!found(constituent),
|
spvCheck(!found(constituent),
|
||||||
|
@ -729,7 +730,7 @@ bool idUsage::isValid<OpConstantComposite>(const spv_instruction_t *inst,
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case OpTypeStruct: {
|
case OpTypeStruct: {
|
||||||
uint32_t memberCount = resultType->second.inst->wordCount - 2;
|
uint32_t memberCount = resultType->second.inst->words.size() - 2;
|
||||||
spvCheck(memberCount != constituentCount,
|
spvCheck(memberCount != constituentCount,
|
||||||
DIAG(resultTypeIndex)
|
DIAG(resultTypeIndex)
|
||||||
<< "OpConstantComposite Constituent <id> '"
|
<< "OpConstantComposite Constituent <id> '"
|
||||||
|
@ -738,7 +739,7 @@ bool idUsage::isValid<OpConstantComposite>(const spv_instruction_t *inst,
|
||||||
<< resultType->second.id << "'s struct member count.";
|
<< resultType->second.id << "'s struct member count.";
|
||||||
return false);
|
return false);
|
||||||
for (uint32_t constituentIndex = 3, memberIndex = 2;
|
for (uint32_t constituentIndex = 3, memberIndex = 2;
|
||||||
constituentIndex < inst->wordCount;
|
constituentIndex < inst->words.size();
|
||||||
constituentIndex++, memberIndex++) {
|
constituentIndex++, memberIndex++) {
|
||||||
auto constituent = find(inst->words[constituentIndex]);
|
auto constituent = find(inst->words[constituentIndex]);
|
||||||
spvCheck(!found(constituent),
|
spvCheck(!found(constituent),
|
||||||
|
@ -841,7 +842,7 @@ bool idUsage::isValid<OpConstantNull>(const spv_instruction_t *inst,
|
||||||
} break;
|
} break;
|
||||||
case OpTypeStruct: {
|
case OpTypeStruct: {
|
||||||
for (uint64_t elementIndex = 2;
|
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]);
|
auto element = find(resultType->second.inst->words[elementIndex]);
|
||||||
spvCheck(!found(element), assert(0 && "Unreachable!"));
|
spvCheck(!found(element), assert(0 && "Unreachable!"));
|
||||||
spvCheck(!spvOpcodeIsBasicTypeNullable(element->second.inst->opcode),
|
spvCheck(!spvOpcodeIsBasicTypeNullable(element->second.inst->opcode),
|
||||||
|
@ -936,7 +937,7 @@ bool idUsage::isValid<OpVariable>(const spv_instruction_t *inst,
|
||||||
<< inst->words[resultTypeIndex]
|
<< inst->words[resultTypeIndex]
|
||||||
<< "' is not a pointer type.";
|
<< "' is not a pointer type.";
|
||||||
return false);
|
return false);
|
||||||
if (opcodeEntry->numTypes < inst->wordCount) {
|
if (opcodeEntry->numTypes < inst->words.size()) {
|
||||||
auto initialiserIndex = 4;
|
auto initialiserIndex = 4;
|
||||||
auto initialiser = find(inst->words[initialiserIndex]);
|
auto initialiser = find(inst->words[initialiserIndex]);
|
||||||
spvCheck(!found(initialiser), DIAG(initialiserIndex)
|
spvCheck(!found(initialiser), DIAG(initialiserIndex)
|
||||||
|
@ -1275,16 +1276,16 @@ bool idUsage::isValid<OpFunctionCall>(const spv_instruction_t *inst,
|
||||||
return false);
|
return false);
|
||||||
auto functionType = find(function->second.inst->words[4]);
|
auto functionType = find(function->second.inst->words[4]);
|
||||||
spvCheck(!found(functionType), assert(0 && "Unreachable!"));
|
spvCheck(!found(functionType), assert(0 && "Unreachable!"));
|
||||||
auto functionCallArgCount = inst->wordCount - 4;
|
auto functionCallArgCount = inst->words.size() - 4;
|
||||||
auto functionParamCount = functionType->second.inst->wordCount - 3;
|
auto functionParamCount = functionType->second.inst->words.size() - 3;
|
||||||
spvCheck(
|
spvCheck(
|
||||||
functionParamCount != functionCallArgCount,
|
functionParamCount != functionCallArgCount,
|
||||||
DIAG(inst->wordCount - 1)
|
DIAG(inst->words.size() - 1)
|
||||||
<< "OpFunctionCall Function <id>'s parameter count does not match "
|
<< "OpFunctionCall Function <id>'s parameter count does not match "
|
||||||
"the argument count.";
|
"the argument count.";
|
||||||
return false);
|
return false);
|
||||||
for (uint64_t argumentIndex = 4, paramIndex = 3;
|
for (uint64_t argumentIndex = 4, paramIndex = 3;
|
||||||
argumentIndex < inst->wordCount; argumentIndex++, paramIndex++) {
|
argumentIndex < inst->words.size(); argumentIndex++, paramIndex++) {
|
||||||
auto argument = find(inst->words[argumentIndex]);
|
auto argument = find(inst->words[argumentIndex]);
|
||||||
spvCheck(!found(argument), DIAG(argumentIndex)
|
spvCheck(!found(argument), DIAG(argumentIndex)
|
||||||
<< "OpFunctionCall Argument <id> '"
|
<< "OpFunctionCall Argument <id> '"
|
||||||
|
@ -2601,7 +2602,7 @@ spv_result_t spvValidateInstructionIDs(
|
||||||
pIdDefs, idDefsCount, pInsts, instCount, position, pDiag);
|
pIdDefs, idDefsCount, pInsts, instCount, position, pDiag);
|
||||||
for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
|
for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
|
||||||
spvCheck(!idUsage.isValid(&pInsts[instIndex]), return SPV_ERROR_INVALID_ID);
|
spvCheck(!idUsage.isValid(&pInsts[instIndex]), return SPV_ERROR_INVALID_ID);
|
||||||
position->index += pInsts[instIndex].wordCount;
|
position->index += pInsts[instIndex].words.size();
|
||||||
}
|
}
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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
|
|
@ -115,6 +115,7 @@ class TextToBinaryTestBase : public T {
|
||||||
spvDiagnosticDestroy(diagnostic);
|
spvDiagnosticDestroy(diagnostic);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(SPV_SUCCESS, error);
|
EXPECT_EQ(SPV_SUCCESS, error);
|
||||||
|
if (!binary) return "";
|
||||||
|
|
||||||
spv_text decoded_text;
|
spv_text decoded_text;
|
||||||
error = spvBinaryToText(
|
error = spvBinaryToText(
|
||||||
|
|
|
@ -129,14 +129,14 @@ TEST(TextLiteral, GoodString) {
|
||||||
TEST(TextLiteral, StringTooLong) {
|
TEST(TextLiteral, StringTooLong) {
|
||||||
spv_literal_t l;
|
spv_literal_t l;
|
||||||
std::string too_long = std::string("\"") +
|
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));
|
EXPECT_EQ(SPV_ERROR_OUT_OF_MEMORY, spvTextToLiteral(too_long.data(), &l));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TextLiteral, GoodLongString) {
|
TEST(TextLiteral, GoodLongString) {
|
||||||
spv_literal_t l;
|
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 + "\"";
|
std::string good_long = std::string("\"") + unquoted + "\"";
|
||||||
EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l));
|
EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l));
|
||||||
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
|
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
|
||||||
|
|
|
@ -50,11 +50,10 @@ TEST_F(TextToBinaryTest, LiteralNumberInPlaceOfLiteralString) {
|
||||||
CompileFailure(R"(OpSourceExtension 1000)"));
|
CompileFailure(R"(OpSourceExtension 1000)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(antiagainst): libspirv.h defines SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX
|
TEST_F(TextToBinaryTest, LiteralStringTooLong) {
|
||||||
// to be 0x108. Lift that limit and enable the following test.
|
// SPIR-V allows strings up to 65535 characters.
|
||||||
TEST_F(TextToBinaryTest, DISABLED_LiteralStringTooLong) {
|
|
||||||
const std::string code =
|
const std::string code =
|
||||||
"OpSourceExtension \"" + std::string(65534, 'o') + "\"\n";
|
"OpSourceExtension \"" + std::string(65535, 'o') + "\"\n";
|
||||||
EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code));
|
EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,17 @@ inline std::vector<uint32_t> MakeInstruction(
|
||||||
return MakeInstruction(opcode, args);
|
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.
|
// Encodes a string as a sequence of words, using the SPIR-V encoding.
|
||||||
inline std::vector<uint32_t> MakeVector(std::string input) {
|
inline std::vector<uint32_t> MakeVector(std::string input) {
|
||||||
std::vector<uint32_t> result;
|
std::vector<uint32_t> result;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче