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
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 3190f4b..8bde13c 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -108,6 +108,9 @@
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
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 2f4c7d6..723de05 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -249,6 +249,11 @@
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;
diff --git a/source/binary.cpp b/source/binary.cpp
index 1d31283..8229e53 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -477,9 +477,22 @@
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: {
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index c116f50..2ba0d3d 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -217,10 +217,18 @@
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;
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 0499e23..6ac5756 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -116,9 +116,21 @@
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,
diff --git a/source/ext_inst.h b/source/ext_inst.h
index a821cc2..b42d82b 100644
--- a/source/ext_inst.h
+++ b/source/ext_inst.h
@@ -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.
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index c68b3e2..e21e680 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -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 @@
} 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;
}
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 9e7fad0..936c966 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -19,12 +19,60 @@
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 @@
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();
diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp
index 984073f..c231ead 100644
--- a/source/opt/strip_reflect_info_pass.cpp
+++ b/source/opt/strip_reflect_info_pass.cpp
@@ -67,9 +67,54 @@
} 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);
diff --git a/source/text.cpp b/source/text.cpp
index d88d4f7..fb475d8 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -242,14 +242,37 @@
// 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: {
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 1fa855f..e30bfbc 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -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 @@
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 {
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 2695280..df38f1b 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -286,7 +286,8 @@
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, "
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index d513a25..3b44833 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1275,6 +1275,7 @@
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)
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index 1a64605..070cc4c 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -61,8 +61,8 @@
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 @@
}
}
+ 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;
}
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index b983194..f995ab3 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -87,7 +87,8 @@
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())
<< ".";
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index d80e1b8..7406330 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -167,7 +167,8 @@
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 @@
<< "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 @@
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;
}
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index 53c2835..259befe 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -34,6 +34,30 @@
// 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 @@
}
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()) {
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 1f171cf..5924c69 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -536,7 +536,7 @@
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()) << ".";
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 20eaf88..51aebbe 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -93,6 +93,9 @@
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;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 3dca430..e6857e0 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -102,6 +102,7 @@
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
diff --git a/test/ext_inst.non_semantic_test.cpp b/test/ext_inst.non_semantic_test.cpp
new file mode 100644
index 0000000..870684e
--- /dev/null
+++ b/test/ext_inst.non_semantic_test.cpp
@@ -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
diff --git a/test/opt/strip_debug_info_test.cpp b/test/opt/strip_debug_info_test.cpp
index 2f2ff46..088bba9 100644
--- a/test/opt/strip_debug_info_test.cpp
+++ b/test/opt/strip_debug_info_test.cpp
@@ -152,6 +152,53 @@
/* 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) {
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index d4bfe1d..138e711 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -70,6 +70,7 @@
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
diff --git a/test/val/val_adjacency_test.cpp b/test/val/val_adjacency_test.cpp
index e61c03d..0b09de0 100644
--- a/test/val/val_adjacency_test.cpp
+++ b/test/val/val_adjacency_test.cpp
@@ -315,6 +315,243 @@
"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
diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp
new file mode 100644
index 0000000..b80bb1a
--- /dev/null
+++ b/test/val/val_non_semantic_test.cpp
@@ -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
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 4364e3e..0ff937a 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -467,7 +467,8 @@
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
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index ed24bd0..f6c671e 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -30,6 +30,7 @@
SPV_AMD_gpu_shader_half_float
SPV_AMD_gpu_shader_int16
SPV_AMD_shader_trinary_minmax
+SPV_KHR_non_semantic_info
"""