mirror of
https://github.com/RPCSX/SPIRV-Tools.git
synced 2025-02-17 01:49:23 +00:00
Add support for SPV_KHR_non_semantic_info (#3110)
Add support for SPV_KHR_non_semantic_info This entails a couple of changes: - Allowing unknown OpExtInstImport that begin with the prefix `NonSemantic.` - Allowing OpExtInst that reference any of those sets to contain unknown ext inst instruction numbers, and assume the format is always a series of IDs as guaranteed by the extension. - Allowing those OpExtInst to appear in the types/variables/constants section. - Not stripping OpString in the --strip-debug pass, since it may be referenced by these non-semantic OpExtInsts. - Stripping them instead in the --strip-reflect pass. * Add adjacency validation of non-semantic OpExtInst - We validate and test that OpExtInst cannot appear before or between OpPhi instructions, or before/between OpFunctionParameter instructions. * Change non-semantic extinst type to single value * Add helper function spvExtInstIsNonSemantic() which will check if the extinst set is non-semantic or not, either the unknown generic value or any future recognised non-semantic set. * Add test of a complex non-semantic extinst * Use DefUseManager in StripDebugInfoPass to strip some OpStrings * Any OpString used by a non-semantic instruction cannot be stripped, all others can so we search for uses to see if each string can be removed. * We only do this if the non-semantic debug info extension is enabled, otherwise all strings can be trivially removed. * Silence -Winconsistent-missing-override in protobufs
This commit is contained in:
parent
38d7fbaad0
commit
e70b009b0f
3
external/CMakeLists.txt
vendored
3
external/CMakeLists.txt
vendored
@ -108,6 +108,9 @@ if(SPIRV_BUILD_FUZZER)
|
||||
set(protobuf_BUILD_TESTS OFF CACHE BOOL "Disable protobuf tests")
|
||||
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Do not build protobuf static runtime")
|
||||
if (IS_DIRECTORY ${PROTOBUF_DIR})
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
|
||||
add_definitions(-Wno-inconsistent-missing-override)
|
||||
endif()
|
||||
add_subdirectory(${PROTOBUF_DIR} EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
|
@ -249,6 +249,11 @@ typedef enum spv_ext_inst_type_t {
|
||||
SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
|
||||
SPV_EXT_INST_TYPE_DEBUGINFO,
|
||||
|
||||
// Multiple distinct extended instruction set types could return this
|
||||
// value, if they are prefixed with NonSemantic. and are otherwise
|
||||
// unrecognised
|
||||
SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN,
|
||||
|
||||
SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t)
|
||||
} spv_ext_inst_type_t;
|
||||
|
||||
|
@ -477,9 +477,22 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
assert(SpvOpExtInst == opcode);
|
||||
assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
|
||||
spv_ext_inst_desc ext_inst;
|
||||
if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst))
|
||||
return diagnostic() << "Invalid extended instruction number: " << word;
|
||||
spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
|
||||
if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
|
||||
SPV_SUCCESS) {
|
||||
// if we know about this ext inst, push the expected operands
|
||||
spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
|
||||
} else {
|
||||
// if we don't know this extended instruction and the set isn't
|
||||
// non-semantic, we cannot process further
|
||||
if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
|
||||
return diagnostic()
|
||||
<< "Invalid extended instruction number: " << word;
|
||||
} else {
|
||||
// for non-semantic instruction sets, we know the form of all such
|
||||
// extended instructions contains a series of IDs as parameters
|
||||
expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
|
||||
|
@ -217,10 +217,18 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
|
||||
break;
|
||||
case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
|
||||
spv_ext_inst_desc ext_inst;
|
||||
if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst))
|
||||
assert(false && "should have caught this earlier");
|
||||
SetRed();
|
||||
stream_ << ext_inst->name;
|
||||
if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) ==
|
||||
SPV_SUCCESS) {
|
||||
stream_ << ext_inst->name;
|
||||
} else {
|
||||
if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) {
|
||||
assert(false && "should have caught this earlier");
|
||||
} else {
|
||||
// for non-semantic instruction sets we can just print the number
|
||||
stream_ << word;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
|
||||
spv_opcode_desc opcode_desc;
|
||||
|
@ -116,9 +116,21 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
|
||||
if (!strcmp("DebugInfo", name)) {
|
||||
return SPV_EXT_INST_TYPE_DEBUGINFO;
|
||||
}
|
||||
// ensure to add any known non-semantic extended instruction sets
|
||||
// above this point, and update spvExtInstIsNonSemantic()
|
||||
if (!strncmp("NonSemantic.", name, 12)) {
|
||||
return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN;
|
||||
}
|
||||
return SPV_EXT_INST_TYPE_NONE;
|
||||
}
|
||||
|
||||
bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
|
||||
if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
|
||||
const spv_ext_inst_type_t type,
|
||||
const char* name,
|
||||
|
@ -21,6 +21,9 @@
|
||||
// Gets the type of the extended instruction set with the specified name.
|
||||
spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name);
|
||||
|
||||
// Returns true if the extended instruction set is non-semantic
|
||||
bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
|
||||
|
||||
// Finds the named extented instruction of the given type in the given extended
|
||||
// instruction table. On success, returns SPV_SUCCESS and writes a handle of
|
||||
// the instruction entry into *entry.
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "source/ext_inst.h"
|
||||
#include "source/opt/log.h"
|
||||
#include "source/opt/reflect.h"
|
||||
#include "source/util/make_unique.h"
|
||||
@ -113,11 +114,14 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
||||
} else if (IsTypeInst(opcode)) {
|
||||
module_->AddType(std::move(spv_inst));
|
||||
} else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
|
||||
opcode == SpvOpUndef) {
|
||||
opcode == SpvOpUndef ||
|
||||
(opcode == SpvOpExtInst &&
|
||||
spvExtInstIsNonSemantic(inst->ext_inst_type))) {
|
||||
module_->AddGlobalValue(std::move(spv_inst));
|
||||
} else {
|
||||
Errorf(consumer_, src, loc,
|
||||
"Unhandled inst type (opcode: %d) found outside function definition.",
|
||||
"Unhandled inst type (opcode: %d) found outside function "
|
||||
"definition.",
|
||||
opcode);
|
||||
return false;
|
||||
}
|
||||
|
@ -19,12 +19,60 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status StripDebugInfoPass::Process() {
|
||||
bool modified = !context()->debugs1().empty() ||
|
||||
!context()->debugs2().empty() ||
|
||||
!context()->debugs3().empty();
|
||||
bool uses_non_semantic_info = false;
|
||||
for (auto& inst : context()->module()->extensions()) {
|
||||
const char* ext_name =
|
||||
reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
|
||||
if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
|
||||
uses_non_semantic_info = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Instruction*> to_kill;
|
||||
for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
|
||||
|
||||
// if we use non-semantic info, it may reference OpString. Do a more
|
||||
// expensive pass checking the uses of the OpString to see if any are
|
||||
// OpExtInst on a non-semantic instruction set. If we're not using the
|
||||
// extension then we can do a simpler pass and kill all debug1 instructions
|
||||
if (uses_non_semantic_info) {
|
||||
for (auto& inst : context()->module()->debugs1()) {
|
||||
switch (inst.opcode()) {
|
||||
case SpvOpString: {
|
||||
analysis::DefUseManager* def_use = context()->get_def_use_mgr();
|
||||
|
||||
// see if this string is used anywhere by a non-semantic instruction
|
||||
bool no_nonsemantic_use =
|
||||
def_use->WhileEachUser(&inst, [def_use](Instruction* use) {
|
||||
if (use->opcode() == SpvOpExtInst) {
|
||||
auto ext_inst_set =
|
||||
def_use->GetDef(use->GetSingleWordInOperand(0u));
|
||||
const char* extension_name = reinterpret_cast<const char*>(
|
||||
&ext_inst_set->GetInOperand(0).words[0]);
|
||||
if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
|
||||
// found a non-semantic use, return false as we cannot
|
||||
// remove this OpString
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// other instructions can't be a non-semantic use
|
||||
return true;
|
||||
});
|
||||
|
||||
if (no_nonsemantic_use) to_kill.push_back(&inst);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
to_kill.push_back(&inst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
|
||||
}
|
||||
|
||||
for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
|
||||
for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
|
||||
|
||||
@ -38,8 +86,11 @@ Pass::Status StripDebugInfoPass::Process() {
|
||||
return false;
|
||||
});
|
||||
|
||||
bool modified = !to_kill.empty();
|
||||
|
||||
for (auto* inst : to_kill) context()->KillInst(inst);
|
||||
|
||||
// clear OpLine information
|
||||
context()->module()->ForEachInst([&modified](Instruction* inst) {
|
||||
modified |= !inst->dbg_line_insts().empty();
|
||||
inst->dbg_line_insts().clear();
|
||||
|
@ -67,9 +67,54 @@ Pass::Status StripReflectInfoPass::Process() {
|
||||
} else if (!other_uses_for_decorate_string &&
|
||||
0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) {
|
||||
to_remove.push_back(&inst);
|
||||
} else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
|
||||
to_remove.push_back(&inst);
|
||||
}
|
||||
}
|
||||
|
||||
// clear all debug data now if it hasn't been cleared already, to remove any
|
||||
// remaining OpString that may have been referenced by non-semantic extinsts
|
||||
for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
|
||||
for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
|
||||
for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
|
||||
|
||||
// remove any extended inst imports that are non semantic
|
||||
std::unordered_set<uint32_t> non_semantic_sets;
|
||||
for (auto& inst : context()->module()->ext_inst_imports()) {
|
||||
assert(inst.opcode() == SpvOpExtInstImport &&
|
||||
"Expecting an import of an extension's instruction set.");
|
||||
const char* extension_name =
|
||||
reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
|
||||
if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
|
||||
non_semantic_sets.insert(inst.result_id());
|
||||
to_remove.push_back(&inst);
|
||||
}
|
||||
}
|
||||
|
||||
// if we removed some non-semantic sets, then iterate over the instructions in
|
||||
// the module to remove any OpExtInst that referenced those sets
|
||||
if (!non_semantic_sets.empty()) {
|
||||
context()->module()->ForEachInst(
|
||||
[&non_semantic_sets, &to_remove](Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpExtInst) {
|
||||
if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) !=
|
||||
non_semantic_sets.end()) {
|
||||
to_remove.push_back(inst);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// OpName must come first, since they may refer to other debug instructions.
|
||||
// If they are after the instructions that refer to, then they will be killed
|
||||
// when that instruction is killed, which will lead to a double kill.
|
||||
std::sort(to_remove.begin(), to_remove.end(),
|
||||
[](Instruction* lhs, Instruction* rhs) -> bool {
|
||||
if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
for (auto* inst : to_remove) {
|
||||
modified = true;
|
||||
context()->KillInst(inst);
|
||||
|
@ -242,14 +242,37 @@ spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar,
|
||||
// The assembler accepts the symbolic name for an extended instruction,
|
||||
// and emits its corresponding number.
|
||||
spv_ext_inst_desc extInst;
|
||||
if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst)) {
|
||||
return context->diagnostic()
|
||||
<< "Invalid extended instruction name '" << textValue << "'.";
|
||||
}
|
||||
spvInstructionAddWord(pInst, extInst->ext_inst);
|
||||
if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) ==
|
||||
SPV_SUCCESS) {
|
||||
// if we know about this extended instruction, push the numeric value
|
||||
spvInstructionAddWord(pInst, extInst->ext_inst);
|
||||
|
||||
// Prepare to parse the operands for the extended instructions.
|
||||
spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
|
||||
// Prepare to parse the operands for the extended instructions.
|
||||
spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
|
||||
} else {
|
||||
// if we don't know this extended instruction and the set isn't
|
||||
// non-semantic, we cannot process further
|
||||
if (!spvExtInstIsNonSemantic(pInst->extInstType)) {
|
||||
return context->diagnostic()
|
||||
<< "Invalid extended instruction name '" << textValue << "'.";
|
||||
} else {
|
||||
// for non-semantic instruction sets, as long as the text name is an
|
||||
// integer value we can encode it since we know the form of all such
|
||||
// extended instructions
|
||||
spv_literal_t extInstValue;
|
||||
if (spvTextToLiteral(textValue, &extInstValue) ||
|
||||
extInstValue.type != SPV_LITERAL_TYPE_UINT_32) {
|
||||
return context->diagnostic()
|
||||
<< "Couldn't translate unknown extended instruction name '"
|
||||
<< textValue << "' to unsigned integer.";
|
||||
}
|
||||
|
||||
spvInstructionAddWord(pInst, extInstValue.value.u32);
|
||||
|
||||
// opcode contains an unknown number of IDs.
|
||||
pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "source/ext_inst.h"
|
||||
#include "source/table.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
@ -85,6 +86,11 @@ class Instruction {
|
||||
return inst_.ext_inst_type;
|
||||
}
|
||||
|
||||
bool IsNonSemantic() const {
|
||||
return opcode() == SpvOp::SpvOpExtInst &&
|
||||
spvExtInstIsNonSemantic(inst_.ext_inst_type);
|
||||
}
|
||||
|
||||
// Casts the words belonging to the operand under |index| to |T| and returns.
|
||||
template <typename T>
|
||||
T GetOperandAs(size_t index) const {
|
||||
|
@ -286,7 +286,8 @@ spv_result_t ValidateDecorationGroup(ValidationState_t& _,
|
||||
auto use = pair.first;
|
||||
if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate &&
|
||||
use->opcode() != SpvOpGroupMemberDecorate &&
|
||||
use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId) {
|
||||
use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId &&
|
||||
!use->IsNonSemantic()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result id of OpDecorationGroup can only "
|
||||
<< "be targeted by OpName, OpGroupDecorate, "
|
||||
|
@ -1275,6 +1275,7 @@ spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
|
||||
const auto store = use.first;
|
||||
if (store->opcode() == SpvOpFConvert) continue;
|
||||
if (spvOpcodeIsDebug(store->opcode())) continue;
|
||||
if (store->IsNonSemantic()) continue;
|
||||
if (spvOpcodeIsDecoration(store->opcode())) continue;
|
||||
if (store->opcode() != SpvOpStore) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
|
@ -61,8 +61,8 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
spv_result_t ValidateExtInstImport(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto name_id = 1;
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
const auto name_id = 1;
|
||||
const std::string name(reinterpret_cast<const char*>(
|
||||
inst->words().data() + inst->operands()[name_id].offset));
|
||||
if (name != "GLSL.std.450") {
|
||||
@ -72,6 +72,16 @@ spv_result_t ValidateExtInstImport(ValidationState_t& _,
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.HasExtension(kSPV_KHR_non_semantic_info)) {
|
||||
const std::string name(reinterpret_cast<const char*>(
|
||||
inst->words().data() + inst->operands()[name_id].offset));
|
||||
if (name.find("NonSemantic.") == 0) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "NonSemantic extended instruction sets cannot be declared "
|
||||
"without SPV_KHR_non_semantic_info.";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,8 @@ spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) {
|
||||
for (auto& pair : inst->uses()) {
|
||||
const auto* use = pair.first;
|
||||
if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
|
||||
acceptable.end()) {
|
||||
acceptable.end() &&
|
||||
!use->IsNonSemantic()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, use)
|
||||
<< "Invalid use of function result id " << _.getIdName(inst->id())
|
||||
<< ".";
|
||||
|
@ -167,7 +167,8 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
|
||||
const auto opcode = inst->opcode();
|
||||
if (spvOpcodeGeneratesType(def->opcode()) &&
|
||||
!spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
|
||||
!spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
|
||||
!inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
|
||||
opcode != SpvOpFunction &&
|
||||
opcode != SpvOpCooperativeMatrixLengthNV &&
|
||||
!(opcode == SpvOpSpecConstantOp &&
|
||||
inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
|
||||
@ -175,7 +176,7 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
|
||||
<< "Operand " << _.getIdName(operand_word)
|
||||
<< " cannot be a type";
|
||||
} else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
|
||||
!spvOpcodeIsDebug(opcode) &&
|
||||
!spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() &&
|
||||
!spvOpcodeIsDecoration(opcode) &&
|
||||
!spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
|
||||
opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
|
||||
@ -187,6 +188,11 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Operand " << _.getIdName(operand_word)
|
||||
<< " requires a type";
|
||||
} else if (def->IsNonSemantic() && !inst->IsNonSemantic()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Operand " << _.getIdName(operand_word)
|
||||
<< " in semantic instruction cannot be a non-semantic "
|
||||
"instruction";
|
||||
} else {
|
||||
ret = SPV_SUCCESS;
|
||||
}
|
||||
|
@ -34,6 +34,30 @@ namespace {
|
||||
// checked.
|
||||
spv_result_t ModuleScopedInstructions(ValidationState_t& _,
|
||||
const Instruction* inst, SpvOp opcode) {
|
||||
switch (opcode) {
|
||||
case SpvOpExtInst:
|
||||
if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
|
||||
// non-semantic extinst opcodes are allowed beginning in the types
|
||||
// section, but since they must name a return type they cannot be the
|
||||
// first instruction in the types section. Therefore check that we are
|
||||
// already in it.
|
||||
if (_.current_layout_section() < kLayoutTypes) {
|
||||
return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
|
||||
<< "Non-semantic OpExtInst must not appear before types "
|
||||
<< "section";
|
||||
}
|
||||
} else {
|
||||
// otherwise they must be used in a block
|
||||
if (_.current_layout_section() < kLayoutFunctionDefinitions) {
|
||||
return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
|
||||
<< spvOpcodeString(opcode) << " must appear in a block";
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
|
||||
_.ProgressToNextLayoutSectionOrder();
|
||||
|
||||
@ -144,6 +168,29 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
|
||||
}
|
||||
break;
|
||||
|
||||
case SpvOpExtInst:
|
||||
if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
|
||||
// non-semantic extinst opcodes are allowed beginning in the types
|
||||
// section, but must either be placed outside a function declaration,
|
||||
// or inside a block.
|
||||
if (_.current_layout_section() < kLayoutTypes) {
|
||||
return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
|
||||
<< "Non-semantic OpExtInst must not appear before types "
|
||||
<< "section";
|
||||
} else if (_.in_function_body() && _.in_block() == false) {
|
||||
return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
|
||||
<< "Non-semantic OpExtInst within function definition must "
|
||||
"appear in a block";
|
||||
}
|
||||
} else {
|
||||
// otherwise they must be used in a block
|
||||
if (_.in_block() == false) {
|
||||
return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
|
||||
<< spvOpcodeString(opcode) << " must appear in a block";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_.current_layout_section() == kLayoutFunctionDeclarations &&
|
||||
_.in_function_body()) {
|
||||
|
@ -536,7 +536,7 @@ spv_result_t ValidateTypeFunction(ValidationState_t& _,
|
||||
for (auto& pair : inst->uses()) {
|
||||
const auto* use = pair.first;
|
||||
if (use->opcode() != SpvOpFunction && !spvOpcodeIsDebug(use->opcode()) &&
|
||||
!spvOpcodeIsDecoration(use->opcode())) {
|
||||
!use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, use)
|
||||
<< "Invalid use of function type result id "
|
||||
<< _.getIdName(inst->id()) << ".";
|
||||
|
@ -93,6 +93,9 @@ bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
||||
case SpvOpLine:
|
||||
case SpvOpNoLine:
|
||||
case SpvOpUndef:
|
||||
// SpvOpExtInst is only allowed here for certain extended instruction
|
||||
// sets. This will be checked separately
|
||||
case SpvOpExtInst:
|
||||
out = true;
|
||||
break;
|
||||
default: break;
|
||||
|
@ -102,6 +102,7 @@ set(TEST_SOURCES
|
||||
enum_set_test.cpp
|
||||
ext_inst.debuginfo_test.cpp
|
||||
ext_inst.glsl_test.cpp
|
||||
ext_inst.non_semantic_test.cpp
|
||||
ext_inst.opencl_test.cpp
|
||||
fix_word_test.cpp
|
||||
generator_magic_number_test.cpp
|
||||
|
90
test/ext_inst.non_semantic_test.cpp
Normal file
90
test/ext_inst.non_semantic_test.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2015-2016 The Khronos Group Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Assembler tests for non-semantic extended instructions
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
namespace spvtools {
|
||||
namespace {
|
||||
|
||||
using NonSemanticRoundTripTest = RoundTripTest;
|
||||
using NonSemanticTextToBinaryTest = spvtest::TextToBinaryTest;
|
||||
|
||||
TEST_F(NonSemanticRoundTripTest, NonSemanticInsts) {
|
||||
std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
|
||||
%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpExtInst %2 %1 132384681 %2
|
||||
%4 = OpTypeInt 32 0
|
||||
%5 = OpConstant %4 123
|
||||
%6 = OpString "Test string"
|
||||
%7 = OpExtInst %4 %1 82198732 %5 %6
|
||||
%8 = OpExtInstImport "NonSemantic.Testing.AnotherUnknownExtInstSet"
|
||||
%9 = OpExtInst %4 %8 613874321 %7 %5 %6
|
||||
)";
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_0);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(NonSemanticTextToBinaryTest, InvalidExtInstSetName) {
|
||||
std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
|
||||
%1 = OpExtInstImport "NonSemantic_Testing_ExtInst"
|
||||
)";
|
||||
|
||||
EXPECT_THAT(
|
||||
CompileFailure(spirv),
|
||||
Eq("Invalid extended instruction import 'NonSemantic_Testing_ExtInst'"));
|
||||
}
|
||||
|
||||
TEST_F(NonSemanticTextToBinaryTest, NonSemanticIntParameter) {
|
||||
std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
|
||||
%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpExtInst %2 %1 1 99999
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
|
||||
}
|
||||
|
||||
TEST_F(NonSemanticTextToBinaryTest, NonSemanticFloatParameter) {
|
||||
std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
|
||||
%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpExtInst %2 %1 1 3.141592
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
|
||||
}
|
||||
|
||||
TEST_F(NonSemanticTextToBinaryTest, NonSemanticStringParameter) {
|
||||
std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
|
||||
%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpExtInst %2 %1 1 "foobar"
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace spvtools
|
@ -152,6 +152,53 @@ TEST_F(StripDebugStringTest, OpNameRemoved) {
|
||||
/* do_validation */ true);
|
||||
}
|
||||
|
||||
TEST_F(StripDebugStringTest, OpStringRemovedWithNonSemantic) {
|
||||
std::vector<const char*> input{
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpExtension \"SPV_KHR_non_semantic_info\"",
|
||||
"%1 = OpExtInstImport \"NonSemantic.Testing.Set\"",
|
||||
"OpMemoryModel Logical GLSL450",
|
||||
"OpEntryPoint Vertex %2 \"main\"",
|
||||
// this string is not referenced, should be removed fully
|
||||
"%3 = OpString \"minimal.vert\"",
|
||||
"OpName %3 \"bob\"",
|
||||
// this string is referenced and cannot be removed,
|
||||
// but the name should be
|
||||
"%4 = OpString \"secondary.inc\"",
|
||||
"OpName %4 \"sue\"",
|
||||
"%void = OpTypeVoid",
|
||||
"%6 = OpTypeFunction %void",
|
||||
"%2 = OpFunction %void None %6",
|
||||
"%7 = OpLabel",
|
||||
"%8 = OpExtInst %void %1 5 %4",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd",
|
||||
// clang-format on
|
||||
};
|
||||
std::vector<const char*> output{
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpExtension \"SPV_KHR_non_semantic_info\"",
|
||||
"%1 = OpExtInstImport \"NonSemantic.Testing.Set\"",
|
||||
"OpMemoryModel Logical GLSL450",
|
||||
"OpEntryPoint Vertex %2 \"main\"",
|
||||
"%4 = OpString \"secondary.inc\"",
|
||||
"%void = OpTypeVoid",
|
||||
"%6 = OpTypeFunction %void",
|
||||
"%2 = OpFunction %void None %6",
|
||||
"%7 = OpLabel",
|
||||
"%8 = OpExtInst %void %1 5 %4",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd",
|
||||
// clang-format on
|
||||
};
|
||||
SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(input),
|
||||
JoinAllInsts(output),
|
||||
/* skip_nop = */ false,
|
||||
/* do_validation */ true);
|
||||
}
|
||||
|
||||
using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>;
|
||||
|
||||
TEST_P(StripDebugInfoTest, Kind) {
|
||||
|
@ -70,6 +70,7 @@ add_spvtools_unittest(TARGET val_fghijklmnop
|
||||
val_memory_test.cpp
|
||||
val_misc_test.cpp
|
||||
val_modes_test.cpp
|
||||
val_non_semantic_test.cpp
|
||||
val_non_uniform_test.cpp
|
||||
val_opencl_test.cpp
|
||||
val_primitives_test.cpp
|
||||
|
@ -315,6 +315,243 @@ OpNop
|
||||
"non-OpPhi instructions"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) {
|
||||
const std::string body = R"(
|
||||
OpSelectionMerge %end_label None
|
||||
OpBranchConditional %true %true_label %false_label
|
||||
%true_label = OpLabel
|
||||
OpBranch %end_label
|
||||
%false_label = OpLabel
|
||||
OpBranch %end_label
|
||||
%end_label = OpLabel
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
%result = OpPhi %bool %true %true_label %false %false_label
|
||||
)";
|
||||
|
||||
const std::string extra = R"(OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body, extra));
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpPhi must appear within a non-entry block before all "
|
||||
"non-OpPhi instructions"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) {
|
||||
const std::string body = R"(
|
||||
OpSelectionMerge %end_label None
|
||||
OpBranchConditional %true %true_label %false_label
|
||||
%true_label = OpLabel
|
||||
OpBranch %end_label
|
||||
%false_label = OpLabel
|
||||
OpBranch %end_label
|
||||
%end_label = OpLabel
|
||||
%result1 = OpPhi %bool %true %true_label %false %false_label
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
%result2 = OpPhi %bool %true %true_label %false %false_label
|
||||
)";
|
||||
|
||||
const std::string extra = R"(OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body, extra));
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpPhi must appear within a non-entry block before all "
|
||||
"non-OpPhi instructions"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) {
|
||||
const std::string body = R"(
|
||||
OpSelectionMerge %end_label None
|
||||
OpBranchConditional %true %true_label %false_label
|
||||
%true_label = OpLabel
|
||||
OpBranch %end_label
|
||||
%false_label = OpLabel
|
||||
OpBranch %end_label
|
||||
%end_label = OpLabel
|
||||
OpLine %string 0 0
|
||||
%result = OpPhi %bool %true %true_label %false %false_label
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
)";
|
||||
|
||||
const std::string extra = R"(OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body, extra));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) {
|
||||
const std::string body = R"(
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
|
||||
%string = OpString ""
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%true = OpConstantTrue %bool
|
||||
%false = OpConstantFalse %bool
|
||||
%zero = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%func = OpTypeFunction %void
|
||||
%func_int = OpTypePointer Function %int
|
||||
%paramfunc_type = OpTypeFunction %void %int %int
|
||||
|
||||
%paramfunc = OpFunction %void None %paramfunc_type
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
%a = OpFunctionParameter %int
|
||||
%b = OpFunctionParameter %int
|
||||
%paramfunc_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
%main_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(body);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Non-semantic OpExtInst within function definition "
|
||||
"must appear in a block"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) {
|
||||
const std::string body = R"(
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
|
||||
%string = OpString ""
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%true = OpConstantTrue %bool
|
||||
%false = OpConstantFalse %bool
|
||||
%zero = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%func = OpTypeFunction %void
|
||||
%func_int = OpTypePointer Function %int
|
||||
%paramfunc_type = OpTypeFunction %void %int %int
|
||||
|
||||
%paramfunc = OpFunction %void None %paramfunc_type
|
||||
%a = OpFunctionParameter %int
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
%b = OpFunctionParameter %int
|
||||
%paramfunc_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
%main_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(body);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Non-semantic OpExtInst within function definition "
|
||||
"must appear in a block"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) {
|
||||
const std::string body = R"(
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
|
||||
%string = OpString ""
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%true = OpConstantTrue %bool
|
||||
%false = OpConstantFalse %bool
|
||||
%zero = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%func = OpTypeFunction %void
|
||||
%func_int = OpTypePointer Function %int
|
||||
%paramfunc_type = OpTypeFunction %void %int %int
|
||||
|
||||
%paramfunc = OpFunction %void None %paramfunc_type
|
||||
%a = OpFunctionParameter %int
|
||||
%b = OpFunctionParameter %int
|
||||
%paramfunc_entry = OpLabel
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
%main_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(body);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) {
|
||||
const std::string body = R"(
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
|
||||
%string = OpString ""
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%true = OpConstantTrue %bool
|
||||
%false = OpConstantFalse %bool
|
||||
%zero = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%func = OpTypeFunction %void
|
||||
%func_int = OpTypePointer Function %int
|
||||
%paramfunc_type = OpTypeFunction %void %int %int
|
||||
|
||||
%paramfunc = OpFunction %void None %paramfunc_type
|
||||
%a = OpFunctionParameter %int
|
||||
%b = OpFunctionParameter %int
|
||||
%paramfunc_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%dummy = OpExtInst %void %extinst 123 %int_1
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
%main_entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(body);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
|
||||
const std::string body = R"(
|
||||
OpLine %string 1 1
|
||||
|
195
test/val/val_non_semantic_test.cpp
Normal file
195
test/val/val_non_semantic_test.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2019 Google LLC.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Validation tests for non-semantic instructions
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/unit_spirv.h"
|
||||
#include "test/val/val_code_generator.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
struct TestResult {
|
||||
TestResult(spv_result_t in_validation_result = SPV_SUCCESS,
|
||||
const char* in_error_str = nullptr,
|
||||
const char* in_error_str2 = nullptr)
|
||||
: validation_result(in_validation_result),
|
||||
error_str(in_error_str),
|
||||
error_str2(in_error_str2) {}
|
||||
spv_result_t validation_result;
|
||||
const char* error_str;
|
||||
const char* error_str2;
|
||||
};
|
||||
|
||||
using ::testing::Combine;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Values;
|
||||
using ::testing::ValuesIn;
|
||||
|
||||
using ValidateNonSemanticGenerated = spvtest::ValidateBase<
|
||||
std::tuple<bool, bool, const char*, const char*, TestResult>>;
|
||||
using ValidateNonSemanticString = spvtest::ValidateBase<bool>;
|
||||
|
||||
CodeGenerator GetNonSemanticCodeGenerator(const bool declare_ext,
|
||||
const bool declare_extinst,
|
||||
const char* const global_extinsts,
|
||||
const char* const function_extinsts) {
|
||||
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
|
||||
|
||||
if (declare_ext) {
|
||||
generator.extensions_ += "OpExtension \"SPV_KHR_non_semantic_info\"\n";
|
||||
}
|
||||
if (declare_extinst) {
|
||||
generator.extensions_ +=
|
||||
"%extinst = OpExtInstImport \"NonSemantic.Testing.Set\"\n";
|
||||
}
|
||||
|
||||
generator.after_types_ = global_extinsts;
|
||||
|
||||
generator.before_types_ = "%decorate_group = OpDecorationGroup";
|
||||
|
||||
EntryPoint entry_point;
|
||||
entry_point.name = "main";
|
||||
entry_point.execution_model = "Vertex";
|
||||
|
||||
entry_point.body = R"(
|
||||
)";
|
||||
entry_point.body += function_extinsts;
|
||||
generator.entry_points_.push_back(std::move(entry_point));
|
||||
|
||||
return generator;
|
||||
}
|
||||
|
||||
TEST_P(ValidateNonSemanticGenerated, InTest) {
|
||||
const bool declare_ext = std::get<0>(GetParam());
|
||||
const bool declare_extinst = std::get<1>(GetParam());
|
||||
const char* const global_extinsts = std::get<2>(GetParam());
|
||||
const char* const function_extinsts = std::get<3>(GetParam());
|
||||
const TestResult& test_result = std::get<4>(GetParam());
|
||||
|
||||
CodeGenerator generator = GetNonSemanticCodeGenerator(
|
||||
declare_ext, declare_extinst, global_extinsts, function_extinsts);
|
||||
|
||||
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
|
||||
ASSERT_EQ(test_result.validation_result,
|
||||
ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
if (test_result.error_str) {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
testing::ContainsRegex(test_result.error_str));
|
||||
}
|
||||
if (test_result.error_str2) {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
testing::ContainsRegex(test_result.error_str2));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(false), Values(""),
|
||||
Values(""), Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
MissingOpExtension, ValidateNonSemanticGenerated,
|
||||
Combine(Values(false), Values(true), Values(""), Values(""),
|
||||
Values(TestResult(
|
||||
SPV_ERROR_INVALID_DATA,
|
||||
"NonSemantic extended instruction sets cannot be declared "
|
||||
"without SPV_KHR_non_semantic_info."))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(NoExtInst, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true), Values(""),
|
||||
Values(""), Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SimpleGlobalExtInst, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true),
|
||||
Values("%result = OpExtInst %void %extinst 123 %i32"), Values(""),
|
||||
Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ComplexGlobalExtInst, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true),
|
||||
Values("%result = OpExtInst %void %extinst 123 %i32 %u32_2 "
|
||||
"%f32vec4_1234 %u32_0"),
|
||||
Values(""), Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SimpleFunctionLevelExtInst, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true), Values(""),
|
||||
Values("%result = OpExtInst %void %extinst 123 %i32"),
|
||||
Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
FunctionTypeReference, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true),
|
||||
Values("%result = OpExtInst %void %extinst 123 %func"), Values(""),
|
||||
Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
EntryPointReference, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true), Values(""),
|
||||
Values("%result = OpExtInst %void %extinst 123 %main"),
|
||||
Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DecorationGroupReference, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true), Values(""),
|
||||
Values("%result = OpExtInst %void %extinst 123 %decorate_group"),
|
||||
Values(TestResult())));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnknownIDReference, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true),
|
||||
Values("%result = OpExtInst %void %extinst 123 %undefined_id"),
|
||||
Values(""),
|
||||
Values(TestResult(SPV_ERROR_INVALID_ID,
|
||||
"ID .* has not been defined"))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NonSemanticUseInSemantic, ValidateNonSemanticGenerated,
|
||||
Combine(Values(true), Values(true),
|
||||
Values("%result = OpExtInst %f32 %extinst 123 %i32\n"
|
||||
"%invalid = OpConstantComposite %f32vec2 %f32_0 %result"),
|
||||
Values(""),
|
||||
Values(TestResult(SPV_ERROR_INVALID_ID,
|
||||
"in semantic instruction cannot be a "
|
||||
"non-semantic instruction"))));
|
||||
|
||||
TEST_F(ValidateNonSemanticString, InvalidSectionOpExtInst) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_KHR_non_semantic_info"
|
||||
%extinst = OpExtInstImport "NonSemantic.Testing.Set"
|
||||
%test = OpExtInst %void %extinst 4 %void
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Vertex %main "main"
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
|
||||
// there's no specific error for using an OpExtInst too early, it requires a
|
||||
// type so by definition any use of a type in it will be an undefined ID
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ID 2[%2] has not been defined"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
@ -467,7 +467,8 @@ Options (in lexicographical order):)",
|
||||
printf(R"(
|
||||
--strip-reflect
|
||||
Remove all reflection information. For now, this covers
|
||||
reflection information defined by SPV_GOOGLE_hlsl_functionality1.)");
|
||||
reflection information defined by SPV_GOOGLE_hlsl_functionality1
|
||||
and SPV_KHR_non_semantic_info)");
|
||||
printf(R"(
|
||||
--target-env=<env>
|
||||
Set the target environment. Without this flag the target
|
||||
|
@ -30,6 +30,7 @@ SPV_AMD_gcn_shader
|
||||
SPV_AMD_gpu_shader_half_float
|
||||
SPV_AMD_gpu_shader_int16
|
||||
SPV_AMD_shader_trinary_minmax
|
||||
SPV_KHR_non_semantic_info
|
||||
"""
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user