Update SPIR-V Tools to 54cd5e196
Changes:
54cd5e196 spirv-opt : SPV_NV_bindless_texture related changes (#4870)
b5d0bf285 Require ColMajor or RowMajor for matrices (#4878)
a90ccc240 Remove default copy constructor in header. (#4879)
4773879b6 Update structure layout validation (#4876)
8dc0030ec spirv-as: Avoid overflow when parsing exponents on hex floats (#4874)
cc5fca057 spirv-val: Fix Vulkan memory scope (#4869)
388ce0ee6 spirv-as: Avoid recursion when skipping whitespace (#4866)
e4cfa190d spirv-val: Add SPV_KHR_ray_query (#4848)
60615b8ec Implement SPV_NV_bindless_texture related changes (#4847)
93ebf698a spirv-val: Add OpConvertUToAccelerationStructureKHR (#4838)
e2cf76930 spirv-val: Label VUID 06925 (#4852)
faa8d6a65 Revert "Optimize DefUseManager allocations (#4709)" (#4846)
69e1deabc Fix small bug traversing users in interface_var_sroa (#4850)
bc5c8760a spirv-val: Add Vulkan decoration interface (#4831)
05de65037 Use structural reachability in CFG checks (#4849)
dcee3a5de Update validator diagnostics with "structurally dominated" (#4844)
5f4284aa7 Add limit for scalar replacment when fuzzing (#4843)
92fe420c8 Reduce load size does not work for array with spec const size (#4845)
d5a3bfcf2 Avoid undefined behaviour when getting debug opcode (#4842)
6803cc512 use exec_tools instead of tools for better RBE compatibility (#4837)
32622ba7c DCE: clean up the cfg for all functions that were processed. (#4840)
8ec4a0772 Fix unreachable loop increment (#4836)
286e9c118 Use structural dominance to validate cfg (#4832)
91572e769 Write binary files to stdout in binary on windows. (#4834)
0b824324b Fix segfault in `SpirvTools::Disassemble` when printing (#4833)
66d88508d Build struct order only for the section needed when unrolling. (#4830)
f2dfa53ae Avoid unrolling large loops while fuzzing (#4835)
37d2396ca Fix SplitLoopHeader to handle single block loop (#4829)
3c9fd7577 Avoid if-conversion if both predecessors are the same (#4826)
c4ed5157d Fixed crash unrolling loops with residual iterations (#4820)
76fe35219 Fail validation when RelaxedPrecision is applied to a type. (#4823)
845d98d46 Do not check if the binary changed if encoding is different (#4824)
4f321f862 Avoid undefined divide-by-0 (#4821)
2eff41e70 Remove stray output to stdout from tests (#4816)
fbcb6cf4c Ability to fold Constant Vector times Matrix and Matrix times vector instructions (#4818)
bfc611b03 spirv-val: Label 06807 and 06808 VUID (#4817)
76ebfb989 Avoid replacing access chain with OOB access (#4819)
8f7f5024f Simplify invocation of snprintf (#4815)
fad68a755 Fix usage of sprintf. (#4811)
044ff1aab spirv-val: Add support for SPV_AMD_shader_early_and_late_fragment_tests (#4812)
c94501352 spirv-val: Optimize struct field decoration lookup (#4809)
Commands:
./third_party/update-spirvtools.sh
Bug: b/123642959
Change-Id: I5868aa33245f9ad3b9e2b36a13dcbee1105f0bc2
diff --git a/third_party/SPIRV-Tools/BUILD.bazel b/third_party/SPIRV-Tools/BUILD.bazel
index 914619a..35dfd66 100644
--- a/third_party/SPIRV-Tools/BUILD.bazel
+++ b/third_party/SPIRV-Tools/BUILD.bazel
@@ -82,7 +82,7 @@
outs = ["generators.inc"],
cmd = "$(location generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
cmd_bat = "$(location //:generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
- tools = [":generate_registry_tables"],
+ exec_tools = [":generate_registry_tables"],
)
py_binary(
@@ -96,7 +96,7 @@
outs = ["build-version.inc"],
cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $(location CHANGES) $(location build-version.inc)",
cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location //:update_build_version) $(location CHANGES) $(location build-version.inc)",
- tools = [":update_build_version"],
+ exec_tools = [":update_build_version"],
)
# Libraries
diff --git a/third_party/SPIRV-Tools/BUILD.gn b/third_party/SPIRV-Tools/BUILD.gn
index bea87e5..ca0fd91 100644
--- a/third_party/SPIRV-Tools/BUILD.gn
+++ b/third_party/SPIRV-Tools/BUILD.gn
@@ -469,7 +469,6 @@
"source/util/make_unique.h",
"source/util/parse_number.cpp",
"source/util/parse_number.h",
- "source/util/pooled_linked_list.h",
"source/util/small_vector.h",
"source/util/string_utils.cpp",
"source/util/string_utils.h",
@@ -534,6 +533,7 @@
"source/val/validate_mode_setting.cpp",
"source/val/validate_non_uniform.cpp",
"source/val/validate_primitives.cpp",
+ "source/val/validate_ray_query.cpp",
"source/val/validate_scopes.cpp",
"source/val/validate_scopes.h",
"source/val/validate_small_type_uses.cpp",
diff --git a/third_party/SPIRV-Tools/DEPS b/third_party/SPIRV-Tools/DEPS
index 808ef6c..78c9dd3 100644
--- a/third_party/SPIRV-Tools/DEPS
+++ b/third_party/SPIRV-Tools/DEPS
@@ -6,7 +6,7 @@
'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133',
'googletest_revision': '548b13dc3c02b93f60eeff9a0cc6e11c1ea722ca',
're2_revision': '5723bb8950318135ed9cf4fc76bed988a087f536',
- 'spirv_headers_revision': '5a121866927a16ab9d49bed4788b532c7fcea766',
+ 'spirv_headers_revision': 'b2a156e1c0434bc8c99aaebba1c7be98be7ac580',
}
deps = {
diff --git a/third_party/SPIRV-Tools/build_defs.bzl b/third_party/SPIRV-Tools/build_defs.bzl
index ef9a829..7189137 100644
--- a/third_party/SPIRV-Tools/build_defs.bzl
+++ b/third_party/SPIRV-Tools/build_defs.bzl
@@ -76,7 +76,7 @@
"--core-insts-output=$(location {3}) " +
"--operand-kinds-output=$(location {4})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -113,7 +113,7 @@
"--extension-enum-output=$(location {3}) " +
"--enum-string-mapping-output=$(location {4})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -139,7 +139,7 @@
"--extinst-opencl-grammar=$(location {0}) " +
"--opencl-insts-output=$(location {1})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -165,7 +165,7 @@
"--extinst-glsl-grammar=$(location {0}) " +
"--glsl-insts-output=$(location {1})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -193,7 +193,7 @@
"--vendor-insts-output=$(location {1}) " +
"--vendor-operand-kind-prefix={2}"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -216,7 +216,7 @@
"--extinst-grammar=$< " +
"--extinst-output-path=$(location {0})"
).format(*fmtargs),
- tools = [":generate_language_headers"],
+ exec_tools = [":generate_language_headers"],
visibility = ["//visibility:private"],
)
diff --git a/third_party/SPIRV-Tools/source/CMakeLists.txt b/third_party/SPIRV-Tools/source/CMakeLists.txt
index 98559b8..1ceb78f 100644
--- a/third_party/SPIRV-Tools/source/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/CMakeLists.txt
@@ -228,7 +228,6 @@
${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h
- ${CMAKE_CURRENT_SOURCE_DIR}/util/pooled_linked_list.h
${CMAKE_CURRENT_SOURCE_DIR}/util/small_vector.h
${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/util/timer.h
@@ -323,6 +322,7 @@
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_query.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp
diff --git a/third_party/SPIRV-Tools/source/cfa.h b/third_party/SPIRV-Tools/source/cfa.h
index 7cadf55..f55a7bd 100644
--- a/third_party/SPIRV-Tools/source/cfa.h
+++ b/third_party/SPIRV-Tools/source/cfa.h
@@ -68,6 +68,8 @@
/// CFG following postorder traversal semantics
/// @param[in] backedge A function that will be called when a backedge is
/// encountered during a traversal
+ /// @param[in] terminal A function that will be called to determine if the
+ /// search should stop at the given node.
/// NOTE: The @p successor_func and predecessor_func each return a pointer to
/// a
/// collection such that iterators to that collection remain valid for the
@@ -76,7 +78,8 @@
const BB* entry, get_blocks_func successor_func,
std::function<void(cbb_ptr)> preorder,
std::function<void(cbb_ptr)> postorder,
- std::function<void(cbb_ptr, cbb_ptr)> backedge);
+ std::function<void(cbb_ptr, cbb_ptr)> backedge,
+ std::function<bool(cbb_ptr)> terminal);
/// @brief Calculates dominator edges for a set of blocks
///
@@ -138,7 +141,8 @@
const BB* entry, get_blocks_func successor_func,
std::function<void(cbb_ptr)> preorder,
std::function<void(cbb_ptr)> postorder,
- std::function<void(cbb_ptr, cbb_ptr)> backedge) {
+ std::function<void(cbb_ptr, cbb_ptr)> backedge,
+ std::function<bool(cbb_ptr)> terminal) {
std::unordered_set<uint32_t> processed;
/// NOTE: work_list is the sequence of nodes from the root node to the node
@@ -152,7 +156,7 @@
while (!work_list.empty()) {
block_info& top = work_list.back();
- if (top.iter == end(*successor_func(top.block))) {
+ if (terminal(top.block) || top.iter == end(*successor_func(top.block))) {
postorder(top.block);
work_list.pop_back();
} else {
@@ -266,11 +270,13 @@
auto mark_visited = [&visited](const BB* b) { visited.insert(b); };
auto ignore_block = [](const BB*) {};
auto ignore_blocks = [](const BB*, const BB*) {};
+ auto no_terminal_blocks = [](const BB*) { return false; };
auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block,
- &ignore_blocks](const BB* entry) {
+ &ignore_blocks,
+ &no_terminal_blocks](const BB* entry) {
DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block,
- ignore_blocks);
+ ignore_blocks, no_terminal_blocks);
};
std::vector<BB*> result;
diff --git a/third_party/SPIRV-Tools/source/disassemble.h b/third_party/SPIRV-Tools/source/disassemble.h
index 8eacb10..b520a1e 100644
--- a/third_party/SPIRV-Tools/source/disassemble.h
+++ b/third_party/SPIRV-Tools/source/disassemble.h
@@ -25,9 +25,10 @@
// Decodes the given SPIR-V instruction binary representation to its assembly
// text. The context is inferred from the provided module binary. The options
-// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
-// be stored into *text. Any error will be written into *diagnostic if
-// diagnostic is non-null.
+// parameter is a bit field of spv_binary_to_text_options_t (note: the option
+// SPV_BINARY_TO_TEXT_OPTION_PRINT will be ignored). Decoded text will be
+// stored into *text. Any error will be written into *diagnostic if diagnostic
+// is non-null.
std::string spvInstructionBinaryToText(const spv_target_env env,
const uint32_t* inst_binary,
const size_t inst_word_count,
diff --git a/third_party/SPIRV-Tools/source/libspirv.cpp b/third_party/SPIRV-Tools/source/libspirv.cpp
index 0bc0935..be76caa 100644
--- a/third_party/SPIRV-Tools/source/libspirv.cpp
+++ b/third_party/SPIRV-Tools/source/libspirv.cpp
@@ -99,7 +99,9 @@
spv_text spvtext = nullptr;
spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size,
options, &spvtext, nullptr);
- if (status == SPV_SUCCESS) {
+ if (status == SPV_SUCCESS &&
+ (options & SPV_BINARY_TO_TEXT_OPTION_PRINT) == 0) {
+ assert(spvtext);
text->assign(spvtext->str, spvtext->str + spvtext->length);
}
spvTextDestroy(spvtext);
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
index 2486242..ffb499f 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -659,9 +659,14 @@
InitializeModuleScopeLiveInstructions();
- // Process all entry point functions.
- ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); };
- modified |= context()->ProcessReachableCallTree(pfn);
+ // Run |AggressiveDCE| on the remaining functions. The order does not matter,
+ // since |AggressiveDCE| is intra-procedural. This can mean that function
+ // will become dead if all function call to them are removed. These dead
+ // function will still be in the module after this pass. We expect this to be
+ // rare.
+ for (Function& fp : *context()->module()) {
+ modified |= AggressiveDCE(&fp);
+ }
// If the decoration manager is kept live then the context will try to keep it
// up to date. ADCE deals with group decorations by changing the operands in
@@ -687,8 +692,9 @@
}
// Cleanup all CFG including all unreachable blocks.
- ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); };
- modified |= context()->ProcessReachableCallTree(cleanup);
+ for (Function& fp : *context()->module()) {
+ modified |= CFGCleanup(&fp);
+ }
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
diff --git a/third_party/SPIRV-Tools/source/opt/cfg.cpp b/third_party/SPIRV-Tools/source/opt/cfg.cpp
index ac0fcc3..66b1aed 100644
--- a/third_party/SPIRV-Tools/source/opt/cfg.cpp
+++ b/third_party/SPIRV-Tools/source/opt/cfg.cpp
@@ -74,6 +74,12 @@
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* order) {
+ ComputeStructuredOrder(func, root, nullptr, order);
+}
+
+void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
+ BasicBlock* end,
+ std::list<BasicBlock*>* order) {
assert(module_->context()->get_feature_mgr()->HasCapability(
SpvCapabilityShader) &&
"This only works on structured control flow");
@@ -82,6 +88,8 @@
ComputeStructuredSuccessors(func);
auto ignore_block = [](cbb_ptr) {};
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
+ auto terminal = [end](cbb_ptr bb) { return bb == end; };
+
auto get_structured_successors = [this](const BasicBlock* b) {
return &(block2structured_succs_[b]);
};
@@ -92,7 +100,8 @@
order->push_front(const_cast<BasicBlock*>(b));
};
CFA<BasicBlock>::DepthFirstTraversal(root, get_structured_successors,
- ignore_block, post_order, ignore_edge);
+ ignore_block, post_order, ignore_edge,
+ terminal);
}
void CFG::ForEachBlockInPostOrder(BasicBlock* bb,
@@ -205,7 +214,7 @@
// Find the back edge
BasicBlock* latch_block = nullptr;
Function::iterator latch_block_iter = header_it;
- while (++latch_block_iter != fn->end()) {
+ for (; latch_block_iter != fn->end(); ++latch_block_iter) {
// If blocks are in the proper order, then the only branch that appears
// after the header is the latch.
if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) !=
@@ -237,6 +246,15 @@
context->set_instr_block(inst, new_header);
});
+ // If |bb| was the latch block, the branch back to the header is not in
+ // |new_header|.
+ if (latch_block == bb) {
+ if (new_header->ContinueBlockId() == bb->id()) {
+ new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id});
+ }
+ latch_block = new_header;
+ }
+
// Adjust the OpPhi instructions as needed.
bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
std::vector<uint32_t> preheader_phi_ops;
diff --git a/third_party/SPIRV-Tools/source/opt/cfg.h b/third_party/SPIRV-Tools/source/opt/cfg.h
index 33412f1..fa4fef2 100644
--- a/third_party/SPIRV-Tools/source/opt/cfg.h
+++ b/third_party/SPIRV-Tools/source/opt/cfg.h
@@ -66,6 +66,14 @@
void ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* order);
+ // Compute structured block order into |order| for |func| starting at |root|
+ // and ending at |end|. This order has the property that dominators come
+ // before all blocks they dominate, merge blocks come after all blocks that
+ // are in the control constructs of their header, and continue blocks come
+ // after all the blocks in the body of their loop.
+ void ComputeStructuredOrder(Function* func, BasicBlock* root, BasicBlock* end,
+ std::list<BasicBlock*>* order);
+
// Applies |f| to all blocks that can be reach from |bb| in post order.
void ForEachBlockInPostOrder(BasicBlock* bb,
const std::function<void(BasicBlock*)>& f);
diff --git a/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp b/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp
index 249e11e..cb36087 100644
--- a/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp
+++ b/third_party/SPIRV-Tools/source/opt/const_folding_rules.cpp
@@ -251,6 +251,193 @@
};
}
+ConstantFoldingRule FoldVectorTimesMatrix() {
+ return [](IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>& constants)
+ -> const analysis::Constant* {
+ assert(inst->opcode() == SpvOpVectorTimesMatrix);
+ analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+ if (!inst->IsFloatingPointFoldingAllowed()) {
+ if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+ return nullptr;
+ }
+ }
+
+ const analysis::Constant* c1 = constants[0];
+ const analysis::Constant* c2 = constants[1];
+
+ if (c1 == nullptr || c2 == nullptr) {
+ return nullptr;
+ }
+
+ // Check result type.
+ const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+ const analysis::Vector* vector_type = result_type->AsVector();
+ assert(vector_type != nullptr);
+ const analysis::Type* element_type = vector_type->element_type();
+ assert(element_type != nullptr);
+ const analysis::Float* float_type = element_type->AsFloat();
+ assert(float_type != nullptr);
+
+ // Check types of c1 and c2.
+ assert(c1->type()->AsVector() == vector_type);
+ assert(c1->type()->AsVector()->element_type() == element_type &&
+ c2->type()->AsMatrix()->element_type() == vector_type);
+
+ // Get a float vector that is the result of vector-times-matrix.
+ std::vector<const analysis::Constant*> c1_components =
+ c1->GetVectorComponents(const_mgr);
+ std::vector<const analysis::Constant*> c2_components =
+ c2->AsMatrixConstant()->GetComponents();
+ uint32_t resultVectorSize = result_type->AsVector()->element_count();
+
+ std::vector<uint32_t> ids;
+
+ if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
+ std::vector<uint32_t> words(float_type->width() / 32, 0);
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+
+ if (float_type->width() == 32) {
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ float result_scalar = 0.0f;
+ const analysis::VectorConstant* c2_vec =
+ c2_components[i]->AsVectorConstant();
+ for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+ float c1_scalar = c1_components[j]->GetFloat();
+ float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<float> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ } else if (float_type->width() == 64) {
+ for (uint32_t i = 0; i < c2_components.size(); ++i) {
+ double result_scalar = 0.0;
+ const analysis::VectorConstant* c2_vec =
+ c2_components[i]->AsVectorConstant();
+ for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+ double c1_scalar = c1_components[j]->GetDouble();
+ double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<double> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+ return nullptr;
+ };
+}
+
+ConstantFoldingRule FoldMatrixTimesVector() {
+ return [](IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>& constants)
+ -> const analysis::Constant* {
+ assert(inst->opcode() == SpvOpMatrixTimesVector);
+ analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+ if (!inst->IsFloatingPointFoldingAllowed()) {
+ if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+ return nullptr;
+ }
+ }
+
+ const analysis::Constant* c1 = constants[0];
+ const analysis::Constant* c2 = constants[1];
+
+ if (c1 == nullptr || c2 == nullptr) {
+ return nullptr;
+ }
+
+ // Check result type.
+ const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+ const analysis::Vector* vector_type = result_type->AsVector();
+ assert(vector_type != nullptr);
+ const analysis::Type* element_type = vector_type->element_type();
+ assert(element_type != nullptr);
+ const analysis::Float* float_type = element_type->AsFloat();
+ assert(float_type != nullptr);
+
+ // Check types of c1 and c2.
+ assert(c1->type()->AsMatrix()->element_type() == vector_type);
+ assert(c2->type()->AsVector()->element_type() == element_type);
+
+ // Get a float vector that is the result of matrix-times-vector.
+ std::vector<const analysis::Constant*> c1_components =
+ c1->AsMatrixConstant()->GetComponents();
+ std::vector<const analysis::Constant*> c2_components =
+ c2->GetVectorComponents(const_mgr);
+ uint32_t resultVectorSize = result_type->AsVector()->element_count();
+
+ std::vector<uint32_t> ids;
+
+ if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
+ std::vector<uint32_t> words(float_type->width() / 32, 0);
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+
+ if (float_type->width() == 32) {
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ float result_scalar = 0.0f;
+ for (uint32_t j = 0; j < c1_components.size(); ++j) {
+ float c1_scalar = c1_components[j]
+ ->AsVectorConstant()
+ ->GetComponents()[i]
+ ->GetFloat();
+ float c2_scalar = c2_components[j]->GetFloat();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<float> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ } else if (float_type->width() == 64) {
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ double result_scalar = 0.0;
+ for (uint32_t j = 0; j < c1_components.size(); ++j) {
+ double c1_scalar = c1_components[j]
+ ->AsVectorConstant()
+ ->GetComponents()[i]
+ ->GetDouble();
+ double c2_scalar = c2_components[j]->GetDouble();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<double> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+ return nullptr;
+ };
+}
+
ConstantFoldingRule FoldCompositeWithConstants() {
// Folds an OpCompositeConstruct where all of the inputs are constants to a
// constant. A new constant is created if necessary.
@@ -1288,6 +1475,8 @@
rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants());
rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar());
+ rules_[SpvOpVectorTimesMatrix].push_back(FoldVectorTimesMatrix());
+ rules_[SpvOpMatrixTimesVector].push_back(FoldMatrixTimesVector());
rules_[SpvOpFNegate].push_back(FoldFNegate());
rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16());
diff --git a/third_party/SPIRV-Tools/source/opt/def_use_manager.cpp b/third_party/SPIRV-Tools/source/opt/def_use_manager.cpp
index e1e441e..d54fdb6 100644
--- a/third_party/SPIRV-Tools/source/opt/def_use_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/def_use_manager.cpp
@@ -13,23 +13,11 @@
// limitations under the License.
#include "source/opt/def_use_manager.h"
-#include "source/util/make_unique.h"
namespace spvtools {
namespace opt {
namespace analysis {
-// Don't compact before we have a reasonable number of ids allocated (~32kb).
-static const size_t kCompactThresholdMinTotalIds = (8 * 1024);
-// Compact when fewer than this fraction of the storage is used (should be 2^n
-// for performance).
-static const size_t kCompactThresholdFractionFreeIds = 8;
-
-DefUseManager::DefUseManager() {
- use_pool_ = MakeUnique<UseListPool>();
- used_id_pool_ = MakeUnique<UsedIdListPool>();
-}
-
void DefUseManager::AnalyzeInstDef(Instruction* inst) {
const uint32_t def_id = inst->result_id();
if (def_id != 0) {
@@ -46,15 +34,15 @@
}
void DefUseManager::AnalyzeInstUse(Instruction* inst) {
- // It might have existed before.
- EraseUseRecordsOfOperandIds(inst);
-
// Create entry for the given instruction. Note that the instruction may
// not have any in-operands. In such cases, we still need a entry for those
// instructions so this manager knows it has seen the instruction later.
- UsedIdList& used_ids =
- inst_to_used_id_.insert({inst, UsedIdList(used_id_pool_.get())})
- .first->second;
+ auto* used_ids = &inst_to_used_ids_[inst];
+ if (used_ids->size()) {
+ EraseUseRecordsOfOperandIds(inst);
+ used_ids = &inst_to_used_ids_[inst];
+ }
+ used_ids->clear(); // It might have existed before.
for (uint32_t i = 0; i < inst->NumOperands(); ++i) {
switch (inst->GetOperand(i).type) {
@@ -66,17 +54,8 @@
uint32_t use_id = inst->GetSingleWordOperand(i);
Instruction* def = GetDef(use_id);
assert(def && "Definition is not registered.");
-
- // Add to inst's use records
- used_ids.push_back(use_id);
-
- // Add to the users, taking care to avoid adding duplicates. We know
- // the duplicate for this instruction will always be at the tail.
- UseList& list = inst_to_users_.insert({def, UseList(use_pool_.get())})
- .first->second;
- if (list.empty() || list.back() != inst) {
- list.push_back(inst);
- }
+ id_to_users_.insert(UserEntry{def, inst});
+ used_ids->push_back(use_id);
} break;
default:
break;
@@ -115,6 +94,23 @@
return iter->second;
}
+DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin(
+ const Instruction* def) const {
+ return id_to_users_.lower_bound(
+ UserEntry{const_cast<Instruction*>(def), nullptr});
+}
+
+bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
+ const IdToUsersMap::const_iterator& cached_end,
+ const Instruction* inst) const {
+ return (iter != cached_end && iter->def == inst);
+}
+
+bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
+ const Instruction* inst) const {
+ return UsersNotEnd(iter, id_to_users_.end(), inst);
+}
+
bool DefUseManager::WhileEachUser(
const Instruction* def, const std::function<bool(Instruction*)>& f) const {
// Ensure that |def| has been registered.
@@ -122,11 +118,9 @@
"Definition is not registered.");
if (!def->HasResultId()) return true;
- auto iter = inst_to_users_.find(def);
- if (iter != inst_to_users_.end()) {
- for (Instruction* user : iter->second) {
- if (!f(user)) return false;
- }
+ auto end = id_to_users_.end();
+ for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
+ if (!f(iter->user)) return false;
}
return true;
}
@@ -157,15 +151,14 @@
"Definition is not registered.");
if (!def->HasResultId()) return true;
- auto iter = inst_to_users_.find(def);
- if (iter != inst_to_users_.end()) {
- for (Instruction* user : iter->second) {
- for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
- const Operand& op = user->GetOperand(idx);
- if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
- if (def->result_id() == op.words[0]) {
- if (!f(user, idx)) return false;
- }
+ auto end = id_to_users_.end();
+ for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
+ Instruction* user = iter->user;
+ for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
+ const Operand& op = user->GetOperand(idx);
+ if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
+ if (def->result_id() == op.words[0]) {
+ if (!f(user, idx)) return false;
}
}
}
@@ -237,18 +230,17 @@
}
void DefUseManager::ClearInst(Instruction* inst) {
- if (inst_to_used_id_.find(inst) != inst_to_used_id_.end()) {
+ auto iter = inst_to_used_ids_.find(inst);
+ if (iter != inst_to_used_ids_.end()) {
EraseUseRecordsOfOperandIds(inst);
- uint32_t const result_id = inst->result_id();
- if (result_id != 0) {
- // For each using instruction, remove result_id from their used ids.
- auto iter = inst_to_users_.find(inst);
- if (iter != inst_to_users_.end()) {
- for (Instruction* use : iter->second) {
- inst_to_used_id_.at(use).remove_first(result_id);
- }
- inst_to_users_.erase(iter);
+ if (inst->result_id() != 0) {
+ // Remove all uses of this inst.
+ auto users_begin = UsersBegin(inst);
+ auto end = id_to_users_.end();
+ auto new_end = users_begin;
+ for (; UsersNotEnd(new_end, end, inst); ++new_end) {
}
+ id_to_users_.erase(users_begin, new_end);
id_to_def_.erase(inst->result_id());
}
}
@@ -257,48 +249,16 @@
void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) {
// Go through all ids used by this instruction, remove this instruction's
// uses of them.
- auto iter = inst_to_used_id_.find(inst);
- if (iter != inst_to_used_id_.end()) {
- const UsedIdList& used_ids = iter->second;
- for (uint32_t def_id : used_ids) {
- auto def_iter = inst_to_users_.find(GetDef(def_id));
- if (def_iter != inst_to_users_.end()) {
- def_iter->second.remove_first(const_cast<Instruction*>(inst));
- }
+ auto iter = inst_to_used_ids_.find(inst);
+ if (iter != inst_to_used_ids_.end()) {
+ for (auto use_id : iter->second) {
+ id_to_users_.erase(
+ UserEntry{GetDef(use_id), const_cast<Instruction*>(inst)});
}
- inst_to_used_id_.erase(inst);
-
- // If we're using only a fraction of the space in used_ids_, compact storage
- // to prevent memory usage from being unbounded.
- if (used_id_pool_->total_nodes() > kCompactThresholdMinTotalIds &&
- used_id_pool_->used_nodes() <
- used_id_pool_->total_nodes() / kCompactThresholdFractionFreeIds) {
- CompactStorage();
- }
+ inst_to_used_ids_.erase(iter);
}
}
-void DefUseManager::CompactStorage() {
- CompactUseRecords();
- CompactUsedIds();
-}
-
-void DefUseManager::CompactUseRecords() {
- std::unique_ptr<UseListPool> new_pool = MakeUnique<UseListPool>();
- for (auto& iter : inst_to_users_) {
- iter.second.move_nodes(new_pool.get());
- }
- use_pool_ = std::move(new_pool);
-}
-
-void DefUseManager::CompactUsedIds() {
- std::unique_ptr<UsedIdListPool> new_pool = MakeUnique<UsedIdListPool>();
- for (auto& iter : inst_to_used_id_) {
- iter.second.move_nodes(new_pool.get());
- }
- used_id_pool_ = std::move(new_pool);
-}
-
bool CompareAndPrintDifferences(const DefUseManager& lhs,
const DefUseManager& rhs) {
bool same = true;
@@ -317,52 +277,34 @@
same = false;
}
- for (const auto& l : lhs.inst_to_used_id_) {
- std::set<uint32_t> ul, ur;
- lhs.ForEachUse(l.first,
- [&ul](Instruction*, uint32_t id) { ul.insert(id); });
- rhs.ForEachUse(l.first,
- [&ur](Instruction*, uint32_t id) { ur.insert(id); });
- if (ul.size() != ur.size()) {
- printf(
- "Diff in inst_to_used_id_: different number of used ids (%zu != %zu)",
- ul.size(), ur.size());
- same = false;
- } else if (ul != ur) {
- printf("Diff in inst_to_used_id_: different used ids\n");
- same = false;
+ if (lhs.id_to_users_ != rhs.id_to_users_) {
+ for (auto p : lhs.id_to_users_) {
+ if (rhs.id_to_users_.count(p) == 0) {
+ printf("Diff in id_to_users: missing value in rhs\n");
+ }
}
- }
- for (const auto& r : rhs.inst_to_used_id_) {
- auto iter_l = lhs.inst_to_used_id_.find(r.first);
- if (r.second.empty() &&
- !(iter_l == lhs.inst_to_used_id_.end() || iter_l->second.empty())) {
- printf("Diff in inst_to_used_id_: unexpected instr in rhs\n");
- same = false;
+ for (auto p : rhs.id_to_users_) {
+ if (lhs.id_to_users_.count(p) == 0) {
+ printf("Diff in id_to_users: missing value in lhs\n");
+ }
}
+ same = false;
}
- for (const auto& l : lhs.inst_to_users_) {
- std::set<Instruction*> ul, ur;
- lhs.ForEachUser(l.first, [&ul](Instruction* use) { ul.insert(use); });
- rhs.ForEachUser(l.first, [&ur](Instruction* use) { ur.insert(use); });
- if (ul.size() != ur.size()) {
- printf("Diff in inst_to_users_: different number of users (%zu != %zu)",
- ul.size(), ur.size());
- same = false;
- } else if (ul != ur) {
- printf("Diff in inst_to_users_: different users\n");
- same = false;
+ if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) {
+ for (auto p : lhs.inst_to_used_ids_) {
+ if (rhs.inst_to_used_ids_.count(p.first) == 0) {
+ printf("Diff in inst_to_used_ids: missing value in rhs\n");
+ }
}
- }
- for (const auto& r : rhs.inst_to_users_) {
- auto iter_l = lhs.inst_to_users_.find(r.first);
- if (r.second.empty() &&
- !(iter_l == lhs.inst_to_users_.end() || iter_l->second.empty())) {
- printf("Diff in inst_to_users_: unexpected instr in rhs\n");
- same = false;
+ for (auto p : rhs.inst_to_used_ids_) {
+ if (lhs.inst_to_used_ids_.count(p.first) == 0) {
+ printf("Diff in inst_to_used_ids: missing value in lhs\n");
+ }
}
+ same = false;
}
+
return same;
}
diff --git a/third_party/SPIRV-Tools/source/opt/def_use_manager.h b/third_party/SPIRV-Tools/source/opt/def_use_manager.h
index cf6cbdf..a8dbbc6 100644
--- a/third_party/SPIRV-Tools/source/opt/def_use_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/def_use_manager.h
@@ -21,7 +21,6 @@
#include "source/opt/instruction.h"
#include "source/opt/module.h"
-#include "source/util/pooled_linked_list.h"
#include "spirv-tools/libspirv.hpp"
namespace spvtools {
@@ -50,6 +49,50 @@
return lhs.operand_index < rhs.operand_index;
}
+// Definition should never be null. User can be null, however, such an entry
+// should be used only for searching (e.g. all users of a particular definition)
+// and never stored in a container.
+struct UserEntry {
+ Instruction* def;
+ Instruction* user;
+};
+
+inline bool operator==(const UserEntry& lhs, const UserEntry& rhs) {
+ return lhs.def == rhs.def && lhs.user == rhs.user;
+}
+
+// Orders UserEntry for use in associative containers (i.e. less than ordering).
+//
+// The definition of an UserEntry is treated as the major key and the users as
+// the minor key so that all the users of a particular definition are
+// consecutive in a container.
+//
+// A null user always compares less than a real user. This is done to provide
+// easy values to search for the beginning of the users of a particular
+// definition (i.e. using {def, nullptr}).
+struct UserEntryLess {
+ bool operator()(const UserEntry& lhs, const UserEntry& rhs) const {
+ // If lhs.def and rhs.def are both null, fall through to checking the
+ // second entries.
+ if (!lhs.def && rhs.def) return true;
+ if (lhs.def && !rhs.def) return false;
+
+ // If neither definition is null, then compare unique ids.
+ if (lhs.def && rhs.def) {
+ if (lhs.def->unique_id() < rhs.def->unique_id()) return true;
+ if (rhs.def->unique_id() < lhs.def->unique_id()) return false;
+ }
+
+ // Return false on equality.
+ if (!lhs.user && !rhs.user) return false;
+ if (!lhs.user) return true;
+ if (!rhs.user) return false;
+
+ // If neither user is null then compare unique ids.
+ return lhs.user->unique_id() < rhs.user->unique_id();
+ }
+};
+
// A class for analyzing and managing defs and uses in an Module.
class DefUseManager {
public:
@@ -59,7 +102,7 @@
// will be communicated to the outside via the given message |consumer|. This
// instance only keeps a reference to the |consumer|, so the |consumer| should
// outlive this instance.
- DefUseManager(Module* module) : DefUseManager() { AnalyzeDefUse(module); }
+ DefUseManager(Module* module) { AnalyzeDefUse(module); }
DefUseManager(const DefUseManager&) = delete;
DefUseManager(DefUseManager&&) = delete;
@@ -171,36 +214,35 @@
// uses.
void UpdateDefUse(Instruction* inst);
- // Compacts any internal storage to save memory.
- void CompactStorage();
-
private:
- using UseList = spvtools::utils::PooledLinkedList<Instruction*>;
- using UseListPool = spvtools::utils::PooledLinkedListNodes<Instruction*>;
- // Stores linked lists of Instructions using a def.
- using InstToUsersMap = std::unordered_map<const Instruction*, UseList>;
+ using IdToUsersMap = std::set<UserEntry, UserEntryLess>;
+ using InstToUsedIdsMap =
+ std::unordered_map<const Instruction*, std::vector<uint32_t>>;
- using UsedIdList = spvtools::utils::PooledLinkedList<uint32_t>;
- using UsedIdListPool = spvtools::utils::PooledLinkedListNodes<uint32_t>;
- // Stores mapping from instruction to their UsedIdRange.
- using InstToUsedIdMap = std::unordered_map<const Instruction*, UsedIdList>;
+ // Returns the first location that {|def|, nullptr} could be inserted into the
+ // users map without violating ordering.
+ IdToUsersMap::const_iterator UsersBegin(const Instruction* def) const;
- DefUseManager();
+ // Returns true if |iter| has not reached the end of |def|'s users.
+ //
+ // In the first version |iter| is compared against the end of the map for
+ // validity before other checks. In the second version, |iter| is compared
+ // against |cached_end| for validity before other checks. This allows caching
+ // the map's end which is a performance improvement on some platforms.
+ bool UsersNotEnd(const IdToUsersMap::const_iterator& iter,
+ const Instruction* def) const;
+ bool UsersNotEnd(const IdToUsersMap::const_iterator& iter,
+ const IdToUsersMap::const_iterator& cached_end,
+ const Instruction* def) const;
// Analyzes the defs and uses in the given |module| and populates data
// structures in this class. Does nothing if |module| is nullptr.
void AnalyzeDefUse(Module* module);
- // Removes unused entries in used_records_ and used_ids_.
- void CompactUseRecords();
- void CompactUsedIds();
-
- IdToDefMap id_to_def_; // Mapping from ids to their definitions
- InstToUsersMap inst_to_users_; // Map from def to uses.
- std::unique_ptr<UseListPool> use_pool_;
-
- std::unique_ptr<UsedIdListPool> used_id_pool_;
- InstToUsedIdMap inst_to_used_id_; // Map from instruction to used ids.
+ IdToDefMap id_to_def_; // Mapping from ids to their definitions
+ IdToUsersMap id_to_users_; // Mapping from ids to their users
+ // Mapping from instructions to the ids used in the instruction.
+ InstToUsedIdsMap inst_to_used_ids_;
};
} // namespace analysis
diff --git a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
index d86de15..d6017bb 100644
--- a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
@@ -59,7 +59,9 @@
PreLambda pre, PostLambda post) {
// Ignore backedge operation.
auto nop_backedge = [](const BBType*, const BBType*) {};
- CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge);
+ auto no_terminal_blocks = [](const BBType*) { return false; };
+ CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge,
+ no_terminal_blocks);
}
// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
diff --git a/third_party/SPIRV-Tools/source/opt/folding_rules.cpp b/third_party/SPIRV-Tools/source/opt/folding_rules.cpp
index 0d8f7c8..2d778b9 100644
--- a/third_party/SPIRV-Tools/source/opt/folding_rules.cpp
+++ b/third_party/SPIRV-Tools/source/opt/folding_rules.cpp
@@ -277,6 +277,11 @@
uint32_t width = c->type()->AsFloat()->width();
assert(width == 32 || width == 64);
std::vector<uint32_t> words;
+
+ if (c->IsZero()) {
+ return 0;
+ }
+
if (width == 64) {
spvtools::utils::FloatProxy<double> result(1.0 / c->GetDouble());
if (!IsValidResult(result.getAsFloat())) return 0;
diff --git a/third_party/SPIRV-Tools/source/opt/if_conversion.cpp b/third_party/SPIRV-Tools/source/opt/if_conversion.cpp
index d1debd0..1232796 100644
--- a/third_party/SPIRV-Tools/source/opt/if_conversion.cpp
+++ b/third_party/SPIRV-Tools/source/opt/if_conversion.cpp
@@ -160,6 +160,11 @@
BasicBlock* inc1 = context()->get_instr_block(preds[1]);
if (dominators->Dominates(block, inc1)) return false;
+ if (inc0 == inc1) {
+ // If the predecessor blocks are the same, then there is only 1 value for
+ // the OpPhi. Other transformation should be able to simplify that.
+ return false;
+ }
// All phis will have the same common dominator, so cache the result
// for this block. If there is no common dominator, then we cannot transform
// any phi in this basic block.
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
index 2cc3125..6e73f1c 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
@@ -508,6 +508,37 @@
delete &*loop_merge_itr;
}
+void InlinePass::UpdateSingleBlockLoopContinueTarget(
+ uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+ auto& header = new_blocks->front();
+ auto* merge_inst = header->GetLoopMergeInst();
+
+ // The back-edge block is split at the branch to create a new back-edge
+ // block. The old block is modified to branch to the new block. The loop
+ // merge instruction is updated to declare the new block as the continue
+ // target. This has the effect of changing the loop from being a large
+ // continue construct and an empty loop construct to being a loop with a loop
+ // construct and a trivial continue construct. This change is made to satisfy
+ // structural dominance.
+
+ // Add the new basic block.
+ std::unique_ptr<BasicBlock> new_block =
+ MakeUnique<BasicBlock>(NewLabel(new_id));
+ auto& old_backedge = new_blocks->back();
+ auto old_branch = old_backedge->tail();
+
+ // Move the old back edge into the new block.
+ std::unique_ptr<Instruction> br(&*old_branch);
+ new_block->AddInstruction(std::move(br));
+
+ // Add a branch to the new block from the old back-edge block.
+ AddBranch(new_id, &old_backedge);
+ new_blocks->push_back(std::move(new_block));
+
+ // Update the loop's continue target to the new block.
+ merge_inst->SetInOperand(1u, {new_id});
+}
+
bool InlinePass::GenInlineCode(
std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
std::vector<std::unique_ptr<Instruction>>* new_vars,
@@ -639,9 +670,19 @@
// Finalize inline code.
new_blocks->push_back(std::move(new_blk_ptr));
- if (caller_is_loop_header && (new_blocks->size() > 1))
+ if (caller_is_loop_header && (new_blocks->size() > 1)) {
MoveLoopMergeInstToFirstBlock(new_blocks);
+ // If the loop was a single basic block previously, update it's structure.
+ auto& header = new_blocks->front();
+ auto* merge_inst = header->GetLoopMergeInst();
+ if (merge_inst->GetSingleWordInOperand(1u) == header->id()) {
+ auto new_id = context()->TakeNextId();
+ if (new_id == 0) return false;
+ UpdateSingleBlockLoopContinueTarget(new_id, new_blocks);
+ }
+ }
+
// Update block map given replacement blocks.
for (auto& blk : *new_blocks) {
id2block_[blk->id()] = &*blk;
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.h b/third_party/SPIRV-Tools/source/opt/inline_pass.h
index 9a5429b..f204395 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.h
@@ -235,6 +235,12 @@
// Move the OpLoopMerge from the last block back to the first.
void MoveLoopMergeInstToFirstBlock(
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+ // Update the structure of single block loops so that the inlined code ends
+ // up in the loop construct and a new continue target is added to satisfy
+ // structural dominance.
+ void UpdateSingleBlockLoopContinueTarget(
+ uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.cpp b/third_party/SPIRV-Tools/source/opt/instruction.cpp
index 418f121..6a8daea 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instruction.cpp
@@ -693,8 +693,12 @@
return NonSemanticShaderDebugInfo100InstructionsMax;
}
- return NonSemanticShaderDebugInfo100Instructions(
- GetSingleWordInOperand(kExtInstInstructionInIdx));
+ uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx);
+ if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
+ }
+
+ return NonSemanticShaderDebugInfo100Instructions(opcode);
}
CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
diff --git a/third_party/SPIRV-Tools/source/opt/interface_var_sroa.cpp b/third_party/SPIRV-Tools/source/opt/interface_var_sroa.cpp
index 58ed897..1b2cb36 100644
--- a/third_party/SPIRV-Tools/source/opt/interface_var_sroa.cpp
+++ b/third_party/SPIRV-Tools/source/opt/interface_var_sroa.cpp
@@ -212,8 +212,12 @@
context()->KillInst(inst);
return;
}
+ std::vector<Instruction*> users;
context()->get_def_use_mgr()->ForEachUser(
- inst, [this](Instruction* user) { KillInstructionAndUsers(user); });
+ inst, [&users](Instruction* user) { users.push_back(user); });
+ for (auto user : users) {
+ context()->KillInst(user);
+ }
context()->KillInst(inst);
}
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
index 97db9d8..734ad55 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
@@ -187,6 +187,8 @@
module_->AddExtInstImport(std::move(spv_inst));
} else if (opcode == SpvOpMemoryModel) {
module_->SetMemoryModel(std::move(spv_inst));
+ } else if (opcode == SpvOpSamplerImageAddressingModeNV) {
+ module_->SetSampledImageAddressMode(std::move(spv_inst));
} else if (opcode == SpvOpEntryPoint) {
module_->AddEntryPoint(std::move(spv_inst));
} else if (opcode == SpvOpExecutionMode ||
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
index da4cac3..9491798 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
@@ -237,7 +237,8 @@
}
// Rule out variables with nested access chains
// TODO(): Convert nested access chains
- if (IsNonPtrAccessChain(op) && ptrInst->GetSingleWordInOperand(
+ bool is_non_ptr_access_chain = IsNonPtrAccessChain(op);
+ if (is_non_ptr_access_chain && ptrInst->GetSingleWordInOperand(
kAccessChainPtrIdInIdx) != varId) {
seen_non_target_vars_.insert(varId);
seen_target_vars_.erase(varId);
@@ -249,6 +250,12 @@
seen_target_vars_.erase(varId);
break;
}
+
+ if (is_non_ptr_access_chain && AnyIndexIsOutOfBounds(ptrInst)) {
+ seen_non_target_vars_.insert(varId);
+ seen_target_vars_.erase(varId);
+ break;
+ }
} break;
default:
break;
@@ -446,5 +453,42 @@
});
}
+bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
+ const Instruction* access_chain_inst) {
+ assert(IsNonPtrAccessChain(access_chain_inst->opcode()));
+
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+ auto constants = const_mgr->GetOperandConstants(access_chain_inst);
+ uint32_t base_pointer_id = access_chain_inst->GetSingleWordInOperand(0);
+ Instruction* base_pointer = get_def_use_mgr()->GetDef(base_pointer_id);
+ const analysis::Pointer* base_pointer_type =
+ type_mgr->GetType(base_pointer->type_id())->AsPointer();
+ assert(base_pointer_type != nullptr &&
+ "The base of the access chain is not a pointer.");
+ const analysis::Type* current_type = base_pointer_type->pointee_type();
+ for (uint32_t i = 1; i < access_chain_inst->NumInOperands(); ++i) {
+ if (IsIndexOutOfBounds(constants[i], current_type)) {
+ return true;
+ }
+
+ uint32_t index =
+ (constants[i]
+ ? static_cast<uint32_t>(constants[i]->GetZeroExtendedValue())
+ : 0);
+ current_type = type_mgr->GetMemberType(current_type, {index});
+ }
+
+ return false;
+}
+
+bool LocalAccessChainConvertPass::IsIndexOutOfBounds(
+ const analysis::Constant* index, const analysis::Type* type) const {
+ if (index == nullptr) {
+ return false;
+ }
+ return index->GetZeroExtendedValue() >= type->NumberOfComponents();
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
index 8548e16..eabf864 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
@@ -111,6 +111,17 @@
// Returns a status to indicate success or failure, and change or no change.
Status ConvertLocalAccessChains(Function* func);
+ // Returns true one of the indexes in the |access_chain_inst| is definitly out
+ // of bounds. If the size of the type or the value of the index is unknown,
+ // then it will be considered in-bounds.
+ bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst);
+
+ // Returns true if getting element |index| from |type| would be out-of-bounds.
+ // If |index| is nullptr or the size of the type are unknown, then it will be
+ // considered in-bounds.
+ bool IsIndexOutOfBounds(const analysis::Constant* index,
+ const analysis::Type* type) const;
+
// Initialize extensions allowlist
void InitExtensions();
diff --git a/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp b/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
index 4feb64e..13982d1 100644
--- a/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
+++ b/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
@@ -497,7 +497,8 @@
// continue blocks that must be copied to retain the structured order.
// The structured order will include these.
std::list<BasicBlock*> order;
- cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
+ cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_,
+ loop_merge_, &order);
for (BasicBlock* bb : order) {
if (bb == GetMergeBlock()) {
break;
diff --git a/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp b/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
index 28ff072..6f4e6f4 100644
--- a/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
+++ b/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
@@ -384,6 +384,7 @@
std::unique_ptr<Instruction> new_label{new Instruction(
context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
+ new_exit_bb->SetParent(&function_);
// Save the id of the block before we move it.
uint32_t new_merge_id = new_exit_bb->id();
@@ -996,6 +997,20 @@
if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr))
return false;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // ClusterFuzz/OSS-Fuzz is likely to yield examples with very high loop
+ // iteration counts. This can cause timeouts and memouts during fuzzing that
+ // are not classed as bugs. To avoid this noise, loop unrolling is not applied
+ // to loops with large iteration counts when fuzzing.
+ const size_t kFuzzerIterationLimit = 100;
+ size_t num_iterations;
+ loop_->FindNumberOfIterations(induction, &*condition->ctail(),
+ &num_iterations);
+ if (num_iterations > kFuzzerIterationLimit) {
+ return false;
+ }
+#endif
+
// Make sure the latch block is a unconditional branch to the header
// block.
const Instruction& branch = *loop_->GetLatchBlock()->ctail();
diff --git a/third_party/SPIRV-Tools/source/opt/merge_return_pass.h b/third_party/SPIRV-Tools/source/opt/merge_return_pass.h
index a35cf26..d15db2f 100644
--- a/third_party/SPIRV-Tools/source/opt/merge_return_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/merge_return_pass.h
@@ -118,8 +118,6 @@
StructuredControlState(Instruction* break_merge, Instruction* merge)
: break_merge_(break_merge), current_merge_(merge) {}
- StructuredControlState(const StructuredControlState&) = default;
-
bool InBreakable() const { return break_merge_; }
bool InStructuredFlow() const { return CurrentMergeId() != 0; }
diff --git a/third_party/SPIRV-Tools/source/opt/module.cpp b/third_party/SPIRV-Tools/source/opt/module.cpp
index 5983abb..c98af8f 100644
--- a/third_party/SPIRV-Tools/source/opt/module.cpp
+++ b/third_party/SPIRV-Tools/source/opt/module.cpp
@@ -90,6 +90,8 @@
DELEGATE(extensions_);
DELEGATE(ext_inst_imports_);
if (memory_model_) memory_model_->ForEachInst(f, run_on_debug_line_insts);
+ if (sampled_image_address_mode_)
+ sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts);
DELEGATE(entry_points_);
DELEGATE(execution_modes_);
DELEGATE(debugs1_);
@@ -114,6 +116,9 @@
if (memory_model_)
static_cast<const Instruction*>(memory_model_.get())
->ForEachInst(f, run_on_debug_line_insts);
+ if (sampled_image_address_mode_)
+ static_cast<const Instruction*>(sampled_image_address_mode_.get())
+ ->ForEachInst(f, run_on_debug_line_insts);
for (auto& i : entry_points_) DELEGATE(i);
for (auto& i : execution_modes_) DELEGATE(i);
for (auto& i : debugs1_) DELEGATE(i);
diff --git a/third_party/SPIRV-Tools/source/opt/module.h b/third_party/SPIRV-Tools/source/opt/module.h
index 230be70..7a6be46 100644
--- a/third_party/SPIRV-Tools/source/opt/module.h
+++ b/third_party/SPIRV-Tools/source/opt/module.h
@@ -83,6 +83,9 @@
// Set the memory model for this module.
inline void SetMemoryModel(std::unique_ptr<Instruction> m);
+ // Set the sampled image addressing mode for this module.
+ inline void SetSampledImageAddressMode(std::unique_ptr<Instruction> m);
+
// Appends an entry point instruction to this module.
inline void AddEntryPoint(std::unique_ptr<Instruction> e);
@@ -158,12 +161,20 @@
inline IteratorRange<inst_iterator> ext_inst_imports();
inline IteratorRange<const_inst_iterator> ext_inst_imports() const;
- // Return the memory model instruction contained inthis module.
+ // Return the memory model instruction contained in this module.
inline Instruction* GetMemoryModel() { return memory_model_.get(); }
inline const Instruction* GetMemoryModel() const {
return memory_model_.get();
}
+ // Return the sampled image address mode instruction contained in this module.
+ inline Instruction* GetSampledImageAddressMode() {
+ return sampled_image_address_mode_.get();
+ }
+ inline const Instruction* GetSampledImageAddressMode() const {
+ return sampled_image_address_mode_.get();
+ }
+
// There are several kinds of debug instructions, according to where they can
// appear in the logical layout of a module:
// - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued
@@ -288,6 +299,8 @@
InstructionList ext_inst_imports_;
// A module only has one memory model instruction.
std::unique_ptr<Instruction> memory_model_;
+ // A module can only have one optional sampled image addressing mode
+ std::unique_ptr<Instruction> sampled_image_address_mode_;
InstructionList entry_points_;
InstructionList execution_modes_;
InstructionList debugs1_;
@@ -326,6 +339,10 @@
memory_model_ = std::move(m);
}
+inline void Module::SetSampledImageAddressMode(std::unique_ptr<Instruction> m) {
+ sampled_image_address_mode_ = std::move(m);
+}
+
inline void Module::AddEntryPoint(std::unique_ptr<Instruction> e) {
entry_points_.push_back(std::move(e));
}
diff --git a/third_party/SPIRV-Tools/source/opt/optimizer.cpp b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
index 2976151..381589b 100644
--- a/third_party/SPIRV-Tools/source/opt/optimizer.cpp
+++ b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
@@ -623,10 +623,16 @@
assert(optimized_binary_with_nop.size() == original_binary_size &&
"Binary size unexpectedly changed despite the optimizer saying "
"there was no change");
- assert(memcmp(optimized_binary_with_nop.data(), original_binary,
- original_binary_size) == 0 &&
- "Binary content unexpectedly changed despite the optimizer saying "
- "there was no change");
+
+ // Compare the magic number to make sure the binaries were encoded in the
+ // endianness. If not, the contents of the binaries will be different, so
+ // do not check the contents.
+ if (optimized_binary_with_nop[0] == original_binary[0]) {
+ assert(memcmp(optimized_binary_with_nop.data(), original_binary,
+ original_binary_size) == 0 &&
+ "Binary content unexpectedly changed despite the optimizer saying "
+ "there was no change");
+ }
}
#endif // !NDEBUG
diff --git a/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp b/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
index e9b8087..56491b2 100644
--- a/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
+++ b/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
@@ -161,8 +161,15 @@
case analysis::Type::kArray: {
const analysis::Constant* size_const =
const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId());
- assert(size_const->AsIntConstant());
- total_size = size_const->GetU32();
+
+ if (size_const) {
+ assert(size_const->AsIntConstant());
+ total_size = size_const->GetU32();
+ } else {
+ // The size is spec constant, so it is unknown at this time. Assume
+ // it is very large.
+ total_size = UINT32_MAX;
+ }
} break;
case analysis::Type::kStruct:
total_size = static_cast<uint32_t>(
diff --git a/third_party/SPIRV-Tools/source/opt/scalar_replacement_pass.h b/third_party/SPIRV-Tools/source/opt/scalar_replacement_pass.h
index 76afc26..6a66dfb 100644
--- a/third_party/SPIRV-Tools/source/opt/scalar_replacement_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/scalar_replacement_pass.h
@@ -15,6 +15,7 @@
#ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
#define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
+#include <cassert>
#include <cstdio>
#include <memory>
#include <queue>
@@ -37,9 +38,20 @@
public:
ScalarReplacementPass(uint32_t limit = kDefaultLimit)
: max_num_elements_(limit) {
- name_[0] = '\0';
- strcat(name_, "scalar-replacement=");
- sprintf(&name_[strlen(name_)], "%d", max_num_elements_);
+ const auto num_to_write = snprintf(
+ name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_);
+ assert(size_t(num_to_write) < sizeof(name_));
+ (void)num_to_write; // Mark as unused
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays.
+ // This can cause timeouts and memouts during fuzzing that
+ // are not classed as bugs. To avoid this noise, we set the
+ // max_num_elements_ to a smaller value for fuzzing.
+ max_num_elements_ =
+ (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_
+ : 100);
+#endif
}
const char* name() const override { return name_; }
@@ -253,7 +265,10 @@
// Limit on the number of members in an object that will be replaced.
// 0 means there is no limit.
uint32_t max_num_elements_;
- char name_[55];
+ // This has to be big enough to fit "scalar-replacement=" followed by a
+ // uint32_t number written in decimal (so 10 digits), and then a
+ // terminating nul.
+ char name_[30];
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/types.cpp b/third_party/SPIRV-Tools/source/opt/types.cpp
index ebbdc36..056aceb 100644
--- a/third_party/SPIRV-Tools/source/opt/types.cpp
+++ b/third_party/SPIRV-Tools/source/opt/types.cpp
@@ -16,6 +16,7 @@
#include <algorithm>
#include <cassert>
+#include <climits>
#include <cstdint>
#include <sstream>
#include <string>
@@ -246,6 +247,35 @@
return ComputeHashValue(0, &seen);
}
+uint64_t Type::NumberOfComponents() const {
+ switch (kind()) {
+ case kVector:
+ return AsVector()->element_count();
+ case kMatrix:
+ return AsMatrix()->element_count();
+ case kArray: {
+ Array::LengthInfo length_info = AsArray()->length_info();
+ if (length_info.words[0] != Array::LengthInfo::kConstant) {
+ return UINT64_MAX;
+ }
+ assert(length_info.words.size() <= 3 &&
+ "The size of the array could not fit size_t.");
+ uint64_t length = 0;
+ length |= length_info.words[1];
+ if (length_info.words.size() > 2) {
+ length |= static_cast<uint64_t>(length_info.words[2]) << 32;
+ }
+ return length;
+ }
+ case kRuntimeArray:
+ return UINT64_MAX;
+ case kStruct:
+ return AsStruct()->element_types().size();
+ default:
+ return 0;
+ }
+}
+
bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
const Integer* it = that->AsInteger();
return it && width_ == it->width_ && signed_ == it->signed_ &&
diff --git a/third_party/SPIRV-Tools/source/opt/types.h b/third_party/SPIRV-Tools/source/opt/types.h
index f5a4a6b..a92669e 100644
--- a/third_party/SPIRV-Tools/source/opt/types.h
+++ b/third_party/SPIRV-Tools/source/opt/types.h
@@ -160,6 +160,10 @@
size_t ComputeHashValue(size_t hash, SeenTypes* seen) const;
+ // Returns the number of components in a composite type. Returns 0 for a
+ // non-composite type.
+ uint64_t NumberOfComponents() const;
+
// A bunch of methods for casting this type to a given type. Returns this if the
// cast can be done, nullptr otherwise.
// clang-format off
diff --git a/third_party/SPIRV-Tools/source/text_handler.cpp b/third_party/SPIRV-Tools/source/text_handler.cpp
index fe12a26..15c1741 100644
--- a/third_party/SPIRV-Tools/source/text_handler.cpp
+++ b/third_party/SPIRV-Tools/source/text_handler.cpp
@@ -62,28 +62,29 @@
// parameters, its the users responsibility to ensure these are non null.
spv_result_t advance(spv_text text, spv_position position) {
// NOTE: Consume white space, otherwise don't advance.
- if (position->index >= text->length) return SPV_END_OF_STREAM;
- switch (text->str[position->index]) {
- case '\0':
- return SPV_END_OF_STREAM;
- case ';':
- if (spv_result_t error = advanceLine(text, position)) return error;
- return advance(text, position);
- case ' ':
- case '\t':
- case '\r':
- position->column++;
- position->index++;
- return advance(text, position);
- case '\n':
- position->column = 0;
- position->line++;
- position->index++;
- return advance(text, position);
- default:
- break;
+ while (true) {
+ if (position->index >= text->length) return SPV_END_OF_STREAM;
+ switch (text->str[position->index]) {
+ case '\0':
+ return SPV_END_OF_STREAM;
+ case ';':
+ if (spv_result_t error = advanceLine(text, position)) return error;
+ continue;
+ case ' ':
+ case '\t':
+ case '\r':
+ position->column++;
+ position->index++;
+ continue;
+ case '\n':
+ position->column = 0;
+ position->line++;
+ position->index++;
+ continue;
+ default:
+ return SPV_SUCCESS;
+ }
}
- return SPV_SUCCESS;
}
// Fetches the next word from the given text stream starting from the given
diff --git a/third_party/SPIRV-Tools/source/util/hex_float.h b/third_party/SPIRV-Tools/source/util/hex_float.h
index 903b628..06e3c57 100644
--- a/third_party/SPIRV-Tools/source/util/hex_float.h
+++ b/third_party/SPIRV-Tools/source/util/hex_float.h
@@ -209,9 +209,10 @@
// be the default for any non-specialized type.
template <typename T>
struct HexFloatTraits {
- // Integer type that can store this hex-float.
+ // Integer type that can store the bit representation of this hex-float.
using uint_type = void;
- // Signed integer type that can store this hex-float.
+ // Signed integer type that can store the bit representation of this
+ // hex-float.
using int_type = void;
// The numerical type that this HexFloat represents.
using underlying_type = void;
@@ -958,9 +959,15 @@
// This "looks" like a hex-float so treat it as one.
bool seen_p = false;
bool seen_dot = false;
+
+ // The mantissa bits, without the most significant 1 bit, and with the
+ // the most recently read bits in the least significant positions.
+ uint_type fraction = 0;
+ // The number of mantissa bits that have been read, including the leading 1
+ // bit that is not written into 'fraction'.
uint_type fraction_index = 0;
- uint_type fraction = 0;
+ // TODO(dneto): handle overflow and underflow
int_type exponent = HF::exponent_bias;
// Strip off leading zeros so we don't have to special-case them later.
@@ -968,11 +975,13 @@
is.get();
}
- bool is_denorm =
- true; // Assume denorm "representation" until we hear otherwise.
- // NB: This does not mean the value is actually denorm,
- // it just means that it was written 0.
+ // Does the mantissa, as written, have non-zero digits to the left of
+ // the decimal point. Assume no until proven otherwise.
+ bool has_integer_part = false;
bool bits_written = false; // Stays false until we write a bit.
+
+ // Scan the mantissa hex digits until we see a '.' or the 'p' that
+ // starts the exponent.
while (!seen_p && !seen_dot) {
// Handle characters that are left of the fractional part.
if (next_char == '.') {
@@ -980,9 +989,8 @@
} else if (next_char == 'p') {
seen_p = true;
} else if (::isxdigit(next_char)) {
- // We know this is not denormalized since we have stripped all leading
- // zeroes and we are not a ".".
- is_denorm = false;
+ // We have stripped all leading zeroes and we have not yet seen a ".".
+ has_integer_part = true;
int number = get_nibble_from_character(next_char);
for (int i = 0; i < 4; ++i, number <<= 1) {
uint_type write_bit = (number & 0x8) ? 0x1 : 0x0;
@@ -993,8 +1001,12 @@
fraction |
static_cast<uint_type>(
write_bit << (HF::top_bit_left_shift - fraction_index++)));
+ // TODO(dneto): Avoid overflow. Testing would require
+ // parameterization.
exponent = static_cast<int_type>(exponent + 1);
}
+ // Since this updated after setting fraction bits, this effectively
+ // drops the leading 1 bit.
bits_written |= write_bit != 0;
}
} else {
@@ -1018,10 +1030,12 @@
for (int i = 0; i < 4; ++i, number <<= 1) {
uint_type write_bit = (number & 0x8) ? 0x01 : 0x00;
bits_written |= write_bit != 0;
- if (is_denorm && !bits_written) {
+ if ((!has_integer_part) && !bits_written) {
// Handle modifying the exponent here this way we can handle
// an arbitrary number of hex values without overflowing our
// integer.
+ // TODO(dneto): Handle underflow. Testing would require extra
+ // parameterization.
exponent = static_cast<int_type>(exponent - 1);
} else {
fraction = static_cast<uint_type>(
@@ -1043,25 +1057,40 @@
// Finished reading the part preceding 'p'.
// In hex floats syntax, the binary exponent is required.
- bool seen_sign = false;
+ bool seen_exponent_sign = false;
int8_t exponent_sign = 1;
bool seen_written_exponent_digits = false;
+ // The magnitude of the exponent, as written, or the sentinel value to signal
+ // overflow.
int_type written_exponent = 0;
+ // A sentinel value signalling overflow of the magnitude of the written
+ // exponent. We'll assume that -written_exponent_overflow is valid for the
+ // type. Later we may add 1 or subtract 1 from the adjusted exponent, so leave
+ // room for an extra 1.
+ const int_type written_exponent_overflow =
+ std::numeric_limits<int_type>::max() - 1;
while (true) {
if (!seen_written_exponent_digits &&
(next_char == '-' || next_char == '+')) {
- if (seen_sign) {
+ if (seen_exponent_sign) {
is.setstate(std::ios::failbit);
return is;
}
- seen_sign = true;
+ seen_exponent_sign = true;
exponent_sign = (next_char == '-') ? -1 : 1;
} else if (::isdigit(next_char)) {
seen_written_exponent_digits = true;
// Hex-floats express their exponent as decimal.
- written_exponent = static_cast<int_type>(written_exponent * 10);
- written_exponent =
- static_cast<int_type>(written_exponent + (next_char - '0'));
+ int_type digit =
+ static_cast<int_type>(static_cast<int_type>(next_char) - '0');
+ if (written_exponent >= (written_exponent_overflow - digit) / 10) {
+ // The exponent is very big. Saturate rather than overflow the exponent.
+ // signed integer, which would be undefined behaviour.
+ written_exponent = written_exponent_overflow;
+ } else {
+ written_exponent = static_cast<int_type>(
+ static_cast<int_type>(written_exponent * 10) + digit);
+ }
} else {
break;
}
@@ -1075,10 +1104,29 @@
}
written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
- exponent = static_cast<int_type>(exponent + written_exponent);
+ // Now fold in the exponent bias into the written exponent, updating exponent.
+ // But avoid undefined behaviour that would result from overflowing int_type.
+ if (written_exponent >= 0 && exponent >= 0) {
+ // Saturate up to written_exponent_overflow.
+ if (written_exponent_overflow - exponent > written_exponent) {
+ exponent = static_cast<int_type>(written_exponent + exponent);
+ } else {
+ exponent = written_exponent_overflow;
+ }
+ } else if (written_exponent < 0 && exponent < 0) {
+ // Saturate down to -written_exponent_overflow.
+ if (written_exponent_overflow + exponent > -written_exponent) {
+ exponent = static_cast<int_type>(written_exponent + exponent);
+ } else {
+ exponent = static_cast<int_type>(-written_exponent_overflow);
+ }
+ } else {
+ // They're of opposing sign, so it's safe to add.
+ exponent = static_cast<int_type>(written_exponent + exponent);
+ }
- bool is_zero = is_denorm && (fraction == 0);
- if (is_denorm && !is_zero) {
+ bool is_zero = (!has_integer_part) && (fraction == 0);
+ if ((!has_integer_part) && !is_zero) {
fraction = static_cast<uint_type>(fraction << 1);
exponent = static_cast<int_type>(exponent - 1);
} else if (is_zero) {
@@ -1095,7 +1143,7 @@
const int_type max_exponent =
SetBits<uint_type, 0, HF::num_exponent_bits>::get;
- // Handle actual denorm numbers
+ // Handle denorm numbers
while (exponent < 0 && !is_zero) {
fraction = static_cast<uint_type>(fraction >> 1);
exponent = static_cast<int_type>(exponent + 1);
diff --git a/third_party/SPIRV-Tools/source/util/pooled_linked_list.h b/third_party/SPIRV-Tools/source/util/pooled_linked_list.h
deleted file mode 100644
index faaa4c4..0000000
--- a/third_party/SPIRV-Tools/source/util/pooled_linked_list.h
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (c) 2021 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.
-
-#ifndef SOURCE_UTIL_POOLED_LINKED_LIST_H_
-#define SOURCE_UTIL_POOLED_LINKED_LIST_H_
-
-#include <cstdint>
-#include <vector>
-
-namespace spvtools {
-namespace utils {
-
-// Shared storage of nodes for PooledLinkedList.
-template <typename T>
-class PooledLinkedListNodes {
- public:
- struct Node {
- Node(T e, int32_t n = -1) : element(e), next(n) {}
-
- T element = {};
- int32_t next = -1;
- };
-
- PooledLinkedListNodes() = default;
- PooledLinkedListNodes(const PooledLinkedListNodes&) = delete;
- PooledLinkedListNodes& operator=(const PooledLinkedListNodes&) = delete;
-
- PooledLinkedListNodes(PooledLinkedListNodes&& that) {
- *this = std::move(that);
- }
-
- PooledLinkedListNodes& operator=(PooledLinkedListNodes&& that) {
- vec_ = std::move(that.vec_);
- free_nodes_ = that.free_nodes_;
- return *this;
- }
-
- size_t total_nodes() { return vec_.size(); }
- size_t free_nodes() { return free_nodes_; }
- size_t used_nodes() { return total_nodes() - free_nodes(); }
-
- private:
- template <typename ListT>
- friend class PooledLinkedList;
-
- Node& at(int32_t index) { return vec_[index]; }
- const Node& at(int32_t index) const { return vec_[index]; }
-
- int32_t insert(T element) {
- int32_t index = int32_t(vec_.size());
- vec_.emplace_back(element);
- return index;
- }
-
- std::vector<Node> vec_;
- size_t free_nodes_ = 0;
-};
-
-// Implements a linked-list where list nodes come from a shared pool. This is
-// meant to be used in scenarios where it is desirable to avoid many small
-// allocations.
-//
-// Instead of pointers, the list uses indices to allow the underlying storage
-// to be modified without needing to modify the list. When removing elements
-// from the list, nodes are not deleted or recycled: to reclaim unused space,
-// perform a sequence of |move_nodes| operations into a temporary pool, which
-// then is moved into the old pool.
-//
-// This does *not* attempt to implement a full stl-compatible interface.
-template <typename T>
-class PooledLinkedList {
- public:
- using NodePool = PooledLinkedListNodes<T>;
- using Node = typename NodePool::Node;
-
- PooledLinkedList() = delete;
- PooledLinkedList(NodePool* nodes) : nodes_(nodes) {}
-
- // Shared iterator implementation (for iterator and const_iterator).
- template <typename ElementT, typename PoolT>
- class iterator_base {
- public:
- iterator_base(const iterator_base& i)
- : nodes_(i.nodes_), index_(i.index_) {}
-
- iterator_base& operator++() {
- index_ = nodes_->at(index_).next;
- return *this;
- }
-
- iterator_base& operator=(const iterator_base& i) {
- nodes_ = i.nodes_;
- index_ = i.index_;
- return *this;
- }
-
- ElementT& operator*() const { return nodes_->at(index_).element; }
- ElementT* operator->() const { return &nodes_->at(index_).element; }
-
- friend inline bool operator==(const iterator_base& lhs,
- const iterator_base& rhs) {
- return lhs.nodes_ == rhs.nodes_ && lhs.index_ == rhs.index_;
- }
- friend inline bool operator!=(const iterator_base& lhs,
- const iterator_base& rhs) {
- return lhs.nodes_ != rhs.nodes_ || lhs.index_ != rhs.index_;
- }
-
- // Define standard iterator types needs so this class can be
- // used with <algorithms>.
- using iterator_category = std::forward_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = ElementT;
- using pointer = ElementT*;
- using const_pointer = const ElementT*;
- using reference = ElementT&;
- using const_reference = const ElementT&;
- using size_type = size_t;
-
- private:
- friend PooledLinkedList;
-
- iterator_base(PoolT* pool, int32_t index) : nodes_(pool), index_(index) {}
-
- PoolT* nodes_;
- int32_t index_ = -1;
- };
-
- using iterator = iterator_base<T, std::vector<Node>>;
- using const_iterator = iterator_base<const T, const std::vector<Node>>;
-
- bool empty() const { return head_ == -1; }
-
- T& front() { return nodes_->at(head_).element; }
- T& back() { return nodes_->at(tail_).element; }
- const T& front() const { return nodes_->at(head_).element; }
- const T& back() const { return nodes_->at(tail_).element; }
-
- iterator begin() { return iterator(&nodes_->vec_, head_); }
- iterator end() { return iterator(&nodes_->vec_, -1); }
- const_iterator begin() const { return const_iterator(&nodes_->vec_, head_); }
- const_iterator end() const { return const_iterator(&nodes_->vec_, -1); }
-
- // Inserts |element| at the back of the list.
- void push_back(T element) {
- int32_t new_tail = nodes_->insert(element);
- if (head_ == -1) {
- head_ = new_tail;
- tail_ = new_tail;
- } else {
- nodes_->at(tail_).next = new_tail;
- tail_ = new_tail;
- }
- }
-
- // Removes the first occurrence of |element| from the list.
- // Returns if |element| was removed.
- bool remove_first(T element) {
- int32_t* prev_next = &head_;
- for (int32_t prev_index = -1, index = head_; index != -1; /**/) {
- auto& node = nodes_->at(index);
- if (node.element == element) {
- // Snip from of the list, optionally fixing up tail pointer.
- if (tail_ == index) {
- assert(node.next == -1);
- tail_ = prev_index;
- }
- *prev_next = node.next;
- nodes_->free_nodes_++;
- return true;
- } else {
- prev_next = &node.next;
- }
- prev_index = index;
- index = node.next;
- }
- return false;
- }
-
- // Returns the PooledLinkedListNodes that owns this list's nodes.
- NodePool* pool() { return nodes_; }
-
- // Moves the nodes in this list into |new_pool|, providing a way to compact
- // storage and reclaim unused space.
- //
- // Upon completing a sequence of |move_nodes| calls, you must ensure you
- // retain ownership of the new storage your lists point to. Example usage:
- //
- // unique_ptr<NodePool> new_pool = ...;
- // for (PooledLinkedList& list : lists) {
- // list.move_to(new_pool);
- // }
- // my_pool_ = std::move(new_pool);
- void move_nodes(NodePool* new_pool) {
- // Be sure to construct the list in the same order, instead of simply
- // doing a sequence of push_backs.
- int32_t prev_entry = -1;
- int32_t nodes_freed = 0;
- for (int32_t index = head_; index != -1; nodes_freed++) {
- const auto& node = nodes_->at(index);
- int32_t this_entry = new_pool->insert(node.element);
- index = node.next;
- if (prev_entry == -1) {
- head_ = this_entry;
- } else {
- new_pool->at(prev_entry).next = this_entry;
- }
- prev_entry = this_entry;
- }
- tail_ = prev_entry;
- // Update our old pool's free count, now we're a member of the new pool.
- nodes_->free_nodes_ += nodes_freed;
- nodes_ = new_pool;
- }
-
- private:
- NodePool* nodes_;
- int32_t head_ = -1;
- int32_t tail_ = -1;
-};
-
-} // namespace utils
-} // namespace spvtools
-
-#endif // SOURCE_UTIL_POOLED_LINKED_LIST_H_
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.cpp b/third_party/SPIRV-Tools/source/val/basic_block.cpp
index b2a8793..da05db3 100644
--- a/third_party/SPIRV-Tools/source/val/basic_block.cpp
+++ b/third_party/SPIRV-Tools/source/val/basic_block.cpp
@@ -24,11 +24,13 @@
BasicBlock::BasicBlock(uint32_t label_id)
: id_(label_id),
immediate_dominator_(nullptr),
- immediate_post_dominator_(nullptr),
+ immediate_structural_dominator_(nullptr),
+ immediate_structural_post_dominator_(nullptr),
predecessors_(),
successors_(),
type_(0),
reachable_(false),
+ structurally_reachable_(false),
label_(nullptr),
terminator_(nullptr) {}
@@ -36,21 +38,32 @@
immediate_dominator_ = dom_block;
}
-void BasicBlock::SetImmediatePostDominator(BasicBlock* pdom_block) {
- immediate_post_dominator_ = pdom_block;
+void BasicBlock::SetImmediateStructuralDominator(BasicBlock* dom_block) {
+ immediate_structural_dominator_ = dom_block;
+}
+
+void BasicBlock::SetImmediateStructuralPostDominator(BasicBlock* pdom_block) {
+ immediate_structural_post_dominator_ = pdom_block;
}
const BasicBlock* BasicBlock::immediate_dominator() const {
return immediate_dominator_;
}
-const BasicBlock* BasicBlock::immediate_post_dominator() const {
- return immediate_post_dominator_;
+const BasicBlock* BasicBlock::immediate_structural_dominator() const {
+ return immediate_structural_dominator_;
+}
+
+const BasicBlock* BasicBlock::immediate_structural_post_dominator() const {
+ return immediate_structural_post_dominator_;
}
BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; }
-BasicBlock* BasicBlock::immediate_post_dominator() {
- return immediate_post_dominator_;
+BasicBlock* BasicBlock::immediate_structural_dominator() {
+ return immediate_structural_dominator_;
+}
+BasicBlock* BasicBlock::immediate_structural_post_dominator() {
+ return immediate_structural_post_dominator_;
}
void BasicBlock::RegisterSuccessors(
@@ -58,6 +71,10 @@
for (auto& block : next_blocks) {
block->predecessors_.push_back(this);
successors_.push_back(block);
+
+ // Register structural successors/predecessors too.
+ block->structural_predecessors_.push_back(this);
+ structural_successors_.push_back(block);
}
}
@@ -67,10 +84,16 @@
std::find(other.dom_begin(), other.dom_end(), this));
}
-bool BasicBlock::postdominates(const BasicBlock& other) const {
- return (this == &other) ||
- !(other.pdom_end() ==
- std::find(other.pdom_begin(), other.pdom_end(), this));
+bool BasicBlock::structurally_dominates(const BasicBlock& other) const {
+ return (this == &other) || !(other.structural_dom_end() ==
+ std::find(other.structural_dom_begin(),
+ other.structural_dom_end(), this));
+}
+
+bool BasicBlock::structurally_postdominates(const BasicBlock& other) const {
+ return (this == &other) || !(other.structural_pdom_end() ==
+ std::find(other.structural_pdom_begin(),
+ other.structural_pdom_end(), this));
}
BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {}
@@ -107,21 +130,43 @@
return DominatorIterator();
}
-const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const {
- return DominatorIterator(
- this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
+const BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() const {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_dominator();
+ });
}
-BasicBlock::DominatorIterator BasicBlock::pdom_begin() {
- return DominatorIterator(
- this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
+BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_dominator();
+ });
}
-const BasicBlock::DominatorIterator BasicBlock::pdom_end() const {
+const BasicBlock::DominatorIterator BasicBlock::structural_dom_end() const {
return DominatorIterator();
}
-BasicBlock::DominatorIterator BasicBlock::pdom_end() {
+BasicBlock::DominatorIterator BasicBlock::structural_dom_end() {
+ return DominatorIterator();
+}
+
+const BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() const {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_post_dominator();
+ });
+}
+
+BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_post_dominator();
+ });
+}
+
+const BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() const {
+ return DominatorIterator();
+}
+
+BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() {
return DominatorIterator();
}
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.h b/third_party/SPIRV-Tools/source/val/basic_block.h
index 47cd06d..be5657e 100644
--- a/third_party/SPIRV-Tools/source/val/basic_block.h
+++ b/third_party/SPIRV-Tools/source/val/basic_block.h
@@ -64,9 +64,32 @@
/// Returns the successors of the BasicBlock
std::vector<BasicBlock*>* successors() { return &successors_; }
- /// Returns true if the block is reachable in the CFG
+ /// Returns the structural successors of the BasicBlock
+ std::vector<BasicBlock*>* structural_predecessors() {
+ return &structural_predecessors_;
+ }
+
+ /// Returns the structural predecessors of the BasicBlock
+ const std::vector<BasicBlock*>* structural_predecessors() const {
+ return &structural_predecessors_;
+ }
+
+ /// Returns the structural successors of the BasicBlock
+ std::vector<BasicBlock*>* structural_successors() {
+ return &structural_successors_;
+ }
+
+ /// Returns the structural predecessors of the BasicBlock
+ const std::vector<BasicBlock*>* structural_successors() const {
+ return &structural_successors_;
+ }
+
+ /// Returns true if the block is reachable in the CFG.
bool reachable() const { return reachable_; }
+ /// Returns true if the block is structurally reachable in the CFG.
+ bool structurally_reachable() const { return structurally_reachable_; }
+
/// Returns true if BasicBlock is of the given type
bool is_type(BlockType type) const {
if (type == kBlockTypeUndefined) return type_.none();
@@ -76,6 +99,11 @@
/// Sets the reachability of the basic block in the CFG
void set_reachable(bool reachability) { reachable_ = reachability; }
+ /// Sets the structural reachability of the basic block in the CFG
+ void set_structurally_reachable(bool reachability) {
+ structurally_reachable_ = reachability;
+ }
+
/// Sets the type of the BasicBlock
void set_type(BlockType type) {
if (type == kBlockTypeUndefined)
@@ -89,10 +117,15 @@
/// @param[in] dom_block The dominator block
void SetImmediateDominator(BasicBlock* dom_block);
+ /// Sets the immediate dominator of this basic block
+ ///
+ /// @param[in] dom_block The dominator block
+ void SetImmediateStructuralDominator(BasicBlock* dom_block);
+
/// Sets the immediate post dominator of this basic block
///
/// @param[in] pdom_block The post dominator block
- void SetImmediatePostDominator(BasicBlock* pdom_block);
+ void SetImmediateStructuralPostDominator(BasicBlock* pdom_block);
/// Returns the immediate dominator of this basic block
BasicBlock* immediate_dominator();
@@ -100,11 +133,17 @@
/// Returns the immediate dominator of this basic block
const BasicBlock* immediate_dominator() const;
- /// Returns the immediate post dominator of this basic block
- BasicBlock* immediate_post_dominator();
+ /// Returns the immediate dominator of this basic block
+ BasicBlock* immediate_structural_dominator();
+
+ /// Returns the immediate dominator of this basic block
+ const BasicBlock* immediate_structural_dominator() const;
/// Returns the immediate post dominator of this basic block
- const BasicBlock* immediate_post_dominator() const;
+ BasicBlock* immediate_structural_post_dominator();
+
+ /// Returns the immediate post dominator of this basic block
+ const BasicBlock* immediate_structural_post_dominator() const;
/// Returns the label instruction for the block, or nullptr if not set.
const Instruction* label() const { return label_; }
@@ -132,9 +171,18 @@
/// Assumes dominators have been computed.
bool dominates(const BasicBlock& other) const;
- /// Returns true if this block postdominates the other block.
- /// Assumes dominators have been computed.
- bool postdominates(const BasicBlock& other) const;
+ /// Returns true if this block structurally dominates the other block.
+ /// Assumes structural dominators have been computed.
+ bool structurally_dominates(const BasicBlock& other) const;
+
+ /// Returns true if this block structurally postdominates the other block.
+ /// Assumes structural dominators have been computed.
+ bool structurally_postdominates(const BasicBlock& other) const;
+
+ void RegisterStructuralSuccessor(BasicBlock* block) {
+ block->structural_predecessors_.push_back(this);
+ structural_successors_.push_back(block);
+ }
/// @brief A BasicBlock dominator iterator class
///
@@ -191,18 +239,32 @@
/// block
DominatorIterator dom_end();
+ /// Returns a dominator iterator which points to the current block
+ const DominatorIterator structural_dom_begin() const;
+
+ /// Returns a dominator iterator which points to the current block
+ DominatorIterator structural_dom_begin();
+
+ /// Returns a dominator iterator which points to one element past the first
+ /// block
+ const DominatorIterator structural_dom_end() const;
+
+ /// Returns a dominator iterator which points to one element past the first
+ /// block
+ DominatorIterator structural_dom_end();
+
/// Returns a post dominator iterator which points to the current block
- const DominatorIterator pdom_begin() const;
+ const DominatorIterator structural_pdom_begin() const;
/// Returns a post dominator iterator which points to the current block
- DominatorIterator pdom_begin();
+ DominatorIterator structural_pdom_begin();
/// Returns a post dominator iterator which points to one element past the
/// last block
- const DominatorIterator pdom_end() const;
+ const DominatorIterator structural_pdom_end() const;
/// Returns a post dominator iterator which points to one element past the
/// last block
- DominatorIterator pdom_end();
+ DominatorIterator structural_pdom_end();
private:
/// Id of the BasicBlock
@@ -211,8 +273,11 @@
/// Pointer to the immediate dominator of the BasicBlock
BasicBlock* immediate_dominator_;
- /// Pointer to the immediate dominator of the BasicBlock
- BasicBlock* immediate_post_dominator_;
+ /// Pointer to the immediate structural dominator of the BasicBlock
+ BasicBlock* immediate_structural_dominator_;
+
+ /// Pointer to the immediate structural post dominator of the BasicBlock
+ BasicBlock* immediate_structural_post_dominator_;
/// The set of predecessors of the BasicBlock
std::vector<BasicBlock*> predecessors_;
@@ -226,11 +291,17 @@
/// True if the block is reachable in the CFG
bool reachable_;
+ /// True if the block is structurally reachable in the CFG
+ bool structurally_reachable_;
+
/// label of this block, if any.
const Instruction* label_;
/// Terminator of this block.
const Instruction* terminator_;
+
+ std::vector<BasicBlock*> structural_predecessors_;
+ std::vector<BasicBlock*> structural_successors_;
};
/// @brief Returns true if the iterators point to the same element or if both
diff --git a/third_party/SPIRV-Tools/source/val/construct.cpp b/third_party/SPIRV-Tools/source/val/construct.cpp
index 251e2bb..52e61d5 100644
--- a/third_party/SPIRV-Tools/source/val/construct.cpp
+++ b/third_party/SPIRV-Tools/source/val/construct.cpp
@@ -70,60 +70,45 @@
void Construct::set_exit(BasicBlock* block) { exit_block_ = block; }
-Construct::ConstructBlockSet Construct::blocks(Function* function) const {
- auto header = entry_block();
- auto merge = exit_block();
- assert(header);
- int header_depth = function->GetBlockDepth(const_cast<BasicBlock*>(header));
- ConstructBlockSet construct_blocks;
- std::unordered_set<BasicBlock*> corresponding_headers;
- for (auto& other : corresponding_constructs()) {
- // The corresponding header can be the same block as this construct's
- // header for loops with no loop construct. In those cases, don't add the
- // loop header as it prevents finding any blocks in the construct.
- if (type() != ConstructType::kContinue || other->entry_block() != header) {
- corresponding_headers.insert(other->entry_block());
- }
+Construct::ConstructBlockSet Construct::blocks(Function* /*function*/) const {
+ const auto header = entry_block();
+ const auto exit = exit_block();
+ const bool is_continue = type() == ConstructType::kContinue;
+ const bool is_loop = type() == ConstructType::kLoop;
+ const BasicBlock* continue_header = nullptr;
+ if (is_loop) {
+ // The only corresponding construct for a loop is the continue.
+ continue_header = (*corresponding_constructs().begin())->entry_block();
}
std::vector<BasicBlock*> stack;
stack.push_back(const_cast<BasicBlock*>(header));
+ ConstructBlockSet construct_blocks;
while (!stack.empty()) {
- BasicBlock* block = stack.back();
+ auto* block = stack.back();
stack.pop_back();
- if (merge == block && ExitBlockIsMergeBlock()) {
- // Merge block is not part of the construct.
- continue;
- }
+ if (header->structurally_dominates(*block)) {
+ bool include = false;
+ if (is_continue && exit->structurally_postdominates(*block)) {
+ // Continue construct include blocks dominated by the continue target
+ // and post-dominated by the back-edge block.
+ include = true;
+ } else if (!exit->structurally_dominates(*block)) {
+ // Selection and loop constructs include blocks dominated by the header
+ // and not dominated by the merge.
+ include = true;
+ if (is_loop && continue_header->structurally_dominates(*block)) {
+ // Loop constructs have an additional constraint that they do not
+ // include blocks dominated by the continue construct. Since all
+ // blocks in the continue construct are dominated by the continue
+ // target, we just test for dominance by continue target.
+ include = false;
+ }
+ }
+ if (include) {
+ if (!construct_blocks.insert(block).second) continue;
- if (corresponding_headers.count(block)) {
- // Entered a corresponding construct.
- continue;
- }
-
- int block_depth = function->GetBlockDepth(block);
- if (block_depth < header_depth) {
- // Broke to outer construct.
- continue;
- }
-
- // In a loop, the continue target is at a depth of the loop construct + 1.
- // A selection construct nested directly within the loop construct is also
- // at the same depth. It is valid, however, to branch directly to the
- // continue target from within the selection construct.
- if (block != header && block_depth == header_depth &&
- type() == ConstructType::kSelection &&
- block->is_type(kBlockTypeContinue)) {
- // Continued to outer construct.
- continue;
- }
-
- if (!construct_blocks.insert(block).second) continue;
-
- if (merge != block) {
- for (auto succ : *block->successors()) {
- // All blocks in the construct must be dominated by the header.
- if (header->dominates(*succ)) {
+ for (auto succ : *block->structural_successors()) {
stack.push_back(succ);
}
}
@@ -181,11 +166,12 @@
for (auto& use : block->label()->uses()) {
if ((use.first->opcode() == SpvOpLoopMerge ||
use.first->opcode() == SpvOpSelectionMerge) &&
- use.second == 1 && use.first->block()->dominates(*block)) {
+ use.second == 1 &&
+ use.first->block()->structurally_dominates(*block)) {
return use.first->block();
}
}
- return block->immediate_dominator();
+ return block->immediate_structural_dominator();
};
bool seen_switch = false;
@@ -201,7 +187,7 @@
terminator->opcode() == SpvOpSwitch)) {
auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
- if (merge_block->dominates(*header)) {
+ if (merge_block->structurally_dominates(*header)) {
block = NextBlock(block);
continue;
}
diff --git a/third_party/SPIRV-Tools/source/val/decoration.h b/third_party/SPIRV-Tools/source/val/decoration.h
index ed3320f..4f53f20 100644
--- a/third_party/SPIRV-Tools/source/val/decoration.h
+++ b/third_party/SPIRV-Tools/source/val/decoration.h
@@ -69,6 +69,15 @@
std::vector<uint32_t>& params() { return params_; }
const std::vector<uint32_t>& params() const { return params_; }
+ inline bool operator<(const Decoration& rhs) const {
+ // Note: Sort by struct_member_index_ first, then type, so look up can be
+ // efficient using lower_bound() and upper_bound().
+ if (struct_member_index_ < rhs.struct_member_index_) return true;
+ if (rhs.struct_member_index_ < struct_member_index_) return false;
+ if (dec_type_ < rhs.dec_type_) return true;
+ if (rhs.dec_type_ < dec_type_) return false;
+ return params_ < rhs.params_;
+ }
inline bool operator==(const Decoration& rhs) const {
return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ &&
struct_member_index_ == rhs.struct_member_index_);
diff --git a/third_party/SPIRV-Tools/source/val/function.cpp b/third_party/SPIRV-Tools/source/val/function.cpp
index f3292b0..fc7ccd0 100644
--- a/third_party/SPIRV-Tools/source/val/function.cpp
+++ b/third_party/SPIRV-Tools/source/val/function.cpp
@@ -73,6 +73,8 @@
BasicBlock& continue_target_block = blocks_.at(continue_id);
assert(current_block_ &&
"RegisterLoopMerge must be called when called within a block");
+ current_block_->RegisterStructuralSuccessor(&merge_block);
+ current_block_->RegisterStructuralSuccessor(&continue_target_block);
current_block_->set_type(kBlockTypeLoop);
merge_block.set_type(kBlockTypeMerge);
@@ -101,6 +103,7 @@
current_block_->set_type(kBlockTypeSelection);
merge_block.set_type(kBlockTypeMerge);
merge_block_header_[&merge_block] = current_block_;
+ current_block_->RegisterStructuralSuccessor(&merge_block);
AddConstruct({ConstructType::kSelection, current_block(), &merge_block});
@@ -251,16 +254,6 @@
};
}
-Function::GetBlocksFunction
-Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const {
- return [this](const BasicBlock* block) {
- auto where = loop_header_successors_plus_continue_target_map_.find(block);
- return where == loop_header_successors_plus_continue_target_map_.end()
- ? AugmentedCFGSuccessorsFunction()(block)
- : &(*where).second;
- };
-}
-
Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
return [this](const BasicBlock* block) {
auto where = augmented_predecessors_map_.find(block);
@@ -269,11 +262,35 @@
};
}
+Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction()
+ const {
+ return [this](const BasicBlock* block) {
+ auto where = augmented_successors_map_.find(block);
+ return where == augmented_successors_map_.end()
+ ? block->structural_successors()
+ : &(*where).second;
+ };
+}
+
+Function::GetBlocksFunction
+Function::AugmentedStructuralCFGPredecessorsFunction() const {
+ return [this](const BasicBlock* block) {
+ auto where = augmented_predecessors_map_.find(block);
+ return where == augmented_predecessors_map_.end()
+ ? block->structural_predecessors()
+ : &(*where).second;
+ };
+}
+
void Function::ComputeAugmentedCFG() {
// Compute the successors of the pseudo-entry block, and
// the predecessors of the pseudo exit block.
- auto succ_func = [](const BasicBlock* b) { return b->successors(); };
- auto pred_func = [](const BasicBlock* b) { return b->predecessors(); };
+ auto succ_func = [](const BasicBlock* b) {
+ return b->structural_successors();
+ };
+ auto pred_func = [](const BasicBlock* b) {
+ return b->structural_predecessors();
+ };
CFA<BasicBlock>::ComputeAugmentedCFG(
ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
&augmented_successors_map_, &augmented_predecessors_map_, succ_func,
diff --git a/third_party/SPIRV-Tools/source/val/function.h b/third_party/SPIRV-Tools/source/val/function.h
index 2fe30bd..126b1dc 100644
--- a/third_party/SPIRV-Tools/source/val/function.h
+++ b/third_party/SPIRV-Tools/source/val/function.h
@@ -184,12 +184,12 @@
std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
/// Returns the block successors function for the augmented CFG.
GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
- /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from
- /// a loop header block to its continue target, if they are different blocks.
- GetBlocksFunction
- AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
/// Returns the block predecessors function for the augmented CFG.
GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
+ /// Returns the block structural successors function for the augmented CFG.
+ GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const;
+ /// Returns the block structural predecessors function for the augmented CFG.
+ GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const;
/// Returns the control flow nesting depth of the given basic block.
/// This function only works when you have structured control flow.
diff --git a/third_party/SPIRV-Tools/source/val/validate.cpp b/third_party/SPIRV-Tools/source/val/validate.cpp
index ecc9fdb..55e9fd2 100644
--- a/third_party/SPIRV-Tools/source/val/validate.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate.cpp
@@ -293,6 +293,11 @@
return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
<< "Missing OpFunctionEnd at end of module.";
+ if (vstate->HasCapability(SpvCapabilityBindlessTextureNV) &&
+ !vstate->has_samplerimage_variable_address_mode_specified())
+ return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
+ << "Missing required OpSamplerImageAddressingModeNV instruction.";
+
// Catch undefined forward references before performing further checks.
if (auto error = ValidateForwardDecls(*vstate)) return error;
@@ -345,6 +350,7 @@
if (auto error = NonUniformPass(*vstate, &instruction)) return error;
if (auto error = LiteralsPass(*vstate, &instruction)) return error;
+ if (auto error = RayQueryPass(*vstate, &instruction)) return error;
}
// Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi
diff --git a/third_party/SPIRV-Tools/source/val/validate.h b/third_party/SPIRV-Tools/source/val/validate.h
index cb1d05a..97d4683 100644
--- a/third_party/SPIRV-Tools/source/val/validate.h
+++ b/third_party/SPIRV-Tools/source/val/validate.h
@@ -197,6 +197,9 @@
/// Validates correctness of miscellaneous instructions.
spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
+/// Validates correctness of ray query instructions.
+spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst);
+
/// Calculates the reachability of basic blocks.
void ReachabilityPass(ValidationState_t& _);
diff --git a/third_party/SPIRV-Tools/source/val/validate_annotation.cpp b/third_party/SPIRV-Tools/source/val/validate_annotation.cpp
index 40f2118..c27c799 100644
--- a/third_party/SPIRV-Tools/source/val/validate_annotation.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_annotation.cpp
@@ -581,7 +581,7 @@
// Word 1 is the group <id>. All subsequent words are target <id>s that
// are going to be decorated with the decorations.
const uint32_t decoration_group_id = inst->word(1);
- std::vector<Decoration>& group_decorations =
+ std::set<Decoration>& group_decorations =
_.id_decorations(decoration_group_id);
for (size_t i = 2; i < inst->words().size(); ++i) {
const uint32_t target_id = inst->word(i);
@@ -595,7 +595,7 @@
// pairs. All decorations of the group should be applied to all the struct
// members that are specified in the instructions.
const uint32_t decoration_group_id = inst->word(1);
- std::vector<Decoration>& group_decorations =
+ std::set<Decoration>& group_decorations =
_.id_decorations(decoration_group_id);
// Grammar checks ensures that the number of arguments to this instruction
// is an odd number: 1 decoration group + (id,literal) pairs.
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index dd605d2..0220fcd 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -66,7 +66,8 @@
assert(type_inst);
const SpvOp type_opcode = type_inst->opcode();
- if (!_.options()->before_hlsl_legalization) {
+ if (!_.options()->before_hlsl_legalization &&
+ !_.HasCapability(SpvCapabilityBindlessTextureNV)) {
if (type_opcode == SpvOpTypeSampledImage ||
(_.HasCapability(SpvCapabilityShader) &&
(type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) {
@@ -466,7 +467,7 @@
std::vector<BasicBlock*> stack;
stack.push_back(target_block);
std::unordered_set<const BasicBlock*> visited;
- bool target_reachable = target_block->reachable();
+ bool target_reachable = target_block->structurally_reachable();
int target_depth = function->GetBlockDepth(target_block);
while (!stack.empty()) {
auto block = stack.back();
@@ -476,8 +477,8 @@
if (!visited.insert(block).second) continue;
- if (target_reachable && block->reachable() &&
- target_block->dominates(*block)) {
+ if (target_reachable && block->structurally_reachable() &&
+ target_block->structurally_dominates(*block)) {
// Still in the case construct.
for (auto successor : *block->successors()) {
stack.push_back(successor);
@@ -549,11 +550,12 @@
if (seen_iter == seen_to_fall_through.end()) {
const auto target_block = function->GetBlock(target).first;
// OpSwitch must dominate all its case constructs.
- if (header->reachable() && target_block->reachable() &&
- !header->dominates(*target_block)) {
+ if (header->structurally_reachable() &&
+ target_block->structurally_reachable() &&
+ !header->structurally_dominates(*target_block)) {
return _.diag(SPV_ERROR_INVALID_CFG, header->label())
<< "Selection header " << _.getIdName(header->id())
- << " does not dominate its case construct "
+ << " does not structurally dominate its case construct "
<< _.getIdName(target);
}
@@ -653,7 +655,7 @@
}
// Skip unreachable blocks.
- if (!block->reachable()) continue;
+ if (!block->structurally_reachable()) continue;
if (terminator->opcode() == SpvOpBranchConditional) {
const auto true_label = terminator->GetOperandAs<uint32_t>(1);
@@ -708,7 +710,7 @@
// Check the loop headers have exactly one back-edge branching to it
for (BasicBlock* loop_header : function->ordered_blocks()) {
- if (!loop_header->reachable()) continue;
+ if (!loop_header->structurally_reachable()) continue;
if (!loop_header->is_type(kBlockTypeLoop)) continue;
auto loop_header_id = loop_header->id();
auto num_latch_blocks = loop_latch_blocks[loop_header_id].size();
@@ -723,9 +725,10 @@
// Check construct rules
for (const Construct& construct : function->constructs()) {
auto header = construct.entry_block();
+ if (!header->structurally_reachable()) continue;
auto merge = construct.exit_block();
- if (header->reachable() && !merge) {
+ if (!merge) {
std::string construct_name, header_name, exit_name;
std::tie(construct_name, header_name, exit_name) =
ConstructNames(construct.type());
@@ -735,32 +738,31 @@
exit_name + ". This may be a bug in the validator.";
}
- // If the exit block is reachable then it's dominated by the
- // header.
- if (merge && merge->reachable()) {
- if (!header->dominates(*merge)) {
- return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
- << ConstructErrorString(construct, _.getIdName(header->id()),
- _.getIdName(merge->id()),
- "does not dominate");
- }
- // If it's really a merge block for a selection or loop, then it must be
- // *strictly* dominated by the header.
- if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
- return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
- << ConstructErrorString(construct, _.getIdName(header->id()),
- _.getIdName(merge->id()),
- "does not strictly dominate");
- }
+ // If the header is reachable, the merge is guaranteed to be structurally
+ // reachable.
+ if (!header->structurally_dominates(*merge)) {
+ return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
+ << ConstructErrorString(construct, _.getIdName(header->id()),
+ _.getIdName(merge->id()),
+ "does not structurally dominate");
}
+ // If it's really a merge block for a selection or loop, then it must be
+ // *strictly* structrually dominated by the header.
+ if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
+ return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
+ << ConstructErrorString(construct, _.getIdName(header->id()),
+ _.getIdName(merge->id()),
+ "does not strictly structurally dominate");
+ }
+
// Check post-dominance for continue constructs. But dominance and
// post-dominance only make sense when the construct is reachable.
- if (header->reachable() && construct.type() == ConstructType::kContinue) {
- if (!merge->postdominates(*header)) {
+ if (construct.type() == ConstructType::kContinue) {
+ if (!merge->structurally_postdominates(*header)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
<< ConstructErrorString(construct, _.getIdName(header->id()),
_.getIdName(merge->id()),
- "is not post dominated by");
+ "is not structurally post dominated by");
}
}
@@ -771,7 +773,7 @@
for (auto block : construct_blocks) {
// Check that all exits from the construct are via structured exits.
for (auto succ : *block->successors()) {
- if (block->reachable() && !construct_blocks.count(succ) &&
+ if (!construct_blocks.count(succ) &&
!construct.IsStructuredExit(_, succ)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
<< "block <ID> " << _.getIdName(block->id()) << " exits the "
@@ -784,7 +786,7 @@
// Check that for all non-header blocks, all predecessors are within this
// construct.
for (auto pred : *block->predecessors()) {
- if (pred->reachable() && !construct_blocks.count(pred)) {
+ if (pred->structurally_reachable() && !construct_blocks.count(pred)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id()))
<< "block <ID> " << pred->id() << " branches to the "
<< construct_name << " construct, but not to the "
@@ -800,7 +802,7 @@
merge_inst.opcode() == SpvOpLoopMerge) {
uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
auto merge_block = function->GetBlock(merge_id).first;
- if (merge_block->reachable() &&
+ if (merge_block->structurally_reachable() &&
!construct_blocks.count(merge_block)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
<< "Header block " << _.getIdName(block->id())
@@ -813,6 +815,43 @@
}
}
+ if (construct.type() == ConstructType::kLoop) {
+ // If the continue target differs from the loop header, then check that
+ // all edges into the continue construct come from within the loop.
+ const auto index = header->terminator() - &_.ordered_instructions()[0];
+ const auto& merge_inst = _.ordered_instructions()[index - 1];
+ const auto continue_id = merge_inst.GetOperandAs<uint32_t>(1);
+ const auto* continue_inst = _.FindDef(continue_id);
+ // OpLabel instructions aren't stored as part of the basic block for
+ // legacy reaasons. Grab the next instruction and use it's block pointer
+ // instead.
+ const auto next_index =
+ (continue_inst - &_.ordered_instructions()[0]) + 1;
+ const auto& next_inst = _.ordered_instructions()[next_index];
+ const auto* continue_target = next_inst.block();
+ if (header->id() != continue_id) {
+ for (auto pred : *continue_target->predecessors()) {
+ // Ignore back-edges from within the continue construct.
+ bool is_back_edge = false;
+ for (auto back_edge : back_edges) {
+ uint32_t back_edge_block;
+ uint32_t header_block;
+ std::tie(back_edge_block, header_block) = back_edge;
+ if (header_block == continue_id && back_edge_block == pred->id())
+ is_back_edge = true;
+ }
+ if (!construct_blocks.count(pred) && !is_back_edge) {
+ return _.diag(SPV_ERROR_INVALID_CFG, pred->terminator())
+ << "Block " << _.getIdName(pred->id())
+ << " branches to the loop continue target "
+ << _.getIdName(continue_id)
+ << ", but is not contained in the associated loop construct "
+ << _.getIdName(header->id());
+ }
+ }
+ }
+ }
+
// Checks rules for case constructs.
if (construct.type() == ConstructType::kSelection &&
header->terminator()->opcode() == SpvOpSwitch) {
@@ -850,52 +889,28 @@
<< _.getIdName(function.id());
}
- // Set each block's immediate dominator and immediate postdominator,
- // and find all back-edges.
+ // Set each block's immediate dominator.
//
// We want to analyze all the blocks in the function, even in degenerate
// control flow cases including unreachable blocks. So use the augmented
// CFG to ensure we cover all the blocks.
std::vector<const BasicBlock*> postorder;
- std::vector<const BasicBlock*> postdom_postorder;
- std::vector<std::pair<uint32_t, uint32_t>> back_edges;
auto ignore_block = [](const BasicBlock*) {};
auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {};
+ auto no_terminal_blocks = [](const BasicBlock*) { return false; };
if (!function.ordered_blocks().empty()) {
/// calculate dominators
CFA<BasicBlock>::DepthFirstTraversal(
function.first_block(), function.AugmentedCFGSuccessorsFunction(),
ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); },
- ignore_edge);
+ ignore_edge, no_terminal_blocks);
auto edges = CFA<BasicBlock>::CalculateDominators(
postorder, function.AugmentedCFGPredecessorsFunction());
for (auto edge : edges) {
if (edge.first != edge.second)
edge.first->SetImmediateDominator(edge.second);
}
-
- /// calculate post dominators
- CFA<BasicBlock>::DepthFirstTraversal(
- function.pseudo_exit_block(),
- function.AugmentedCFGPredecessorsFunction(), ignore_block,
- [&](const BasicBlock* b) { postdom_postorder.push_back(b); },
- ignore_edge);
- auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
- postdom_postorder, function.AugmentedCFGSuccessorsFunction());
- for (auto edge : postdom_edges) {
- edge.first->SetImmediatePostDominator(edge.second);
- }
- /// calculate back edges.
- CFA<BasicBlock>::DepthFirstTraversal(
- function.pseudo_entry_block(),
- function
- .AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge(),
- ignore_block, ignore_block,
- [&](const BasicBlock* from, const BasicBlock* to) {
- back_edges.emplace_back(from->id(), to->id());
- });
}
- UpdateContinueConstructExitBlocks(function, back_edges);
auto& blocks = function.ordered_blocks();
if (!blocks.empty()) {
@@ -929,6 +944,52 @@
/// Structured control flow checks are only required for shader capabilities
if (_.HasCapability(SpvCapabilityShader)) {
+ // Calculate structural dominance.
+ postorder.clear();
+ std::vector<const BasicBlock*> postdom_postorder;
+ std::vector<std::pair<uint32_t, uint32_t>> back_edges;
+ if (!function.ordered_blocks().empty()) {
+ /// calculate dominators
+ CFA<BasicBlock>::DepthFirstTraversal(
+ function.first_block(),
+ function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
+ [&](const BasicBlock* b) { postorder.push_back(b); }, ignore_edge,
+ no_terminal_blocks);
+ auto edges = CFA<BasicBlock>::CalculateDominators(
+ postorder, function.AugmentedStructuralCFGPredecessorsFunction());
+ for (auto edge : edges) {
+ if (edge.first != edge.second)
+ edge.first->SetImmediateStructuralDominator(edge.second);
+ }
+
+ /// calculate post dominators
+ CFA<BasicBlock>::DepthFirstTraversal(
+ function.pseudo_exit_block(),
+ function.AugmentedStructuralCFGPredecessorsFunction(), ignore_block,
+ [&](const BasicBlock* b) { postdom_postorder.push_back(b); },
+ ignore_edge, no_terminal_blocks);
+ auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
+ postdom_postorder,
+ function.AugmentedStructuralCFGSuccessorsFunction());
+ for (auto edge : postdom_edges) {
+ edge.first->SetImmediateStructuralPostDominator(edge.second);
+ }
+ /// calculate back edges.
+ CFA<BasicBlock>::DepthFirstTraversal(
+ function.pseudo_entry_block(),
+ function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
+ ignore_block,
+ [&](const BasicBlock* from, const BasicBlock* to) {
+ // A back edge must be a real edge. Since the augmented successors
+ // contain structural edges, filter those from consideration.
+ for (const auto* succ : *(from->successors())) {
+ if (succ == to) back_edges.emplace_back(from->id(), to->id());
+ }
+ },
+ no_terminal_blocks);
+ }
+ UpdateContinueConstructExitBlocks(function, back_edges);
+
if (auto error =
StructuredControlFlowChecks(_, &function, back_edges, postorder))
return error;
@@ -1054,6 +1115,26 @@
}
}
}
+
+ // Repeat for structural reachability.
+ for (auto& f : _.functions()) {
+ std::vector<BasicBlock*> stack;
+ auto entry = f.first_block();
+ // Skip function declarations.
+ if (entry) stack.push_back(entry);
+
+ while (!stack.empty()) {
+ auto block = stack.back();
+ stack.pop_back();
+
+ if (block->structurally_reachable()) continue;
+
+ block->set_structurally_reachable(true);
+ for (auto succ : *block->structural_successors()) {
+ stack.push_back(succ);
+ }
+ }
+ }
}
spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
diff --git a/third_party/SPIRV-Tools/source/val/validate_conversion.cpp b/third_party/SPIRV-Tools/source/val/validate_conversion.cpp
index b4e39cf..dc6b151 100644
--- a/third_party/SPIRV-Tools/source/val/validate_conversion.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_conversion.cpp
@@ -534,6 +534,24 @@
break;
}
+ case SpvOpConvertUToAccelerationStructureKHR: {
+ if (!_.IsAccelerationStructureType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Result Type to be a Acceleration Structure: "
+ << spvOpcodeString(opcode);
+ }
+
+ const uint32_t input_type = _.GetOperandTypeId(inst, 2);
+ if (!input_type || !_.IsUnsigned64BitHandle(input_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected 64-bit uint scalar or 2-component 32-bit uint "
+ "vector as input: "
+ << spvOpcodeString(opcode);
+ }
+
+ break;
+ }
+
default:
break;
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
index 73d512a..4e4f108 100644
--- a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
@@ -190,6 +190,13 @@
// Minimal alignment is byte-aligned.
uint32_t baseAlignment = 1;
switch (inst->opcode()) {
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeImage:
+ if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+ return baseAlignment = vstate.samplerimage_variable_address_mode() / 8;
+ assert(0);
+ return 0;
case SpvOpTypeInt:
case SpvOpTypeFloat:
baseAlignment = words[2] / 8;
@@ -219,6 +226,7 @@
baseAlignment =
componentAlignment * (num_columns == 3 ? 4 : num_columns);
}
+ if (roundUp) baseAlignment = align(baseAlignment, 16u);
} break;
case SpvOpTypeArray:
case SpvOpTypeRuntimeArray:
@@ -256,6 +264,13 @@
const auto inst = vstate.FindDef(type_id);
const auto& words = inst->words();
switch (inst->opcode()) {
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeImage:
+ if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+ return vstate.samplerimage_variable_address_mode() / 8;
+ assert(0);
+ return 0;
case SpvOpTypeInt:
case SpvOpTypeFloat:
return words[2] / 8;
@@ -296,6 +311,13 @@
const auto inst = vstate.FindDef(member_id);
const auto& words = inst->words();
switch (inst->opcode()) {
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeImage:
+ if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+ return vstate.samplerimage_variable_address_mode() / 8;
+ assert(0);
+ return 0;
case SpvOpTypeInt:
case SpvOpTypeFloat:
return words[2] / 8;
@@ -346,10 +368,13 @@
const auto& lastMember = members.back();
uint32_t offset = 0xffffffff;
// Find the offset of the last element and add the size.
- for (auto& decoration : vstate.id_decorations(member_id)) {
- if (SpvDecorationOffset == decoration.dec_type() &&
- decoration.struct_member_index() == (int)lastIdx) {
- offset = decoration.params()[0];
+ auto member_decorations =
+ vstate.id_member_decorations(member_id, lastIdx);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ assert(decoration->struct_member_index() == (int)lastIdx);
+ if (SpvDecorationOffset == decoration->dec_type()) {
+ offset = decoration->params()[0];
}
}
// This check depends on the fact that all members have offsets. This
@@ -445,15 +470,17 @@
for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
memberIdx < numMembers; memberIdx++) {
uint32_t offset = 0xffffffff;
- for (auto& decoration : vstate.id_decorations(struct_id)) {
- if (decoration.struct_member_index() == (int)memberIdx) {
- switch (decoration.dec_type()) {
- case SpvDecorationOffset:
- offset = decoration.params()[0];
- break;
- default:
- break;
- }
+ auto member_decorations =
+ vstate.id_member_decorations(struct_id, memberIdx);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ assert(decoration->struct_member_index() == (int)memberIdx);
+ switch (decoration->dec_type()) {
+ case SpvDecorationOffset:
+ offset = decoration->params()[0];
+ break;
+ default:
+ break;
}
}
member_offsets.push_back(
@@ -633,7 +660,8 @@
}
// Returns true if all ids of given type have a specified decoration.
-bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration,
+bool checkForRequiredDecoration(uint32_t struct_id,
+ std::function<bool(SpvDecoration)> checker,
SpvOp type, ValidationState_t& vstate) {
const auto& members = getStructMembers(struct_id, vstate);
for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
@@ -641,10 +669,10 @@
if (type != vstate.FindDef(id)->opcode()) continue;
bool found = false;
for (auto& dec : vstate.id_decorations(id)) {
- if (decoration == dec.dec_type()) found = true;
+ if (checker(dec.dec_type())) found = true;
}
for (auto& dec : vstate.id_decorations(struct_id)) {
- if (decoration == dec.dec_type() &&
+ if (checker(dec.dec_type()) &&
(int)memberIdx == dec.struct_member_index()) {
found = true;
}
@@ -654,7 +682,7 @@
}
}
for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
- if (!checkForRequiredDecoration(id, decoration, type, vstate)) {
+ if (!checkForRequiredDecoration(id, checker, type, vstate)) {
return false;
}
}
@@ -806,6 +834,56 @@
++num_workgroup_variables_with_aliased;
}
}
+
+ if (spvIsVulkanEnv(vstate.context()->target_env)) {
+ const auto* models = vstate.GetExecutionModels(entry_point);
+ const bool has_frag =
+ models->find(SpvExecutionModelFragment) != models->end();
+ const bool has_vert =
+ models->find(SpvExecutionModelVertex) != models->end();
+ for (const auto& decoration :
+ vstate.id_decorations(var_instr->id())) {
+ if (decoration == SpvDecorationFlat ||
+ decoration == SpvDecorationNoPerspective ||
+ decoration == SpvDecorationSample ||
+ decoration == SpvDecorationCentroid) {
+ // VUID 04670 already validates these decorations are input/output
+ if (storage_class == SpvStorageClassInput &&
+ (models->size() > 1 || has_vert)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+ << vstate.VkErrorID(6202)
+ << "OpEntryPoint interfaces variable must not be vertex "
+ "execution model with an input storage class for "
+ "Entry Point id "
+ << entry_point << ".";
+ } else if (storage_class == SpvStorageClassOutput &&
+ (models->size() > 1 || has_frag)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+ << vstate.VkErrorID(6201)
+ << "OpEntryPoint interfaces variable must not be "
+ "fragment "
+ "execution model with an output storage class for "
+ "Entry Point id "
+ << entry_point << ".";
+ }
+ }
+ }
+
+ const bool has_flat =
+ hasDecoration(var_instr->id(), SpvDecorationFlat, vstate);
+ if (has_frag && storage_class == SpvStorageClassInput && !has_flat &&
+ ((vstate.IsFloatScalarType(type_id) &&
+ vstate.GetBitWidth(type_id) == 64) ||
+ vstate.IsIntScalarOrVectorType(type_id))) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+ << vstate.VkErrorID(4744)
+ << "Fragment OpEntryPoint operand "
+ << interface << " with Input interfaces with integer or "
+ "float type must have a Flat decoration "
+ "for Entry Point id "
+ << entry_point << ".";
+ }
+ }
}
if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
return vstate.diag(SPV_ERROR_INVALID_BINARY,
@@ -878,21 +956,23 @@
LayoutConstraints& constraint =
(*constraints)[std::make_pair(struct_id, memberIdx)];
constraint = inherited;
- for (auto& decoration : vstate.id_decorations(struct_id)) {
- if (decoration.struct_member_index() == (int)memberIdx) {
- switch (decoration.dec_type()) {
- case SpvDecorationRowMajor:
- constraint.majorness = kRowMajor;
- break;
- case SpvDecorationColMajor:
- constraint.majorness = kColumnMajor;
- break;
- case SpvDecorationMatrixStride:
- constraint.matrix_stride = decoration.params()[0];
- break;
- default:
- break;
- }
+ auto member_decorations =
+ vstate.id_member_decorations(struct_id, memberIdx);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ assert(decoration->struct_member_index() == (int)memberIdx);
+ switch (decoration->dec_type()) {
+ case SpvDecorationRowMajor:
+ constraint.majorness = kRowMajor;
+ break;
+ case SpvDecorationColMajor:
+ constraint.majorness = kColumnMajor;
+ break;
+ case SpvDecorationMatrixStride:
+ constraint.matrix_stride = decoration->params()[0];
+ break;
+ default:
+ break;
}
}
@@ -1144,30 +1224,48 @@
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must not use GLSLPacked decoration.";
- } else if (!checkForRequiredDecoration(id, SpvDecorationArrayStride,
- SpvOpTypeArray, vstate)) {
+ } else if (!checkForRequiredDecoration(
+ id,
+ [](SpvDecoration d) {
+ return d == SpvDecorationArrayStride;
+ },
+ SpvOpTypeArray, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must be explicitly laid out with ArrayStride "
"decorations.";
- } else if (!checkForRequiredDecoration(id,
- SpvDecorationMatrixStride,
- SpvOpTypeMatrix, vstate)) {
+ } else if (!checkForRequiredDecoration(
+ id,
+ [](SpvDecoration d) {
+ return d == SpvDecorationMatrixStride;
+ },
+ SpvOpTypeMatrix, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must be explicitly laid out with MatrixStride "
"decorations.";
+ } else if (!checkForRequiredDecoration(
+ id,
+ [](SpvDecoration d) {
+ return d == SpvDecorationRowMajor ||
+ d == SpvDecorationColMajor;
+ },
+ SpvOpTypeMatrix, vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
+ << "Structure id " << id << " decorated as " << deco_str
+ << " must be explicitly laid out with RowMajor or "
+ "ColMajor decorations.";
} else if (blockRules &&
- (SPV_SUCCESS != (recursive_status = checkLayout(
- id, sc_str, deco_str, true,
- scalar_block_layout, 0,
- constraints, vstate)))) {
+ (SPV_SUCCESS !=
+ (recursive_status = checkLayout(
+ id, sc_str, deco_str, true, scalar_block_layout, 0,
+ constraints, vstate)))) {
return recursive_status;
} else if (bufferRules &&
- (SPV_SUCCESS != (recursive_status = checkLayout(
- id, sc_str, deco_str, false,
- scalar_block_layout, 0,
- constraints, vstate)))) {
+ (SPV_SUCCESS !=
+ (recursive_status = checkLayout(
+ id, sc_str, deco_str, false, scalar_block_layout,
+ 0, constraints, vstate)))) {
return recursive_status;
}
}
@@ -1654,6 +1752,24 @@
"of a structure type";
}
+spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate,
+ const Instruction& inst,
+ const Decoration& decoration) {
+ // This is not the most precise check, but the rules for RelaxPrecision are
+ // very general, and it will be difficult to implement precisely. For now,
+ // I will only check for the cases that cause problems for the optimizer.
+ if (!spvOpcodeGeneratesType(inst.opcode())) {
+ return SPV_SUCCESS;
+ }
+
+ if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+ inst.opcode() == SpvOpTypeStruct) {
+ return SPV_SUCCESS;
+ }
+ return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+ << "RelaxPrecision decoration cannot be applied to a type";
+}
+
#define PASS_OR_BAIL_AT_LINE(X, LINE) \
{ \
spv_result_t e##LINE = (X); \
@@ -1708,6 +1824,10 @@
case SpvDecorationLocation:
PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
break;
+ case SpvDecorationRelaxedPrecision:
+ PASS_OR_BAIL(
+ CheckRelaxPrecisionDecoration(vstate, *inst, decoration));
+ break;
default:
break;
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_image.cpp b/third_party/SPIRV-Tools/source/val/validate_image.cpp
index f6d7d10..2d5e2c7 100644
--- a/third_party/SPIRV-Tools/source/val/validate_image.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_image.cpp
@@ -927,7 +927,7 @@
return SPV_SUCCESS;
}
-bool IsAllowedSampledImageOperand(SpvOp opcode) {
+bool IsAllowedSampledImageOperand(SpvOp opcode, ValidationState_t& _) {
switch (opcode) {
case SpvOpSampledImage:
case SpvOpImageSampleImplicitLod:
@@ -950,6 +950,9 @@
case SpvOpImageSparseDrefGather:
case SpvOpCopyObject:
return true;
+ case SpvOpStore:
+ if (_.HasCapability(SpvCapabilityBindlessTextureNV)) return true;
+ return false;
default:
return false;
}
@@ -1035,7 +1038,7 @@
<< _.getIdName(consumer_instr->id()) << "'.";
}
- if (!IsAllowedSampledImageOperand(consumer_opcode)) {
+ if (!IsAllowedSampledImageOperand(consumer_opcode, _)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Result <id> from OpSampledImage instruction must not appear "
"as operand for Op"
diff --git a/third_party/SPIRV-Tools/source/val/validate_instruction.cpp b/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
index 3edf163..767c0ce 100644
--- a/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
@@ -483,6 +483,22 @@
if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
return error;
}
+ } else if (opcode == SpvOpSamplerImageAddressingModeNV) {
+ if (!_.HasCapability(SpvCapabilityBindlessTextureNV)) {
+ return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
+ << "OpSamplerImageAddressingModeNV supported only with extension "
+ "SPV_NV_bindless_texture";
+ }
+ uint32_t bitwidth = inst->GetOperandAs<uint32_t>(0);
+ if (_.samplerimage_variable_address_mode() != 0) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "OpSamplerImageAddressingModeNV should only be provided once";
+ }
+ if (bitwidth != 32 && bitwidth != 64) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32";
+ }
+ _.set_samplerimage_variable_address_mode(bitwidth);
}
if (auto error = ReservedCheck(_, inst)) return error;
diff --git a/third_party/SPIRV-Tools/source/val/validate_layout.cpp b/third_party/SPIRV-Tools/source/val/validate_layout.cpp
index d582321..6f95135 100644
--- a/third_party/SPIRV-Tools/source/val/validate_layout.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_layout.cpp
@@ -363,6 +363,7 @@
case kLayoutExtensions:
case kLayoutExtInstImport:
case kLayoutMemoryModel:
+ case kLayoutSamplerImageAddressMode:
case kLayoutEntryPoint:
case kLayoutExecutionMode:
case kLayoutDebug1:
diff --git a/third_party/SPIRV-Tools/source/val/validate_logicals.cpp b/third_party/SPIRV-Tools/source/val/validate_logicals.cpp
index 5307988..ec1e207 100644
--- a/third_party/SPIRV-Tools/source/val/validate_logicals.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_logicals.cpp
@@ -170,6 +170,16 @@
break;
}
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeImage:
+ case SpvOpTypeSampler: {
+ if (!_.HasCapability(SpvCapabilityBindlessTextureNV))
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Using image/sampler with OpSelect requires capability "
+ << "BindlessTextureNV";
+ break;
+ }
+
case SpvOpTypeVector: {
dimension = type_inst->word(3);
break;
diff --git a/third_party/SPIRV-Tools/source/val/validate_memory.cpp b/third_party/SPIRV-Tools/source/val/validate_memory.cpp
index af9da67..425a8d3 100644
--- a/third_party/SPIRV-Tools/source/val/validate_memory.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_memory.cpp
@@ -35,8 +35,8 @@
const Instruction*);
bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*,
const Instruction*);
-bool HasConflictingMemberOffsets(const std::vector<Decoration>&,
- const std::vector<Decoration>&);
+bool HasConflictingMemberOffsets(const std::set<Decoration>&,
+ const std::set<Decoration>&);
bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
std::initializer_list<uint32_t> allowed) {
@@ -105,10 +105,8 @@
"type1 must be an OpTypeStruct instruction.");
assert(type2->opcode() == SpvOpTypeStruct &&
"type2 must be an OpTypeStruct instruction.");
- const std::vector<Decoration>& type1_decorations =
- _.id_decorations(type1->id());
- const std::vector<Decoration>& type2_decorations =
- _.id_decorations(type2->id());
+ const std::set<Decoration>& type1_decorations = _.id_decorations(type1->id());
+ const std::set<Decoration>& type2_decorations = _.id_decorations(type2->id());
// TODO: Will have to add other check for arrays an matricies if we want to
// handle them.
@@ -120,8 +118,8 @@
}
bool HasConflictingMemberOffsets(
- const std::vector<Decoration>& type1_decorations,
- const std::vector<Decoration>& type2_decorations) {
+ const std::set<Decoration>& type1_decorations,
+ const std::set<Decoration>& type2_decorations) {
{
// We are interested in conflicting decoration. If a decoration is in one
// list but not the other, then we will assume the code is correct. We are
@@ -526,8 +524,8 @@
if (storage_class == SpvStorageClassPushConstant) {
if (pointee->opcode() != SpvOpTypeStruct) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "PushConstant OpVariable <id> '" << _.getIdName(inst->id())
- << "' has illegal type.\n"
+ << _.VkErrorID(6808) << "PushConstant OpVariable <id> '"
+ << _.getIdName(inst->id()) << "' has illegal type.\n"
<< "From Vulkan spec, Push Constant Interface section:\n"
<< "Such variables must be typed as OpTypeStruct";
}
@@ -554,9 +552,9 @@
if (storage_class == SpvStorageClassUniform) {
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Uniform OpVariable <id> '" << _.getIdName(inst->id())
- << "' has illegal type.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << _.VkErrorID(6807) << "Uniform OpVariable <id> '"
+ << _.getIdName(inst->id()) << "' has illegal type.\n"
+ << "From Vulkan spec:\n"
<< "Variables identified with the Uniform storage class are "
<< "used to access transparent buffer backed resources. Such "
<< "variables must be typed as OpTypeStruct, or an array of "
@@ -567,9 +565,9 @@
if (storage_class == SpvStorageClassStorageBuffer) {
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "StorageBuffer OpVariable <id> '" << _.getIdName(inst->id())
- << "' has illegal type.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> '"
+ << _.getIdName(inst->id()) << "' has illegal type.\n"
+ << "From Vulkan spec:\n"
<< "Variables identified with the StorageBuffer storage class "
"are used to access transparent buffer backed resources. "
"Such variables must be typed as OpTypeStruct, or an array "
@@ -982,6 +980,7 @@
}
if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(6925)
<< "In the Vulkan environment, cannot store to Uniform Blocks";
}
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_misc.cpp b/third_party/SPIRV-Tools/source/val/validate_misc.cpp
index 3bc15ca..5acc21e 100644
--- a/third_party/SPIRV-Tools/source/val/validate_misc.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_misc.cpp
@@ -59,10 +59,7 @@
// a vector of two - components of 32 -
// bit unsigned integer type
const uint32_t result_type = inst->type_id();
- if (!(_.IsUnsignedIntScalarType(result_type) &&
- _.GetBitWidth(result_type) == 64) &&
- !(_.IsUnsignedIntVectorType(result_type) &&
- _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) {
+ if (!_.IsUnsigned64BitHandle(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a "
"vector of two components"
" of unsigned integer"
diff --git a/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp b/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
index 9635268..67b10c5 100644
--- a/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
@@ -112,6 +112,44 @@
<< "Fragment execution model entry points can specify at most "
"one fragment shader interlock execution mode.";
}
+ if (execution_modes &&
+ 1 < std::count_if(
+ execution_modes->begin(), execution_modes->end(),
+ [](const SpvExecutionMode& mode) {
+ switch (mode) {
+ case SpvExecutionModeStencilRefUnchangedFrontAMD:
+ case SpvExecutionModeStencilRefLessFrontAMD:
+ case SpvExecutionModeStencilRefGreaterFrontAMD:
+ return true;
+ default:
+ return false;
+ }
+ })) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedFrontAMD, "
+ "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
+ "execution modes.";
+ }
+ if (execution_modes &&
+ 1 < std::count_if(
+ execution_modes->begin(), execution_modes->end(),
+ [](const SpvExecutionMode& mode) {
+ switch (mode) {
+ case SpvExecutionModeStencilRefUnchangedBackAMD:
+ case SpvExecutionModeStencilRefLessBackAMD:
+ case SpvExecutionModeStencilRefGreaterBackAMD:
+ return true;
+ default:
+ return false;
+ }
+ })) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedBackAMD, "
+ "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
+ "execution modes.";
+ }
break;
case SpvExecutionModelTessellationControl:
case SpvExecutionModelTessellationEvaluation:
@@ -412,6 +450,13 @@
case SpvExecutionModeSampleInterlockUnorderedEXT:
case SpvExecutionModeShadingRateInterlockOrderedEXT:
case SpvExecutionModeShadingRateInterlockUnorderedEXT:
+ case SpvExecutionModeEarlyAndLateFragmentTestsAMD:
+ case SpvExecutionModeStencilRefUnchangedFrontAMD:
+ case SpvExecutionModeStencilRefGreaterFrontAMD:
+ case SpvExecutionModeStencilRefLessFrontAMD:
+ case SpvExecutionModeStencilRefUnchangedBackAMD:
+ case SpvExecutionModeStencilRefGreaterBackAMD:
+ case SpvExecutionModeStencilRefLessBackAMD:
if (!std::all_of(models->begin(), models->end(),
[](const SpvExecutionModel& model) {
return model == SpvExecutionModelFragment;
diff --git a/third_party/SPIRV-Tools/source/val/validate_ray_query.cpp b/third_party/SPIRV-Tools/source/val/validate_ray_query.cpp
new file mode 100644
index 0000000..f92bf01
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/val/validate_ray_query.cpp
@@ -0,0 +1,271 @@
+// Copyright (c) 2022 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.
+
+// Validates ray query instructions from SPV_KHR_ray_query
+
+#include "source/opcode.h"
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+spv_result_t ValidateRayQueryPointer(ValidationState_t& _,
+ const Instruction* inst,
+ uint32_t ray_query_index) {
+ const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index);
+ auto variable = _.FindDef(ray_query_id);
+ if (!variable || (variable->opcode() != SpvOpVariable &&
+ variable->opcode() != SpvOpFunctionParameter)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Query must be a memory object declaration";
+ }
+ auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
+ if (!pointer || pointer->opcode() != SpvOpTypePointer) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Query must be a pointer";
+ }
+ auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+ if (!type || type->opcode() != SpvOpTypeRayQueryKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Query must be a pointer to OpTypeRayQueryKHR";
+ }
+ return SPV_SUCCESS;
+}
+
+spv_result_t ValidateIntersectionId(ValidationState_t& _,
+ const Instruction* inst,
+ uint32_t intersection_index) {
+ const uint32_t intersection_id =
+ inst->GetOperandAs<uint32_t>(intersection_index);
+ const uint32_t intersection_type = _.GetTypeId(intersection_id);
+ const SpvOp intersection_opcode = _.GetIdOpcode(intersection_id);
+ if (!_.IsIntScalarType(intersection_type) ||
+ _.GetBitWidth(intersection_type) != 32 ||
+ !spvOpcodeIsConstant(intersection_opcode)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Intersection ID to be a constant 32-bit int scalar";
+ }
+
+ return SPV_SUCCESS;
+}
+
+} // namespace
+
+spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst) {
+ const SpvOp opcode = inst->opcode();
+ const uint32_t result_type = inst->type_id();
+
+ switch (opcode) {
+ case SpvOpRayQueryInitializeKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
+
+ if (_.GetIdOpcode(_.GetOperandTypeId(inst, 1)) !=
+ SpvOpTypeAccelerationStructureKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Acceleration Structure to be of type "
+ "OpTypeAccelerationStructureKHR";
+ }
+
+ const uint32_t ray_flags = _.GetOperandTypeId(inst, 2);
+ if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Flags must be a 32-bit int scalar";
+ }
+
+ const uint32_t cull_mask = _.GetOperandTypeId(inst, 3);
+ if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Cull Mask must be a 32-bit int scalar";
+ }
+
+ const uint32_t ray_origin = _.GetOperandTypeId(inst, 4);
+ if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+ _.GetBitWidth(ray_origin) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Origin must be a 32-bit float 3-component vector";
+ }
+
+ const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5);
+ if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray TMin must be a 32-bit float scalar";
+ }
+
+ const uint32_t ray_direction = _.GetOperandTypeId(inst, 6);
+ if (!_.IsFloatVectorType(ray_direction) ||
+ _.GetDimension(ray_direction) != 3 ||
+ _.GetBitWidth(ray_direction) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Direction must be a 32-bit float 3-component vector";
+ }
+
+ const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7);
+ if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray TMax must be a 32-bit float scalar";
+ }
+ break;
+ }
+
+ case SpvOpRayQueryTerminateKHR:
+ case SpvOpRayQueryConfirmIntersectionKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
+ break;
+ }
+
+ case SpvOpRayQueryGenerateIntersectionKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
+
+ const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1);
+ if (!_.IsFloatScalarType(hit_t_id) || _.GetBitWidth(hit_t_id) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Hit T must be a 32-bit float scalar";
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionFrontFaceKHR:
+ case SpvOpRayQueryProceedKHR:
+ case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsBoolScalarType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be bool scalar type";
+ }
+
+ if (opcode == SpvOpRayQueryGetIntersectionFrontFaceKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionTKHR:
+ case SpvOpRayQueryGetRayTMinKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsFloatScalarType(result_type) ||
+ _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit float scalar type";
+ }
+
+ if (opcode == SpvOpRayQueryGetIntersectionTKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionTypeKHR:
+ case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR:
+ case SpvOpRayQueryGetIntersectionInstanceIdKHR:
+ case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
+ case SpvOpRayQueryGetIntersectionGeometryIndexKHR:
+ case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR:
+ case SpvOpRayQueryGetRayFlagsKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit int scalar type";
+ }
+
+ if (opcode != SpvOpRayQueryGetRayFlagsKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR:
+ case SpvOpRayQueryGetIntersectionObjectRayOriginKHR:
+ case SpvOpRayQueryGetWorldRayDirectionKHR:
+ case SpvOpRayQueryGetWorldRayOriginKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsFloatVectorType(result_type) ||
+ _.GetDimension(result_type) != 3 ||
+ _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit float 3-component "
+ "vector type";
+ }
+
+ if (opcode == SpvOpRayQueryGetIntersectionObjectRayDirectionKHR ||
+ opcode == SpvOpRayQueryGetIntersectionObjectRayOriginKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionBarycentricsKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+ if (!_.IsFloatVectorType(result_type) ||
+ _.GetDimension(result_type) != 2 ||
+ _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit float 2-component "
+ "vector type";
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionObjectToWorldKHR:
+ case SpvOpRayQueryGetIntersectionWorldToObjectKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+ uint32_t num_rows = 0;
+ uint32_t num_cols = 0;
+ uint32_t col_type = 0;
+ uint32_t component_type = 0;
+ if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
+ &component_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected matrix type as Result Type";
+ }
+
+ if (num_cols != 4) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type matrix to have a Column Count of 4";
+ }
+
+ if (!_.IsFloatScalarType(component_type) ||
+ _.GetBitWidth(result_type) != 32 || num_rows != 3) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type matrix to have a Column Type of "
+ "3-component 32-bit float vectors";
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return SPV_SUCCESS;
+}
+
+} // namespace val
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
index 1c5f70a..887e8d1 100644
--- a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
@@ -220,30 +220,23 @@
// Vulkan Specific rules
if (spvIsVulkanEnv(_.context()->target_env)) {
- if (value == SpvScopeCrossDevice) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(4638) << spvOpcodeString(opcode)
- << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
- }
- // Vulkan 1.0 specific rules
- if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
- value != SpvScopeDevice && value != SpvScopeWorkgroup &&
- value != SpvScopeInvocation) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(4638) << spvOpcodeString(opcode)
- << ": in Vulkan 1.0 environment Memory Scope is limited to "
- << "Device, Workgroup and Invocation";
- }
- // Vulkan 1.1 specific rules
- if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 ||
- _.context()->target_env == SPV_ENV_VULKAN_1_2) &&
- value != SpvScopeDevice && value != SpvScopeWorkgroup &&
+ if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
value != SpvScopeSubgroup && value != SpvScopeInvocation &&
- value != SpvScopeShaderCallKHR) {
+ value != SpvScopeShaderCallKHR && value != SpvScopeQueueFamily) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4638) << spvOpcodeString(opcode)
- << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
- << "to Device, Workgroup, Invocation, and ShaderCall";
+ << ": in Vulkan environment Memory Scope is limited to Device, "
+ "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or "
+ "Invocation";
+ } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
+ value == SpvScopeSubgroup &&
+ !_.HasCapability(SpvCapabilitySubgroupBallotKHR) &&
+ !_.HasCapability(SpvCapabilitySubgroupVoteKHR)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": in Vulkan 1.0 environment Memory Scope is can not be "
+ "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
+ "declared";
}
if (value == SpvScopeShaderCallKHR) {
diff --git a/third_party/SPIRV-Tools/source/val/validate_type.cpp b/third_party/SPIRV-Tools/source/val/validate_type.cpp
index 2aded61..b0b6079 100644
--- a/third_party/SPIRV-Tools/source/val/validate_type.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_type.cpp
@@ -306,35 +306,6 @@
return SPV_SUCCESS;
}
-bool ContainsOpaqueType(ValidationState_t& _, const Instruction* str) {
- const size_t elem_type_index = 1;
- uint32_t elem_type_id;
- Instruction* elem_type;
-
- if (spvOpcodeIsBaseOpaqueType(str->opcode())) {
- return true;
- }
-
- switch (str->opcode()) {
- case SpvOpTypeArray:
- case SpvOpTypeRuntimeArray:
- elem_type_id = str->GetOperandAs<uint32_t>(elem_type_index);
- elem_type = _.FindDef(elem_type_id);
- return ContainsOpaqueType(_, elem_type);
- case SpvOpTypeStruct:
- for (size_t member_type_index = 1;
- member_type_index < str->operands().size(); ++member_type_index) {
- auto member_type_id = str->GetOperandAs<uint32_t>(member_type_index);
- auto member_type = _.FindDef(member_type_id);
- if (ContainsOpaqueType(_, member_type)) return true;
- }
- break;
- default:
- break;
- }
- return false;
-}
-
spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
const uint32_t struct_id = inst->GetOperandAs<uint32_t>(0);
for (size_t member_type_index = 1;
@@ -425,8 +396,21 @@
_.RegisterStructTypeWithBuiltInMember(struct_id);
}
+ const auto isOpaqueType = [&_](const Instruction* opaque_inst) {
+ auto opcode = opaque_inst->opcode();
+ if (_.HasCapability(SpvCapabilityBindlessTextureNV) &&
+ (opcode == SpvOpTypeImage || opcode == SpvOpTypeSampler ||
+ opcode == SpvOpTypeSampledImage)) {
+ return false;
+ } else if (spvOpcodeIsBaseOpaqueType(opcode)) {
+ return true;
+ }
+ return false;
+ };
+
if (spvIsVulkanEnv(_.context()->target_env) &&
- !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) {
+ !_.options()->before_hlsl_legalization &&
+ _.ContainsType(inst->id(), isOpaqueType)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4667) << "In "
<< spvLogStringForEnv(_.context()->target_env)
diff --git a/third_party/SPIRV-Tools/source/val/validation_state.cpp b/third_party/SPIRV-Tools/source/val/validation_state.cpp
index d9422b2..adfe75b 100644
--- a/third_party/SPIRV-Tools/source/val/validation_state.cpp
+++ b/third_party/SPIRV-Tools/source/val/validation_state.cpp
@@ -90,6 +90,8 @@
if (current_section == kLayoutFunctionDeclarations)
return kLayoutFunctionDeclarations;
return kLayoutFunctionDefinitions;
+ case SpvOpSamplerImageAddressingModeNV:
+ return kLayoutSamplerImageAddressMode;
default:
break;
}
@@ -161,6 +163,7 @@
addressing_model_(SpvAddressingModelMax),
memory_model_(SpvMemoryModelMax),
pointer_size_and_alignment_(0),
+ sampler_image_addressing_mode_(0),
in_function_(false),
num_of_warnings_(0),
max_num_of_warnings_(max_warnings) {
@@ -473,6 +476,15 @@
SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; }
+void ValidationState_t::set_samplerimage_variable_address_mode(
+ uint32_t bit_width) {
+ sampler_image_addressing_mode_ = bit_width;
+}
+
+uint32_t ValidationState_t::samplerimage_variable_address_mode() const {
+ return sampler_image_addressing_mode_;
+}
+
spv_result_t ValidationState_t::RegisterFunction(
uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
uint32_t function_type_id) {
@@ -965,6 +977,11 @@
return true;
}
+bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const {
+ const Instruction* inst = FindDef(id);
+ return inst && inst->opcode() == SpvOpTypeAccelerationStructureKHR;
+}
+
bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const {
const Instruction* inst = FindDef(id);
return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV;
@@ -985,6 +1002,13 @@
return IsUnsignedIntScalarType(FindDef(id)->word(2));
}
+// Either a 32 bit 2-component uint vector or a 64 bit uint scalar
+bool ValidationState_t::IsUnsigned64BitHandle(uint32_t id) const {
+ return ((IsUnsignedIntScalarType(id) && GetBitWidth(id) == 64) ||
+ (IsUnsignedIntVectorType(id) && GetDimension(id) == 2 &&
+ GetBitWidth(id) == 32));
+}
+
spv_result_t ValidationState_t::CooperativeMatrixShapesMatch(
const Instruction* inst, uint32_t m1, uint32_t m2) {
const auto m1_type = FindDef(m1);
@@ -1878,7 +1902,7 @@
case 4677:
return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
case 4680:
- return VUID_WRAP( VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
+ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
case 4682:
return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
case 6426:
@@ -1903,6 +1927,8 @@
return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
case 4734:
return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
+ case 4744:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744);
case 4777:
return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
case 4780:
@@ -1919,6 +1945,10 @@
return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
case 4919:
return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
+ case 6201:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201);
+ case 6202:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202);
case 6214:
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
case 6491:
@@ -1941,6 +1971,12 @@
return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777);
case 6778:
return VUID_WRAP(VUID-StandaloneSpirv-Input-06778);
+ case 6807:
+ return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807);
+ case 6808:
+ return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808);
+ case 6925:
+ return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925);
default:
return ""; // unknown id
}
diff --git a/third_party/SPIRV-Tools/source/val/validation_state.h b/third_party/SPIRV-Tools/source/val/validation_state.h
index 4888840..b4d343d 100644
--- a/third_party/SPIRV-Tools/source/val/validation_state.h
+++ b/third_party/SPIRV-Tools/source/val/validation_state.h
@@ -44,19 +44,20 @@
/// of the SPIRV spec for additional details of the order. The enumerant values
/// are in the same order as the vector returned by GetModuleOrder
enum ModuleLayoutSection {
- kLayoutCapabilities, /// < Section 2.4 #1
- kLayoutExtensions, /// < Section 2.4 #2
- kLayoutExtInstImport, /// < Section 2.4 #3
- kLayoutMemoryModel, /// < Section 2.4 #4
- kLayoutEntryPoint, /// < Section 2.4 #5
- kLayoutExecutionMode, /// < Section 2.4 #6
- kLayoutDebug1, /// < Section 2.4 #7 > 1
- kLayoutDebug2, /// < Section 2.4 #7 > 2
- kLayoutDebug3, /// < Section 2.4 #7 > 3
- kLayoutAnnotations, /// < Section 2.4 #8
- kLayoutTypes, /// < Section 2.4 #9
- kLayoutFunctionDeclarations, /// < Section 2.4 #10
- kLayoutFunctionDefinitions /// < Section 2.4 #11
+ kLayoutCapabilities, /// < Section 2.4 #1
+ kLayoutExtensions, /// < Section 2.4 #2
+ kLayoutExtInstImport, /// < Section 2.4 #3
+ kLayoutMemoryModel, /// < Section 2.4 #4
+ kLayoutSamplerImageAddressMode, /// < Section 2.4 #5
+ kLayoutEntryPoint, /// < Section 2.4 #6
+ kLayoutExecutionMode, /// < Section 2.4 #7
+ kLayoutDebug1, /// < Section 2.4 #8 > 1
+ kLayoutDebug2, /// < Section 2.4 #8 > 2
+ kLayoutDebug3, /// < Section 2.4 #8 > 3
+ kLayoutAnnotations, /// < Section 2.4 #9
+ kLayoutTypes, /// < Section 2.4 #10
+ kLayoutFunctionDeclarations, /// < Section 2.4 #11
+ kLayoutFunctionDefinitions /// < Section 2.4 #12
};
/// This class manages the state of the SPIR-V validation as it is being parsed.
@@ -360,6 +361,20 @@
/// Returns the memory model of this module, or Simple if uninitialized.
SpvMemoryModel memory_model() const;
+ /// Sets the bit width for sampler/image type variables. If not set, they are
+ /// considered opaque
+ void set_samplerimage_variable_address_mode(uint32_t bit_width);
+
+ /// Get the addressing mode currently set. If 0, it means addressing mode is
+ /// invalid Sampler/Image type variables must be considered opaque This mode
+ /// is only valid after the instruction has been read
+ uint32_t samplerimage_variable_address_mode() const;
+
+ /// Returns true if the OpSamplerImageAddressingModeNV was found.
+ bool has_samplerimage_variable_address_mode_specified() const {
+ return sampler_image_addressing_mode_ != 0;
+ }
+
const AssemblyGrammar& grammar() const { return grammar_; }
/// Inserts the instruction into the list of ordered instructions in the file.
@@ -375,17 +390,14 @@
/// Registers the decoration for the given <id>
void RegisterDecorationForId(uint32_t id, const Decoration& dec) {
auto& dec_list = id_decorations_[id];
- auto lb = std::find(dec_list.begin(), dec_list.end(), dec);
- if (lb == dec_list.end()) {
- dec_list.push_back(dec);
- }
+ dec_list.insert(dec);
}
/// Registers the list of decorations for the given <id>
template <class InputIt>
void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) {
- std::vector<Decoration>& cur_decs = id_decorations_[id];
- cur_decs.insert(cur_decs.end(), begin, end);
+ std::set<Decoration>& cur_decs = id_decorations_[id];
+ cur_decs.insert(begin, end);
}
/// Registers the list of decorations for the given member of the given
@@ -394,21 +406,44 @@
void RegisterDecorationsForStructMember(uint32_t struct_id,
uint32_t member_index, InputIt begin,
InputIt end) {
- RegisterDecorationsForId(struct_id, begin, end);
- for (auto& decoration : id_decorations_[struct_id]) {
- decoration.set_struct_member_index(member_index);
+ std::set<Decoration>& cur_decs = id_decorations_[struct_id];
+ for (InputIt iter = begin; iter != end; ++iter) {
+ Decoration dec = *iter;
+ dec.set_struct_member_index(member_index);
+ cur_decs.insert(dec);
}
}
/// Returns all the decorations for the given <id>. If no decorations exist
- /// for the <id>, it registers an empty vector for it in the map and
- /// returns the empty vector.
- std::vector<Decoration>& id_decorations(uint32_t id) {
+ /// for the <id>, it registers an empty set for it in the map and
+ /// returns the empty set.
+ std::set<Decoration>& id_decorations(uint32_t id) {
return id_decorations_[id];
}
+ /// Returns the range of decorations for the given field of the given <id>.
+ struct FieldDecorationsIter {
+ std::set<Decoration>::const_iterator begin;
+ std::set<Decoration>::const_iterator end;
+ };
+ FieldDecorationsIter id_member_decorations(uint32_t id,
+ uint32_t member_index) {
+ const auto& decorations = id_decorations_[id];
+
+ // The decorations are sorted by member_index, so this look up will give the
+ // exact range of decorations for this member index.
+ Decoration min_decoration((SpvDecoration)0, {}, member_index);
+ Decoration max_decoration(SpvDecorationMax, {}, member_index);
+
+ FieldDecorationsIter result;
+ result.begin = decorations.lower_bound(min_decoration);
+ result.end = decorations.upper_bound(max_decoration);
+
+ return result;
+ }
+
// Returns const pointer to the internal decoration container.
- const std::map<uint32_t, std::vector<Decoration>>& id_decorations() const {
+ const std::map<uint32_t, std::set<Decoration>>& id_decorations() const {
return id_decorations_;
}
@@ -572,10 +607,12 @@
bool IsBoolVectorType(uint32_t id) const;
bool IsBoolScalarOrVectorType(uint32_t id) const;
bool IsPointerType(uint32_t id) const;
+ bool IsAccelerationStructureType(uint32_t id) const;
bool IsCooperativeMatrixType(uint32_t id) const;
bool IsFloatCooperativeMatrixType(uint32_t id) const;
bool IsIntCooperativeMatrixType(uint32_t id) const;
bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const;
+ bool IsUnsigned64BitHandle(uint32_t id) const;
// Returns true if |id| is a type id that contains |type| (or integer or
// floating point type) of |width| bits.
@@ -826,7 +863,7 @@
struct_has_nested_blockorbufferblock_struct_;
/// Stores the list of decorations for a given <id>
- std::map<uint32_t, std::vector<Decoration>> id_decorations_;
+ std::map<uint32_t, std::set<Decoration>> id_decorations_;
/// Stores type declarations which need to be unique (i.e. non-aggregates),
/// in the form [opcode, operand words], result_id is not stored.
@@ -842,7 +879,10 @@
// have the same pointer size (for physical pointer types).
uint32_t pointer_size_and_alignment_;
- /// NOTE: See corresponding getter functions
+ /// bit width of sampler/image type variables. Valid values are 32 and 64
+ uint32_t sampler_image_addressing_mode_;
+
+ /// NOTE: See correspoding getter functions
bool in_function_;
/// The state of optional features. These are determined by capabilities
diff --git a/third_party/SPIRV-Tools/test/binary_to_text_test.cpp b/third_party/SPIRV-Tools/test/binary_to_text_test.cpp
index df703e5..44705f2 100644
--- a/third_party/SPIRV-Tools/test/binary_to_text_test.cpp
+++ b/third_party/SPIRV-Tools/test/binary_to_text_test.cpp
@@ -101,6 +101,17 @@
spvTextDestroy(text);
}
+TEST_F(BinaryToText, Print) {
+ spv_text text = nullptr;
+ spv_diagnostic diagnostic = nullptr;
+ ASSERT_EQ(
+ SPV_SUCCESS,
+ spvBinaryToText(context, binary->code, binary->wordCount,
+ SPV_BINARY_TO_TEXT_OPTION_PRINT, &text, &diagnostic));
+ ASSERT_EQ(text, nullptr);
+ spvTextDestroy(text);
+}
+
TEST_F(BinaryToText, MissingModule) {
spv_text text;
spv_diagnostic diagnostic = nullptr;
diff --git a/third_party/SPIRV-Tools/test/hex_float_test.cpp b/third_party/SPIRV-Tools/test/hex_float_test.cpp
index 7edfd43..25d3c70 100644
--- a/third_party/SPIRV-Tools/test/hex_float_test.cpp
+++ b/third_party/SPIRV-Tools/test/hex_float_test.cpp
@@ -1395,6 +1395,47 @@
{"0x1.0p1+", true, "+", 2.0f},
{"0x1.0p1-", true, "-", 2.0f}}));
+INSTANTIATE_TEST_SUITE_P(
+ HexFloatPositiveExponentOverflow, FloatStreamParseTest,
+ ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+ // Positive exponents
+ {"0x1.0p1", true, "", 2.0f}, // fine, a normal number
+ {"0x1.0p15", true, "", 32768.0f}, // fine, a normal number
+ {"0x1.0p127", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x0.8p128", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x0.1p131", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x0.01p135", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x1.0p128", true, "", float(ldexp(1.0f, 128))}, // infinity
+ {"0x1.0p4294967295", true, "", float(ldexp(1.0f, 128))}, // infinity
+ {"0x1.0p5000000000", true, "", float(ldexp(1.0f, 128))}, // infinity
+ {"0x0.0p5000000000", true, "", 0.0f}, // zero mantissa, zero result
+ }));
+
+INSTANTIATE_TEST_SUITE_P(
+ HexFloatNegativeExponentOverflow, FloatStreamParseTest,
+ ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+ // Positive results, digits before '.'
+ {"0x1.0p-126", true, "",
+ float(ldexp(1.0f, -126))}, // fine, a small normal number
+ {"0x1.0p-127", true, "", float(ldexp(1.0f, -127))}, // denorm number
+ {"0x1.0p-149", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+ {"0x0.8p-148", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+ {"0x0.1p-145", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+ {"0x0.01p-141", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+
+ // underflow rounds down to zero
+ {"0x1.0p-150", true, "", 0.0f},
+ {"0x1.0p-4294967296", true, "",
+ 0.0f}, // avoid exponent overflow in parser
+ {"0x1.0p-5000000000", true, "",
+ 0.0f}, // avoid exponent overflow in parser
+ {"0x0.0p-5000000000", true, "", 0.0f}, // zero mantissa, zero result
+ }));
+
// TODO(awoloszyn): Add fp16 tests and HexFloatTraits.
} // namespace
} // namespace utils
diff --git a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
index 25f8541..89cb56f 100644
--- a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
@@ -3541,9 +3541,11 @@
OpBranch %16
%16 = OpLabel
OpSelectionMerge %18 None
-OpSwitch %13 %18 0 %17 1 %15
+OpSwitch %13 %18 0 %17 1 %19
%17 = OpLabel
OpStore %3 %uint_1
+OpBranch %19
+%19 = OpLabel
OpBranch %15
%15 = OpLabel
OpBranch %12
@@ -7626,6 +7628,55 @@
SinglePassRunAndCheck<AggressiveDCEPass>(text, text, false);
}
+TEST_F(AggressiveDCETest, FunctionBecomesUnreachableAfterDCE) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 320
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %7 = OpTypeFunction %int
+ %int_n1 = OpConstant %int -1
+ %2 = OpFunction %void None %4
+ %9 = OpLabel
+ OpKill
+ %10 = OpLabel
+ %11 = OpFunctionCall %int %12
+ OpReturn
+ OpFunctionEnd
+; CHECK: {{%\w+}} = OpFunction %int DontInline|Pure
+ %12 = OpFunction %int DontInline|Pure %7
+; CHECK-NEXT: {{%\w+}} = OpLabel
+ %13 = OpLabel
+ %14 = OpVariable %_ptr_Function_int Function
+; CHECK-NEXT: OpBranch [[header:%\w+]]
+ OpBranch %15
+; CHECK-NEXT: [[header]] = OpLabel
+; CHECK-NEXT: OpBranch [[merge:%\w+]]
+ %15 = OpLabel
+ OpLoopMerge %16 %17 None
+ OpBranch %18
+ %18 = OpLabel
+ %19 = OpLoad %int %14
+ OpBranch %17
+ %17 = OpLabel
+ OpBranch %15
+; CHECK-NEXT: [[merge]] = OpLabel
+ %16 = OpLabel
+; CHECK-NEXT: OpReturnValue %int_n1
+ OpReturnValue %int_n1
+; CHECK-NEXT: OpFunctionEnd
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/cfg_test.cpp b/third_party/SPIRV-Tools/test/opt/cfg_test.cpp
index 2cfc9f3..7dfd2bc 100644
--- a/third_party/SPIRV-Tools/test/opt/cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/cfg_test.cpp
@@ -200,6 +200,125 @@
ContainerEq(expected_result2)));
}
+TEST_F(CFGTest, SplitLoopHeaderForSingleBlockLoop) {
+ const std::string test = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %void = OpTypeVoid
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %6 = OpTypeFunction %void
+ %2 = OpFunction %void None %6
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ %9 = OpPhi %uint %uint_0 %7 %9 %8
+ OpLoopMerge %10 %8 None
+ OpBranch %8
+ %10 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ const std::string expected_result = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%6 = OpTypeFunction %void
+%2 = OpFunction %void None %6
+%7 = OpLabel
+OpBranch %8
+%8 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%9 = OpPhi %uint %9 %11 %uint_0 %8
+OpLoopMerge %10 %11 None
+OpBranch %11
+%10 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ BasicBlock* loop_header = context->get_instr_block(8);
+ ASSERT_TRUE(loop_header->GetLoopMergeInst() != nullptr);
+
+ CFG* cfg = context->cfg();
+ cfg->SplitLoopHeader(loop_header);
+
+ std::vector<uint32_t> binary;
+ bool skip_nop = false;
+ context->module()->ToBinary(&binary, skip_nop);
+
+ std::string optimized_asm;
+ SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+ EXPECT_TRUE(tools.Disassemble(binary, &optimized_asm,
+ SpirvTools::kDefaultDisassembleOption))
+ << "Disassembling failed for shader\n"
+ << std::endl;
+
+ EXPECT_EQ(optimized_asm, expected_result);
+}
+
+TEST_F(CFGTest, ComputeStructedOrderForLoop) {
+ const std::string test = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpName %main "main"
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%5 = OpConstant %uint 5
+%main = OpFunction %void None %4
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %11 %10 None
+OpBranchConditional %true %11 %10
+%10 = OpLabel
+OpBranch %9
+%11 = OpLabel
+OpBranch %12
+%12 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ CFG* cfg = context->cfg();
+ Module* module = context->module();
+ Function* function = &*module->begin();
+ std::list<BasicBlock*> order;
+ cfg->ComputeStructuredOrder(function, context->get_instr_block(9),
+ context->get_instr_block(11), &order);
+
+ // Order should contain the loop header, the continue target, and the merge
+ // node.
+ std::list<BasicBlock*> expected_result = {context->get_instr_block(9),
+ context->get_instr_block(10),
+ context->get_instr_block(11)};
+ EXPECT_THAT(order, ContainerEq(expected_result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
index b04c8f5..1095d3b 100644
--- a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
@@ -2570,9 +2570,8 @@
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop3) {
// Checks that if a selection merge construct contains a conditional branch
- // to the merge of a surrounding loop, the selection merge, and another block
- // inside the selection merge, then we must keep the OpSelectionMerge
- // instruction on that branch.
+ // to the selection merge, and another block inside the selection merge,
+ // then we must keep the OpSelectionMerge instruction on that branch.
const std::string predefs = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -2586,6 +2585,7 @@
%true = OpConstantTrue %bool
%uint = OpTypeInt 32 0
%undef_int = OpUndef %uint
+%undef_bool = OpUndef %bool
)";
const std::string body =
@@ -2596,7 +2596,7 @@
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
-; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge]] [[bb3:%\w+]]
; CHECK: [[bb3]] = OpLabel
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
@@ -2613,7 +2613,8 @@
OpSelectionMerge %sel_merge None
OpBranchConditional %true %bb2 %bb4
%bb2 = OpLabel
-OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3
+;OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3
+OpBranchConditional %undef_bool %sel_merge %bb3
%bb3 = OpLabel
OpBranch %sel_merge
%bb4 = OpLabel
@@ -2632,9 +2633,8 @@
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue3) {
// Checks that if a selection merge construct contains a conditional branch
- // to the merge of a surrounding loop, the selection merge, and another block
- // inside the selection merge, then we must keep the OpSelectionMerge
- // instruction on that branch.
+ // the selection merge, and another block inside the selection merge, then we
+ // must keep the OpSelectionMerge instruction on that branch.
const std::string predefs = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -2648,6 +2648,7 @@
%true = OpConstantTrue %bool
%uint = OpTypeInt 32 0
%undef_int = OpUndef %uint
+%undef_bool = OpUndef %bool
)";
const std::string body =
@@ -2660,7 +2661,7 @@
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
-; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_continue]] 1 [[bb3:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge]] [[bb3:%\w+]]
; CHECK: [[bb3]] = OpLabel
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
@@ -2679,131 +2680,7 @@
OpSelectionMerge %sel_merge None
OpBranchConditional %true %bb2 %bb4
%bb2 = OpLabel
-OpSwitch %undef_int %sel_merge 0 %cont 1 %bb3
-%bb3 = OpLabel
-OpBranch %sel_merge
-%bb4 = OpLabel
-OpBranch %sel_merge
-%sel_merge = OpLabel
-OpBranch %loop_merge
-%cont = OpLabel
-OpBranch %loop_header
-%loop_merge = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
-}
-
-TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop4) {
- // Same as |SelectionMergeWithExitToLoop|, except the branch in the selection
- // construct is an |OpSwitch| instead of an |OpConditionalBranch|. The
- // OpSelectionMerge instruction is not needed in this case either.
- const std::string predefs = R"(
-OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-%void = OpTypeVoid
-%func_type = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%uint = OpTypeInt 32 0
-%undef_int = OpUndef %uint
-)";
-
- const std::string body =
- R"(
-; CHECK: OpLoopMerge [[loop_merge:%\w+]]
-; CHECK-NEXT: OpBranch [[bb1:%\w+]]
-; CHECK: [[bb1]] = OpLabel
-; CHECK-NEXT: OpBranch [[bb2:%\w+]]
-; CHECK: [[bb2]] = OpLabel
-; CHECK-NEXT: OpSelectionMerge
-; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
-; CHECK: [[bb3]] = OpLabel
-; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
-; CHECK: [[sel_merge]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop_merge]]
-; CHECK: [[loop_merge]] = OpLabel
-; CHECK-NEXT: OpReturn
-%main = OpFunction %void None %func_type
-%entry_bb = OpLabel
-OpBranch %loop_header
-%loop_header = OpLabel
-OpLoopMerge %loop_merge %cont None
-OpBranch %bb1
-%bb1 = OpLabel
-OpSelectionMerge %sel_merge None
-OpBranchConditional %true %bb2 %bb4
-%bb2 = OpLabel
-OpSelectionMerge %bb3 None
-OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3
-%bb3 = OpLabel
-OpBranch %sel_merge
-%bb4 = OpLabel
-OpBranch %sel_merge
-%sel_merge = OpLabel
-OpBranch %loop_merge
-%cont = OpLabel
-OpBranch %loop_header
-%loop_merge = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
-}
-
-TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue4) {
- // Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the
- // selection construct is an |OpSwitch| instead of an |OpConditionalBranch|.
- // The OpSelectionMerge instruction is not needed in this case either.
- const std::string predefs = R"(
-OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-%void = OpTypeVoid
-%func_type = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%uint = OpTypeInt 32 0
-%undef_int = OpUndef %uint
-)";
-
- const std::string body =
- R"(
-; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]]
-; CHECK-NEXT: OpBranch [[bb1:%\w+]]
-; CHECK: [[bb1]] = OpLabel
-; CHECK-NEXT: OpBranch [[bb2:%\w+]]
-; CHECK: [[bb2]] = OpLabel
-; CHECK-NEXT: OpSelectionMerge
-; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]]
-; CHECK: [[bb3]] = OpLabel
-; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
-; CHECK: [[sel_merge]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop_merge]]
-; CHECK: [[loop_merge]] = OpLabel
-; CHECK-NEXT: OpReturn
-%main = OpFunction %void None %func_type
-%entry_bb = OpLabel
-OpBranch %loop_header
-%loop_header = OpLabel
-OpLoopMerge %loop_merge %cont None
-OpBranch %bb1
-%bb1 = OpLabel
-OpSelectionMerge %sel_merge None
-OpBranchConditional %true %bb2 %bb4
-%bb2 = OpLabel
-OpSelectionMerge %bb3 None
-OpSwitch %undef_int %bb3 0 %cont 1 %bb3
+OpBranchConditional %undef_bool %sel_merge %bb3
%bb3 = OpLabel
OpBranch %sel_merge
%bb4 = OpLabel
@@ -3036,9 +2913,11 @@
; CHECK-NEXT: OpLoopMerge [[outer_merge:%\w+]] [[outer_cont:%\w+]] None
; CHECK-NEXT: OpBranch [[inner:%\w+]]
; CHECK: [[inner]] = OpLabel
-; CHECK: OpLoopMerge [[outer_cont]] [[inner_cont:%\w+]] None
+; CHECK: OpLoopMerge [[inner_merge:%\w+]] [[inner_cont:%\w+]] None
; CHECK: [[inner_cont]] = OpLabel
; CHECK-NEXT: OpBranch [[inner]]
+; CHECK: [[inner_merge]] = OpLabel
+; CHECK-NEXT: OpUnreachable
; CHECK: [[outer_cont]] = OpLabel
; CHECK-NEXT: OpBranch [[outer]]
; CHECK: [[outer_merge]] = OpLabel
@@ -3058,7 +2937,7 @@
OpLoopMerge %outer_merge %outer_continue None
OpBranch %inner_loop
%inner_loop = OpLabel
-OpLoopMerge %outer_continue %inner_continue None
+OpLoopMerge %inner_merge %inner_continue None
OpBranch %inner_body
%inner_body = OpLabel
OpSelectionMerge %inner_continue None
@@ -3066,7 +2945,9 @@
%ret = OpLabel
OpReturn
%inner_continue = OpLabel
-OpBranchConditional %true %outer_continue %inner_loop
+OpBranchConditional %true %inner_merge %inner_loop
+%inner_merge = OpLabel
+OpBranch %outer_continue
%outer_continue = OpLabel
OpBranchConditional %true %outer_merge %outer_loop
%outer_merge = OpLabel
diff --git a/third_party/SPIRV-Tools/test/opt/def_use_test.cpp b/third_party/SPIRV-Tools/test/opt/def_use_test.cpp
index 48a485e..0210095 100644
--- a/third_party/SPIRV-Tools/test/opt/def_use_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/def_use_test.cpp
@@ -950,369 +950,6 @@
);
// clang-format on
-// We re-use the same replace usecases, we need to similarly exercise the
-// DefUseManager by replacing instructions and uses.
-using CompactIdempotence = ::testing::TestWithParam<ReplaceUseCase>;
-
-TEST_P(CompactIdempotence, Case) {
- const auto& tc = GetParam();
-
- // Build module.
- const std::vector<const char*> text = {tc.before};
- std::unique_ptr<IRContext> context =
- BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text),
- SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- ASSERT_NE(nullptr, context);
-
- // Force a re-build of def-use manager.
- context->InvalidateAnalyses(IRContext::Analysis::kAnalysisDefUse);
- (void)context->get_def_use_mgr();
-
- // Do the substitution.
- for (const auto& candidate : tc.candidates) {
- context->ReplaceAllUsesWith(candidate.first, candidate.second);
- }
-
- // Ensure new/uncompacted managers produce the same result
- EXPECT_TRUE(CompareAndPrintDifferences(*context->get_def_use_mgr(),
- DefUseManager(context->module())));
-
- EXPECT_EQ(tc.after, DisassembleModule(context->module()));
- CheckDef(tc.du, context->get_def_use_mgr()->id_to_defs());
- CheckUse(tc.du, context->get_def_use_mgr(), context->module()->IdBound());
-
- // Compare again after compacting the defuse manager's storage
- context->get_def_use_mgr()->CompactStorage();
-
- CheckDef(tc.du, context->get_def_use_mgr()->id_to_defs());
- CheckUse(tc.du, context->get_def_use_mgr(), context->module()->IdBound());
-
- EXPECT_TRUE(CompareAndPrintDifferences(*context->get_def_use_mgr(),
- DefUseManager(context->module())));
-}
-
-// clang-format off
-INSTANTIATE_TEST_SUITE_P(
- TestCase, CompactIdempotence,
- ::testing::ValuesIn(std::vector<ReplaceUseCase>{
- { // no use, no replace request
- "", {}, "", {},
- },
- { // replace one use
- "%1 = OpTypeBool "
- "%2 = OpTypeVector %1 3 "
- "%3 = OpTypeInt 32 0 ",
- {{1, 3}},
- "%1 = OpTypeBool\n"
- "%2 = OpTypeVector %3 3\n"
- "%3 = OpTypeInt 32 0",
- {
- { // defs
- {1, "%1 = OpTypeBool"},
- {2, "%2 = OpTypeVector %3 3"},
- {3, "%3 = OpTypeInt 32 0"},
- },
- { // uses
- {3, {"%2 = OpTypeVector %3 3"}},
- },
- },
- },
- { // replace and then replace back
- "%1 = OpTypeBool "
- "%2 = OpTypeVector %1 3 "
- "%3 = OpTypeInt 32 0",
- {{1, 3}, {3, 1}},
- "%1 = OpTypeBool\n"
- "%2 = OpTypeVector %1 3\n"
- "%3 = OpTypeInt 32 0",
- {
- { // defs
- {1, "%1 = OpTypeBool"},
- {2, "%2 = OpTypeVector %1 3"},
- {3, "%3 = OpTypeInt 32 0"},
- },
- { // uses
- {1, {"%2 = OpTypeVector %1 3"}},
- },
- },
- },
- { // replace with the same id
- "%1 = OpTypeBool "
- "%2 = OpTypeVector %1 3",
- {{1, 1}, {2, 2}, {3, 3}},
- "%1 = OpTypeBool\n"
- "%2 = OpTypeVector %1 3",
- {
- { // defs
- {1, "%1 = OpTypeBool"},
- {2, "%2 = OpTypeVector %1 3"},
- },
- { // uses
- {1, {"%2 = OpTypeVector %1 3"}},
- },
- },
- },
- { // replace in sequence
- "%1 = OpTypeBool "
- "%2 = OpTypeVector %1 3 "
- "%3 = OpTypeInt 32 0 "
- "%4 = OpTypeInt 32 1 ",
- {{1, 3}, {3, 4}},
- "%1 = OpTypeBool\n"
- "%2 = OpTypeVector %4 3\n"
- "%3 = OpTypeInt 32 0\n"
- "%4 = OpTypeInt 32 1",
- {
- { // defs
- {1, "%1 = OpTypeBool"},
- {2, "%2 = OpTypeVector %4 3"},
- {3, "%3 = OpTypeInt 32 0"},
- {4, "%4 = OpTypeInt 32 1"},
- },
- { // uses
- {4, {"%2 = OpTypeVector %4 3"}},
- },
- },
- },
- { // replace multiple uses
- "%1 = OpTypeBool "
- "%2 = OpTypeVector %1 2 "
- "%3 = OpTypeVector %1 3 "
- "%4 = OpTypeVector %1 4 "
- "%5 = OpTypeMatrix %2 2 "
- "%6 = OpTypeMatrix %3 3 "
- "%7 = OpTypeMatrix %4 4 "
- "%8 = OpTypeInt 32 0 "
- "%9 = OpTypeInt 32 1 "
- "%10 = OpTypeInt 64 0",
- {{1, 8}, {2, 9}, {4, 10}},
- "%1 = OpTypeBool\n"
- "%2 = OpTypeVector %8 2\n"
- "%3 = OpTypeVector %8 3\n"
- "%4 = OpTypeVector %8 4\n"
- "%5 = OpTypeMatrix %9 2\n"
- "%6 = OpTypeMatrix %3 3\n"
- "%7 = OpTypeMatrix %10 4\n"
- "%8 = OpTypeInt 32 0\n"
- "%9 = OpTypeInt 32 1\n"
- "%10 = OpTypeInt 64 0",
- {
- { // defs
- {1, "%1 = OpTypeBool"},
- {2, "%2 = OpTypeVector %8 2"},
- {3, "%3 = OpTypeVector %8 3"},
- {4, "%4 = OpTypeVector %8 4"},
- {5, "%5 = OpTypeMatrix %9 2"},
- {6, "%6 = OpTypeMatrix %3 3"},
- {7, "%7 = OpTypeMatrix %10 4"},
- {8, "%8 = OpTypeInt 32 0"},
- {9, "%9 = OpTypeInt 32 1"},
- {10, "%10 = OpTypeInt 64 0"},
- },
- { // uses
- {8,
- {
- "%2 = OpTypeVector %8 2",
- "%3 = OpTypeVector %8 3",
- "%4 = OpTypeVector %8 4",
- }
- },
- {9, {"%5 = OpTypeMatrix %9 2"}},
- {3, {"%6 = OpTypeMatrix %3 3"}},
- {10, {"%7 = OpTypeMatrix %10 4"}},
- },
- },
- },
- { // OpPhi.
- kOpPhiTestFunction,
- // replace one id used by OpPhi, replace one id generated by OpPhi
- {{9, 13}, {11, 9}},
- "%1 = OpTypeVoid\n"
- "%6 = OpTypeInt 32 0\n"
- "%10 = OpTypeFloat 32\n"
- "%16 = OpTypeBool\n"
- "%3 = OpTypeFunction %1\n"
- "%8 = OpConstant %6 0\n"
- "%18 = OpConstant %6 1\n"
- "%12 = OpConstant %10 1\n"
- "%2 = OpFunction %1 None %3\n"
- "%4 = OpLabel\n"
- "OpBranch %5\n"
-
- "%5 = OpLabel\n"
- "%7 = OpPhi %6 %8 %4 %13 %5\n" // %9 -> %13
- "%11 = OpPhi %10 %12 %4 %13 %5\n"
- "%9 = OpIAdd %6 %7 %8\n"
- "%13 = OpFAdd %10 %9 %12\n" // %11 -> %9
- "%17 = OpSLessThan %16 %7 %18\n"
- "OpLoopMerge %19 %5 None\n"
- "OpBranchConditional %17 %5 %19\n"
-
- "%19 = OpLabel\n"
- "OpReturn\n"
- "OpFunctionEnd",
- {
- { // defs.
- {1, "%1 = OpTypeVoid"},
- {2, "%2 = OpFunction %1 None %3"},
- {3, "%3 = OpTypeFunction %1"},
- {4, "%4 = OpLabel"},
- {5, "%5 = OpLabel"},
- {6, "%6 = OpTypeInt 32 0"},
- {7, "%7 = OpPhi %6 %8 %4 %13 %5"},
- {8, "%8 = OpConstant %6 0"},
- {9, "%9 = OpIAdd %6 %7 %8"},
- {10, "%10 = OpTypeFloat 32"},
- {11, "%11 = OpPhi %10 %12 %4 %13 %5"},
- {12, "%12 = OpConstant %10 1.0"},
- {13, "%13 = OpFAdd %10 %9 %12"},
- {16, "%16 = OpTypeBool"},
- {17, "%17 = OpSLessThan %16 %7 %18"},
- {18, "%18 = OpConstant %6 1"},
- {19, "%19 = OpLabel"},
- },
- { // uses
- {1,
- {
- "%2 = OpFunction %1 None %3",
- "%3 = OpTypeFunction %1",
- }
- },
- {3, {"%2 = OpFunction %1 None %3"}},
- {4,
- {
- "%7 = OpPhi %6 %8 %4 %13 %5",
- "%11 = OpPhi %10 %12 %4 %13 %5",
- }
- },
- {5,
- {
- "OpBranch %5",
- "%7 = OpPhi %6 %8 %4 %13 %5",
- "%11 = OpPhi %10 %12 %4 %13 %5",
- "OpLoopMerge %19 %5 None",
- "OpBranchConditional %17 %5 %19",
- }
- },
- {6,
- {
- // Can't properly check constants
- // "%8 = OpConstant %6 0",
- // "%18 = OpConstant %6 1",
- "%7 = OpPhi %6 %8 %4 %13 %5",
- "%9 = OpIAdd %6 %7 %8"
- }
- },
- {7,
- {
- "%9 = OpIAdd %6 %7 %8",
- "%17 = OpSLessThan %16 %7 %18",
- }
- },
- {8,
- {
- "%7 = OpPhi %6 %8 %4 %13 %5",
- "%9 = OpIAdd %6 %7 %8",
- }
- },
- {9, {"%13 = OpFAdd %10 %9 %12"}}, // uses of %9 changed from %7 to %13
- {10,
- {
- "%11 = OpPhi %10 %12 %4 %13 %5",
- // "%12 = OpConstant %10 1",
- "%13 = OpFAdd %10 %9 %12"
- }
- },
- // no more uses of %11
- {12,
- {
- "%11 = OpPhi %10 %12 %4 %13 %5",
- "%13 = OpFAdd %10 %9 %12"
- }
- },
- {13, {
- "%7 = OpPhi %6 %8 %4 %13 %5",
- "%11 = OpPhi %10 %12 %4 %13 %5",
- }
- },
- {16, {"%17 = OpSLessThan %16 %7 %18"}},
- {17, {"OpBranchConditional %17 %5 %19"}},
- {18, {"%17 = OpSLessThan %16 %7 %18"}},
- {19,
- {
- "OpLoopMerge %19 %5 None",
- "OpBranchConditional %17 %5 %19",
- }
- },
- },
- },
- },
- { // OpPhi defining and referencing the same id.
- "%1 = OpTypeBool "
- "%3 = OpTypeFunction %1 "
- "%2 = OpConstantTrue %1 "
-
- "%4 = OpFunction %3 None %1 "
- "%6 = OpLabel "
- " OpBranch %7 "
- "%7 = OpLabel "
- "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8
- " OpBranch %7 "
- " OpFunctionEnd",
- {{8, 2}},
- "%1 = OpTypeBool\n"
- "%3 = OpTypeFunction %1\n"
- "%2 = OpConstantTrue %1\n"
-
- "%4 = OpFunction %3 None %1\n"
- "%6 = OpLabel\n"
- "OpBranch %7\n"
- "%7 = OpLabel\n"
- "%8 = OpPhi %1 %2 %7 %2 %6\n" // use of %8 changed to %2
- "OpBranch %7\n"
- "OpFunctionEnd",
- {
- { // defs
- {1, "%1 = OpTypeBool"},
- {2, "%2 = OpConstantTrue %1"},
- {3, "%3 = OpTypeFunction %1"},
- {4, "%4 = OpFunction %3 None %1"},
- {6, "%6 = OpLabel"},
- {7, "%7 = OpLabel"},
- {8, "%8 = OpPhi %1 %2 %7 %2 %6"},
- },
- { // uses
- {1,
- {
- "%2 = OpConstantTrue %1",
- "%3 = OpTypeFunction %1",
- "%4 = OpFunction %3 None %1",
- "%8 = OpPhi %1 %2 %7 %2 %6",
- }
- },
- {2,
- {
- // Only checking users
- "%8 = OpPhi %1 %2 %7 %2 %6",
- }
- },
- {3, {"%4 = OpFunction %3 None %1"}},
- {6, {"%8 = OpPhi %1 %2 %7 %2 %6"}},
- {7,
- {
- "OpBranch %7",
- "%8 = OpPhi %1 %2 %7 %2 %6",
- "OpBranch %7",
- }
- },
- // {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
- },
- },
- },
- })
-);
-// clang-format on
-
struct KillDefCase {
const char* before;
std::vector<uint32_t> ids_to_kill;
diff --git a/third_party/SPIRV-Tools/test/opt/fold_test.cpp b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
index 8ee14a9..f6c94ca 100644
--- a/third_party/SPIRV-Tools/test/opt/fold_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
@@ -148,6 +148,8 @@
%v2half = OpTypeVector %half 2
%v2bool = OpTypeVector %bool 2
%m2x2int = OpTypeMatrix %v2int 2
+%mat4v4float = OpTypeMatrix %v4float 4
+%mat4v4double = OpTypeMatrix %v4double 4
%struct_v2int_int_int = OpTypeStruct %v2int %int %int
%_ptr_int = OpTypePointer Function %int
%_ptr_uint = OpTypePointer Function %uint
@@ -276,13 +278,20 @@
%v4float_0_0_0_1 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1
%v4float_0_1_0_0 = OpConstantComposite %v4float %float_0 %float_1 %float_null %float_0
%v4float_1_1_1_1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4float_1_2_3_4 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
+%v4float_null = OpConstantNull %v4float
+%mat4v4float_null = OpConstantComposite %mat4v4float %v4float_null %v4float_null %v4float_null %v4float_null
+%mat4v4float_1_2_3_4 = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4
%107 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
%v4double_0_0_0_0 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
%v4double_0_0_0_1 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_1
%v4double_0_1_0_0 = OpConstantComposite %v4double %double_0 %double_1 %double_null %double_0
%v4double_1_1_1_1 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_1
+%v4double_1_2_3_4 = OpConstantComposite %v4double %double_1 %double_2 %double_3 %double_4
%v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5
%v4double_null = OpConstantNull %v4double
+%mat4v4double_null = OpConstantComposite %mat4v4double %v4double_null %v4double_null %v4double_null %v4double_null
+%mat4v4double_1_2_3_4 = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4
%v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3
%uint_0x3f800000 = OpConstant %uint 0x3f800000
%uint_0xbf800000 = OpConstant %uint 0xbf800000
@@ -912,7 +921,61 @@
"%2 = OpBitcast %v2double %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, {1.0,-127.35})
+ 2, {1.0,-127.35}),
+ // Test case 1: OpVectorTimesMatrix Non-Zero Zero {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {1.0, 2.0, 3.0, 4.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 2: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4double %v4double_null %mat4v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 3: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {30.0,30.0,30.0,30.0}),
+ // Test case 4: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4double %mat4v4double_null %v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 5: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 6: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {10.0,20.0,30.0,40.0})
));
using FloatVectorInstructionFoldingTest =
@@ -981,7 +1044,61 @@
"%2 = OpBitcast %v2float %long_0xbf8000003f800000\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, {1.0f,-1.0f})
+ 2, {1.0f,-1.0f}),
+ // Test case 3: OpVectorTimesMatrix Non-Zero Zero {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {1.0, 2.0, 3.0, 4.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 4: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4float %v4float_null %mat4v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 5: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {30.0f,30.0f,30.0f,30.0f}),
+ // Test case 6: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4float %mat4v4float_null %v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 7: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {10.0f,20.0f,30.0f,40.0f})
));
// clang-format on
using BooleanInstructionFoldingTest =
diff --git a/third_party/SPIRV-Tools/test/opt/if_conversion_test.cpp b/third_party/SPIRV-Tools/test/opt/if_conversion_test.cpp
index 81e9bb2..dc7f831 100644
--- a/third_party/SPIRV-Tools/test/opt/if_conversion_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/if_conversion_test.cpp
@@ -591,6 +591,33 @@
SinglePassRunAndMatch<IfConversion>(text, true);
}
+TEST_F(IfConversionTest, MultipleEdgesFromSameBlock) {
+ // If a block has two out going edges that go to the same block, then there
+ // can be an OpPhi instruction with fewer entries than the number of incoming
+ // edges. This must be handled.
+ const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%true_0 = OpConstantTrue %bool
+%2 = OpFunction %void None %4
+%8 = OpLabel
+OpSelectionMerge %9 None
+OpBranchConditional %true_0 %9 %9
+%9 = OpLabel
+%10 = OpPhi %bool %true %8
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<IfConversion>(text, text, true, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/inline_test.cpp b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
index cefd8e5..d804f7c 100644
--- a/third_party/SPIRV-Tools/test/opt/inline_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
@@ -1533,9 +1533,11 @@
%9 = OpLabel
OpBranch %10
%10 = OpLabel
-OpLoopMerge %12 %10 None
+OpLoopMerge %12 %15 None
OpBranch %14
%14 = OpLabel
+OpBranch %15
+%15 = OpLabel
OpBranchConditional %true %10 %12
%12 = OpLabel
OpReturn
@@ -1694,7 +1696,7 @@
OpBranch %13
%13 = OpLabel
%14 = OpCopyObject %bool %false
-OpLoopMerge %16 %13 None
+OpLoopMerge %16 %22 None
OpBranch %17
%17 = OpLabel
%19 = OpCopyObject %bool %true
@@ -1702,6 +1704,8 @@
OpBranchConditional %true %20 %20
%20 = OpLabel
%21 = OpPhi %bool %19 %17
+OpBranch %22
+%22 = OpLabel
OpBranchConditional %true %13 %16
%16 = OpLabel
OpReturn
diff --git a/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp b/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp
index 4c271de..c8eb6c1 100644
--- a/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp
@@ -8897,6 +8897,7 @@
)";
SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ ValidatorOptions()->uniform_buffer_standard_layout = true;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
false, true, false, true);
diff --git a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
index 2a48134..dd749ab 100644
--- a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
@@ -1525,6 +1525,45 @@
EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
}
+TEST_F(DescriptorTypeTest, GetShader100DebugOpcode) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ %2 = OpString "ps.hlsl"
+ %3 = OpString "#line 1 \"ps.hlsl\""
+ %void = OpTypeVoid
+ %5 = OpExtInst %void %1 DebugExpression
+ %6 = OpExtInst %void %1 DebugSource %2 %3
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+ Instruction* debug_expression = context->get_def_use_mgr()->GetDef(5);
+ EXPECT_EQ(debug_expression->GetShader100DebugOpcode(),
+ NonSemanticShaderDebugInfo100DebugExpression);
+ Instruction* debug_source = context->get_def_use_mgr()->GetDef(6);
+ EXPECT_EQ(debug_source->GetShader100DebugOpcode(),
+ NonSemanticShaderDebugInfo100DebugSource);
+
+ // Test that an opcode larger than the max will return Max. This instruction
+ // cannot be in the assembly above because the assembler expects the string
+ // for the opcode, so we cannot use an arbitrary number. However, a binary
+ // file could have an arbitrary number.
+ std::unique_ptr<Instruction> past_max(debug_expression->Clone(context.get()));
+ const uint32_t kExtInstOpcodeInIndex = 1;
+ uint32_t large_opcode = NonSemanticShaderDebugInfo100InstructionsMax + 2;
+ past_max->SetInOperand(kExtInstOpcodeInIndex, {large_opcode});
+ EXPECT_EQ(past_max->GetShader100DebugOpcode(),
+ NonSemanticShaderDebugInfo100InstructionsMax);
+
+ // Test that an opcode without a value in the enum, but less than Max returns
+ // the same value.
+ uint32_t opcode = NonSemanticShaderDebugInfo100InstructionsMax - 2;
+ past_max->SetInOperand(kExtInstOpcodeInIndex, {opcode});
+ EXPECT_EQ(past_max->GetShader100DebugOpcode(), opcode);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/local_access_chain_convert_test.cpp b/third_party/SPIRV-Tools/test/opt/local_access_chain_convert_test.cpp
index 2b3231c..6f5021c 100644
--- a/third_party/SPIRV-Tools/test/opt/local_access_chain_convert_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_access_chain_convert_test.cpp
@@ -1252,6 +1252,70 @@
true);
}
+TEST_F(LocalAccessChainConvertTest, OutOfBoundsAccess) {
+ // The access chain indexes element 12 in an array of size 10. Nothing should
+ // be done.
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_10 = OpConstant %int 10
+%_arr_int_int_10 = OpTypeArray %int %int_10
+%_ptr_Function_int = OpTypePointer Function %int
+%int_12 = OpConstant %int 12
+%_ptr_Output_int = OpTypePointer Output %int
+%3 = OpVariable %_ptr_Output_int Output
+%_ptr_Function__arr_int_int_10 = OpTypePointer Function %_arr_int_int_10
+%2 = OpFunction %void None %5
+%13 = OpLabel
+%14 = OpVariable %_ptr_Function__arr_int_int_10 Function
+%15 = OpAccessChain %_ptr_Function_int %14 %int_12
+%16 = OpLoad %int %15
+OpStore %3 %16
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false,
+ true);
+}
+
+TEST_F(LocalAccessChainConvertTest, OutOfBoundsAccessAtBoundary) {
+ // The access chain indexes element 10 in an array of size 10. Nothing should
+ // be done.
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_10 = OpConstant %int 10
+%_arr_int_int_10 = OpTypeArray %int %int_10
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Output_int = OpTypePointer Output %int
+%3 = OpVariable %_ptr_Output_int Output
+%_ptr_Function__arr_int_int_10 = OpTypePointer Function %_arr_int_int_10
+%2 = OpFunction %void None %5
+%12 = OpLabel
+%13 = OpVariable %_ptr_Function__arr_int_int_10 Function
+%14 = OpAccessChain %_ptr_Function_int %13 %int_10
+%15 = OpLoad %int %14
+OpStore %3 %15
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false,
+ true);
+}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Assorted vector and matrix types
diff --git a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
index 21960d1..04bd5d9 100644
--- a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
@@ -2231,7 +2231,7 @@
TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) {
// Make sure that the pass can handle a single block that is both a merge and
- // a continue.
+ // a continue. Note that this is invalid SPIR-V.
const std::string text =
R"(
OpCapability Shader
@@ -2265,7 +2265,7 @@
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- auto result = SinglePassRunAndDisassemble<MergeReturnPass>(text, true, true);
+ auto result = SinglePassRunAndDisassemble<MergeReturnPass>(text, true, false);
// Not looking for any particular output. Other tests do that.
// Just want to make sure the check for unreachable blocks does not emit an
diff --git a/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp b/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
index abb5cde..4546750 100644
--- a/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
@@ -498,6 +498,45 @@
SinglePassRunAndMatch<ReduceLoadSize>(test, false, 1.1);
}
+TEST_F(ReduceLoadSizeTest, replace_array_with_spec_constant_size) {
+ const std::string test =
+ R"(
+ OpCapability ClipDistance
+ OpExtension " "
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 " "
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %uint = OpTypeInt 32 0
+ %6 = OpSpecConstant %uint 538976288
+ %_arr_int_6 = OpTypeArray %int %6
+ %_struct_8 = OpTypeStruct %_arr_int_6
+ %_struct_9 = OpTypeStruct %_struct_8
+%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
+; CHECK: [[var:%\w+]] = OpVariable %_ptr_Uniform__struct_9 Uniform
+ %11 = OpVariable %_ptr_Uniform__struct_9 Uniform
+ %int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_int_6 = OpTypePointer Uniform %_arr_int_6
+ %1 = OpFunction %void None %3
+ %14 = OpLabel
+; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform__arr_int_6 [[var]] %int_0 %int_0
+; CHECK: [[new_ac:%\w+]] = OpAccessChain %_ptr_Uniform_int [[ac]] %uint_538976288
+; CHECK: [[ld:%\w+]] = OpLoad %int [[new_ac]]
+; CHECK: %18 = OpIAdd %int [[ld]] [[ld]]
+ %15 = OpAccessChain %_ptr_Uniform__arr_int_6 %11 %int_0 %int_0
+ %16 = OpLoad %_arr_int_6 %15
+ %17 = OpCompositeExtract %int %16 538976288
+ %18 = OpIAdd %int %17 %17
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<ReduceLoadSize>(test, false,
+ kDefaultLoadReductionThreshold);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/scalar_analysis.cpp b/third_party/SPIRV-Tools/test/opt/scalar_analysis.cpp
index 598d8c7..df2aa8f 100644
--- a/third_party/SPIRV-Tools/test/opt/scalar_analysis.cpp
+++ b/third_party/SPIRV-Tools/test/opt/scalar_analysis.cpp
@@ -1202,7 +1202,6 @@
EXPECT_EQ(phis.size(), 2u);
SENode* phi_node_1 = analysis.AnalyzeInstruction(phis[0]);
SENode* phi_node_2 = analysis.AnalyzeInstruction(phis[1]);
- phi_node_1->DumpDot(std::cout, true);
EXPECT_NE(phi_node_1, nullptr);
EXPECT_NE(phi_node_2, nullptr);
diff --git a/third_party/SPIRV-Tools/test/opt/scalar_replacement_test.cpp b/third_party/SPIRV-Tools/test/opt/scalar_replacement_test.cpp
index 7db997d..0c97c80 100644
--- a/third_party/SPIRV-Tools/test/opt/scalar_replacement_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/scalar_replacement_test.cpp
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "source/opt/scalar_replacement_pass.h"
+
#include <string>
#include "gmock/gmock.h"
@@ -23,6 +25,18 @@
namespace opt {
namespace {
+using ScalarReplacementPassName = ::testing::Test;
+
+TEST_F(ScalarReplacementPassName, Default) {
+ auto srp = ScalarReplacementPass();
+ EXPECT_STREQ(srp.name(), "scalar-replacement=100");
+}
+
+TEST_F(ScalarReplacementPassName, Large) {
+ auto srp = ScalarReplacementPass(0xffffffffu);
+ EXPECT_STREQ(srp.name(), "scalar-replacement=4294967295");
+}
+
using ScalarReplacementTest = PassTest<::testing::Test>;
TEST_F(ScalarReplacementTest, SimpleStruct) {
diff --git a/third_party/SPIRV-Tools/test/opt/types_test.cpp b/third_party/SPIRV-Tools/test/opt/types_test.cpp
index 82e4040..552ad97 100644
--- a/third_party/SPIRV-Tools/test/opt/types_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/types_test.cpp
@@ -266,6 +266,67 @@
}
}
+TEST(Types, TestNumberOfComponentsOnArrays) {
+ Float f32(32);
+ EXPECT_EQ(f32.NumberOfComponents(), 0);
+
+ Array array_size_42(
+ &f32, Array::LengthInfo{99u, {Array::LengthInfo::kConstant, 42u}});
+ EXPECT_EQ(array_size_42.NumberOfComponents(), 42);
+
+ Array array_size_0xDEADBEEF00C0FFEE(
+ &f32, Array::LengthInfo{
+ 99u, {Array::LengthInfo::kConstant, 0xC0FFEE, 0xDEADBEEF}});
+ EXPECT_EQ(array_size_0xDEADBEEF00C0FFEE.NumberOfComponents(),
+ 0xDEADBEEF00C0FFEEull);
+
+ Array array_size_unknown(
+ &f32,
+ Array::LengthInfo{99u, {Array::LengthInfo::kConstantWithSpecId, 10}});
+ EXPECT_EQ(array_size_unknown.NumberOfComponents(), UINT64_MAX);
+
+ RuntimeArray runtime_array(&f32);
+ EXPECT_EQ(runtime_array.NumberOfComponents(), UINT64_MAX);
+}
+
+TEST(Types, TestNumberOfComponentsOnVectors) {
+ Float f32(32);
+ EXPECT_EQ(f32.NumberOfComponents(), 0);
+
+ for (uint32_t vector_size = 1; vector_size < 4; ++vector_size) {
+ Vector vector(&f32, vector_size);
+ EXPECT_EQ(vector.NumberOfComponents(), vector_size);
+ }
+}
+
+TEST(Types, TestNumberOfComponentsOnMatrices) {
+ Float f32(32);
+ Vector vector(&f32, 2);
+
+ for (uint32_t number_of_columns = 1; number_of_columns < 4;
+ ++number_of_columns) {
+ Matrix matrix(&vector, number_of_columns);
+ EXPECT_EQ(matrix.NumberOfComponents(), number_of_columns);
+ }
+}
+
+TEST(Types, TestNumberOfComponentsOnStructs) {
+ Float f32(32);
+ Vector vector(&f32, 2);
+
+ Struct empty_struct({});
+ EXPECT_EQ(empty_struct.NumberOfComponents(), 0);
+
+ Struct struct_f32({&f32});
+ EXPECT_EQ(struct_f32.NumberOfComponents(), 1);
+
+ Struct struct_f32_vec({&f32, &vector});
+ EXPECT_EQ(struct_f32_vec.NumberOfComponents(), 2);
+
+ Struct struct_100xf32(std::vector<const Type*>(100, &f32));
+ EXPECT_EQ(struct_100xf32.NumberOfComponents(), 100);
+}
+
TEST(Types, IntSignedness) {
std::vector<bool> signednesses = {true, false, false, true};
std::vector<std::unique_ptr<Integer>> types;
diff --git a/third_party/SPIRV-Tools/test/reduce/structured_loop_to_selection_test.cpp b/third_party/SPIRV-Tools/test/reduce/structured_loop_to_selection_test.cpp
index 0cfcfdf..d203f3e 100644
--- a/third_party/SPIRV-Tools/test/reduce/structured_loop_to_selection_test.cpp
+++ b/third_party/SPIRV-Tools/test/reduce/structured_loop_to_selection_test.cpp
@@ -2957,7 +2957,7 @@
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpBranchConditional %6 %9 %11
+ OpBranch %11
%12 = OpLabel
OpBranch %10
%10 = OpLabel
@@ -2999,7 +2999,7 @@
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpBranchConditional %6 %9 %11
+ OpBranch %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
@@ -3036,7 +3036,7 @@
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
- OpBranchConditional %6 %9 %11
+ OpBranch %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
@@ -3050,8 +3050,7 @@
TEST(StructuredLoopToSelectionReductionPassTest,
UnreachableInnerLoopContinueBranchingToOuterLoopMerge2) {
- // In this test, the branch to the outer loop merge from the inner loop's
- // continue is part of a structured selection.
+ // In this test, the unreachable continue is composed of multiple blocks.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -3073,8 +3072,7 @@
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %6 %9 %14
+ OpBranch %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
@@ -3118,8 +3116,7 @@
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %6 %9 %14
+ OpBranch %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
@@ -3158,8 +3155,7 @@
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %6 %9 %14
+ OpBranch %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
diff --git a/third_party/SPIRV-Tools/test/text_advance_test.cpp b/third_party/SPIRV-Tools/test/text_advance_test.cpp
index 9de77a8..0d23ab1 100644
--- a/third_party/SPIRV-Tools/test/text_advance_test.cpp
+++ b/third_party/SPIRV-Tools/test/text_advance_test.cpp
@@ -130,5 +130,14 @@
EXPECT_EQ(2u, pos.line);
EXPECT_EQ(4u, pos.index);
}
+
+TEST(TextAdvance, HandleLotsOfWhitespace) {
+ std::string lots_of_spaces(10000, ' ');
+ lots_of_spaces += "Word";
+ const auto pos = PositionAfterAdvance(lots_of_spaces.c_str());
+ EXPECT_EQ(10000u, pos.column);
+ EXPECT_EQ(0u, pos.line);
+ EXPECT_EQ(10000u, pos.index);
+}
} // namespace
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/util/CMakeLists.txt b/third_party/SPIRV-Tools/test/util/CMakeLists.txt
index 6808783..20038f7 100644
--- a/third_party/SPIRV-Tools/test/util/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/util/CMakeLists.txt
@@ -17,7 +17,6 @@
bit_vector_test.cpp
bitutils_test.cpp
hash_combine_test.cpp
- pooled_linked_list_test.cpp
small_vector_test.cpp
LIBS SPIRV-Tools-opt
)
diff --git a/third_party/SPIRV-Tools/test/util/pooled_linked_list_test.cpp b/third_party/SPIRV-Tools/test/util/pooled_linked_list_test.cpp
deleted file mode 100644
index 82fb4ac..0000000
--- a/third_party/SPIRV-Tools/test/util/pooled_linked_list_test.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (c) 2021 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.
-
-#include <algorithm>
-#include <list>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "source/util/pooled_linked_list.h"
-
-namespace spvtools {
-namespace utils {
-namespace {
-
-using PooledLinkedListTest = ::testing::Test;
-
-template <typename T>
-static std::vector<T> ToVector(const PooledLinkedList<T>& list) {
- std::vector<T> vec;
- for (auto it = list.begin(); it != list.end(); ++it) {
- vec.push_back(*it);
- }
- return vec;
-}
-
-template <typename T>
-static void AppendVector(PooledLinkedList<T>& list, const std::vector<T>& vec) {
- for (const T& t : vec) {
- list.push_back(t);
- }
-}
-
-TEST(PooledLinkedListTest, Empty) {
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
- EXPECT_TRUE(ll.empty());
-
- ll.push_back(1u);
- EXPECT_TRUE(!ll.empty());
-}
-
-TEST(PooledLinkedListTest, Iterator) {
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
-
- EXPECT_EQ(ll.begin(), ll.end());
-
- ll.push_back(1);
- EXPECT_NE(ll.begin(), ll.end());
-
- auto it = ll.begin();
- EXPECT_EQ(*it, 1);
- ++it;
- EXPECT_EQ(it, ll.end());
-}
-
-TEST(PooledLinkedListTest, Iterator_algorithms) {
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
-
- AppendVector(ll, {3, 2, 0, 1});
- EXPECT_EQ(std::distance(ll.begin(), ll.end()), 4);
- EXPECT_EQ(*std::min_element(ll.begin(), ll.end()), 0);
- EXPECT_EQ(*std::max_element(ll.begin(), ll.end()), 3);
-}
-
-TEST(PooledLinkedListTest, FrontBack) {
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
-
- ll.push_back(1);
- EXPECT_EQ(ll.front(), 1);
- EXPECT_EQ(ll.back(), 1);
-
- ll.push_back(2);
- EXPECT_EQ(ll.front(), 1);
- EXPECT_EQ(ll.back(), 2);
-}
-
-TEST(PooledLinkedListTest, PushBack) {
- const std::vector<uint32_t> vec = {1, 2, 3, 4, 5, 6};
-
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
-
- AppendVector(ll, vec);
- EXPECT_EQ(vec, ToVector(ll));
-}
-
-TEST(PooledLinkedListTest, RemoveFirst) {
- const std::vector<uint32_t> vec = {1, 2, 3, 4, 5, 6};
-
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
-
- EXPECT_FALSE(ll.remove_first(0));
- AppendVector(ll, vec);
- EXPECT_FALSE(ll.remove_first(0));
-
- std::vector<uint32_t> tmp = vec;
- while (!tmp.empty()) {
- size_t mid = tmp.size() / 2;
- uint32_t elt = tmp[mid];
- tmp.erase(tmp.begin() + mid);
-
- EXPECT_TRUE(ll.remove_first(elt));
- EXPECT_FALSE(ll.remove_first(elt));
- EXPECT_EQ(tmp, ToVector(ll));
- }
- EXPECT_TRUE(ll.empty());
-}
-
-TEST(PooledLinkedListTest, RemoveFirst_Duplicates) {
- const std::vector<uint32_t> vec = {3, 1, 2, 3, 3, 3, 3, 4, 3, 5, 3, 6, 3};
-
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll(&pool);
- AppendVector(ll, vec);
-
- std::vector<uint32_t> tmp = vec;
- while (!tmp.empty()) {
- size_t mid = tmp.size() / 2;
- uint32_t elt = tmp[mid];
- tmp.erase(std::find(tmp.begin(), tmp.end(), elt));
-
- EXPECT_TRUE(ll.remove_first(elt));
- EXPECT_EQ(tmp, ToVector(ll));
- }
- EXPECT_TRUE(ll.empty());
-}
-
-TEST(PooledLinkedList, MoveTo) {
- const std::vector<uint32_t> vec = {1, 2, 3, 4, 5, 6};
-
- PooledLinkedListNodes<uint32_t> pool;
- PooledLinkedList<uint32_t> ll1(&pool);
- PooledLinkedList<uint32_t> ll2(&pool);
- PooledLinkedList<uint32_t> ll3(&pool);
-
- AppendVector(ll1, vec);
- AppendVector(ll2, vec);
- AppendVector(ll3, vec);
- EXPECT_EQ(pool.total_nodes(), vec.size() * 3);
- EXPECT_EQ(pool.total_nodes(), vec.size() * 3);
- EXPECT_EQ(pool.free_nodes(), 0);
-
- // Move two lists to the new pool
- PooledLinkedListNodes<uint32_t> pool_new;
- ll1.move_nodes(&pool_new);
- ll2.move_nodes(&pool_new);
-
- // Moved nodes should belong to new pool
- EXPECT_EQ(ll1.pool(), &pool_new);
- EXPECT_EQ(ll2.pool(), &pool_new);
-
- // Old pool should be smaller & have free nodes.
- EXPECT_EQ(pool.used_nodes(), vec.size());
- EXPECT_EQ(pool.free_nodes(), vec.size() * 2);
-
- // New pool should be sized exactly and no free nodes.
- EXPECT_EQ(pool_new.total_nodes(), vec.size() * 2);
- EXPECT_EQ(pool_new.used_nodes(), vec.size() * 2);
- EXPECT_EQ(pool_new.free_nodes(), 0);
-
- // All lists should be preserved
- EXPECT_EQ(ToVector(ll1), vec);
- EXPECT_EQ(ToVector(ll2), vec);
- EXPECT_EQ(ToVector(ll3), vec);
-}
-
-} // namespace
-} // namespace utils
-} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/CMakeLists.txt b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
index 65f2791..d02807a 100644
--- a/third_party/SPIRV-Tools/test/val/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
@@ -88,8 +88,9 @@
PCH_FILE pch_test_val
)
-add_spvtools_unittest(TARGET val_stuvw
+add_spvtools_unittest(TARGET val_rstuvw
SRCS
+ val_ray_query.cpp
val_small_type_uses_test.cpp
val_ssa_test.cpp
val_state_test.cpp
diff --git a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
index 1178ca0..f27e467 100644
--- a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
@@ -359,12 +359,25 @@
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- AnyVUID("VUID-StandaloneSpirv-None-04638"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is "
- "limited to Device, Workgroup and Invocation"));
+ HasSubstr(
+ "ControlBarrier: in Vulkan 1.0 environment Memory Scope is can not "
+ "be Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared"));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroupVoteKHR) {
+ const std::string capabilities = R"(
+OpCapability SubgroupVoteKHR
+OpExtension "SPV_KHR_subgroup_vote"
+)";
+ const std::string body = R"(
+OpControlBarrier %subgroup %subgroup %none
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, capabilities),
+ SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeSubgroup) {
@@ -386,8 +399,9 @@
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-04638"));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("ControlBarrier: in Vulkan environment, Memory Scope "
- "cannot be CrossDevice"));
+ HasSubstr("ControlBarrier: in Vulkan environment Memory Scope is "
+ "limited to Device, QueueFamily, Workgroup, "
+ "ShaderCallKHR, Subgroup, or Invocation"));
}
TEST_F(ValidateBarriers,
@@ -751,12 +765,11 @@
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- AnyVUID("VUID-StandaloneSpirv-None-04638"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is "
- "limited to Device, Workgroup and Invocation"));
+ HasSubstr(
+ "MemoryBarrier: in Vulkan 1.0 environment Memory Scope is can not be "
+ "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkan1p1MemoryScopeSubgroup) {
diff --git a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
index b76c163..2cbe9a8 100644
--- a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
@@ -394,6 +394,11 @@
generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
+ if ((0 == std::strcmp(storage_class, "Input")) &&
+ (0 == std::strcmp(execution_model, "Fragment"))) {
+ // ensure any needed input types that might require Flat
+ generator.before_types_ += "OpDecorate %built_in_var Flat\n";
+ }
std::ostringstream after_types;
if (InitializerRequired(storage_class)) {
diff --git a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
index 7647746..ede51a9 100644
--- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -637,41 +637,6 @@
" %Main = OpFunction %void None %9\n"));
}
-TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) {
- bool is_shader = GetParam() == SpvCapabilityShader;
- Block entry("entry");
- Block head("head", SpvOpBranchConditional);
- Block f("f");
- Block merge("merge", SpvOpReturn);
-
- head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
-
- if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n");
-
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("head", "merge", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
-
- str += entry >> merge;
- str += head >> std::vector<Block>({merge, f});
- str += f >> merge;
- str += merge;
- str += "OpFunctionEnd\n";
-
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("The selection construct with the selection header "
- ".\\[%head\\] does not dominate the merge block "
- ".\\[%merge\\]\n %merge = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
-}
-
TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
// If a merge block is reachable, then it must be strictly dominated by
// its header block.
@@ -698,7 +663,8 @@
EXPECT_THAT(
getDiagnosticString(),
MatchesRegex("The selection construct with the selection header "
- ".\\[%head\\] does not strictly dominate the merge block "
+ ".\\[%head\\] does not strictly structurally dominate the "
+ "merge block "
".\\[%head\\]\n %head = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
@@ -907,16 +873,7 @@
TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam()));
- if (GetParam() == SpvCapabilityShader) {
- // Shader causes additional structured CFG checks that cause a failure.
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Back-edges (1[%branch] -> 3[%target]) can only be "
- "formed between a block and a loop header."));
-
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) {
@@ -1070,12 +1027,10 @@
std::string header = GetDefaultHeader(cap);
Block entry("entry");
- Block foo("foo", SpvOpBranch);
Block branch("branch", SpvOpBranch);
Block merge("merge", SpvOpReturn);
Block target("target", SpvOpBranch);
- foo >> target;
target >> branch;
entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
@@ -1092,7 +1047,6 @@
str += branch >> std::vector<Block>({merge});
str += merge;
str += target;
- str += foo;
str += "OpFunctionEnd\n";
return str;
@@ -1156,6 +1110,7 @@
Block body("body", SpvOpBranchConditional);
Block t("t", SpvOpReturn);
Block f("f", SpvOpReturn);
+ Block pre_target("pre_target", SpvOpBranch);
target >> branch;
body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
@@ -1163,10 +1118,10 @@
std::string str = header;
if (cap == SpvCapabilityShader) {
branch.AppendBody("OpLoopMerge %merge %target None\n");
- body.AppendBody("OpSelectionMerge %target None\n");
+ body.AppendBody("OpSelectionMerge %pre_target None\n");
}
- str += nameOps("branch", "merge", "target", "body", "t", "f",
+ str += nameOps("branch", "merge", "pre_target", "target", "body", "t", "f",
std::make_pair("func", "Main"));
str += types_consts();
str += "%func = OpFunction %voidt None %funct\n";
@@ -1176,6 +1131,7 @@
str += t;
str += f;
str += merge;
+ str += pre_target >> target;
str += target;
str += "OpFunctionEnd\n";
@@ -1296,9 +1252,10 @@
loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
}
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("loop2", "loop2_merge") + types_consts() +
- "%func = OpFunction %voidt None %funct\n";
+ std::string str =
+ GetDefaultHeader(GetParam()) +
+ nameOps("loop1", "loop1_cont_break_block", "loop2", "loop2_merge") +
+ types_consts() + "%func = OpFunction %voidt None %funct\n";
str += entry >> loop1;
str += loop1 >> loop1_cont_break_block;
@@ -1389,11 +1346,13 @@
CompileSuccessfully(str);
if (GetParam() == SpvCapabilityShader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%loop1_cont\\] is not post dominated by the "
- "back-edge block .\\[%be_block\\]\n"
- " %be_block = OpLabel\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ MatchesRegex(
+ "The continue construct with the continue target "
+ ".\\[%loop1_cont\\] is not structurally post dominated by the "
+ "back-edge block .\\[%be_block\\]\n"
+ " %be_block = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
@@ -1529,10 +1488,11 @@
if (is_shader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%cheader\\] is not post dominated by the "
- "back-edge block .\\[%be_block\\]\n"
- " %be_block = OpLabel\n"));
+ MatchesRegex(
+ "The continue construct with the continue target "
+ ".\\[%cheader\\] is not structurally post dominated by the "
+ "back-edge block .\\[%be_block\\]\n"
+ " %be_block = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
@@ -1561,11 +1521,12 @@
CompileSuccessfully(str);
if (is_shader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%loop\\] is not post dominated by the "
- "back-edge block .\\[%cont\\]\n"
- " %cont = OpLabel\n"))
+ EXPECT_THAT(
+ getDiagnosticString(),
+ MatchesRegex("The continue construct with the continue target "
+ ".\\[%loop\\] is not structurally post dominated by the "
+ "back-edge block .\\[%cont\\]\n"
+ " %cont = OpLabel\n"))
<< str;
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -1597,11 +1558,12 @@
CompileSuccessfully(str);
if (is_shader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%loop\\] is not post dominated by the "
- "back-edge block .\\[%cont\\]\n"
- " %cont = OpLabel\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ MatchesRegex("The continue construct with the continue target "
+ ".\\[%loop\\] is not structurally post dominated by the "
+ "back-edge block .\\[%cont\\]\n"
+ " %cont = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
@@ -1824,40 +1786,6 @@
<< str << getDiagnosticString();
}
-TEST_P(ValidateCFG, SingleLatchBlockHeaderContinueTargetIsItselfGood) {
- // This test case ensures we don't count a Continue Target from a loop
- // header to itself as a self-loop when computing back edges.
- // Also, it detects that there is an edge from %latch to the pseudo-exit
- // node, rather than from %loop. In particular, it detects that we
- // have used the *reverse* textual order of blocks when computing
- // predecessor traversal roots.
- bool is_shader = GetParam() == SpvCapabilityShader;
- Block entry("entry");
- Block loop("loop");
- Block latch("latch");
- Block merge("merge", SpvOpReturn);
-
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop.SetBody("OpLoopMerge %merge %loop None\n");
- }
-
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "loop", "latch", "merge") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
-
- str += entry >> loop;
- str += loop >> latch;
- str += latch >> loop;
- str += merge;
- str += "OpFunctionEnd";
-
- CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions())
- << str << getDiagnosticString();
-}
-
// Unit test to check the case where a basic block is the entry block of 2
// different constructs. In this case, the basic block is the entry block of a
// continue construct as well as a selection construct. See issue# 517 for more
@@ -2872,8 +2800,8 @@
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> 9 branches to the loop construct, but not "
- "to the loop header <ID> 7"));
+ HasSubstr("Back-edges (10[%10] -> 9[%9]) can only be formed "
+ "between a block and a loop header"));
}
TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) {
@@ -3275,9 +3203,10 @@
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> 13[%13] exits the selection headed by <ID> "
- "9[%9], but not via a structured exit"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("The continue construct with the continue target 9[%9] is not "
+ "structurally post dominated by the back-edge block 13[%13]"));
}
TEST_F(ValidateCFG, BreakFromSwitch) {
@@ -4285,9 +4214,11 @@
CompileSuccessfully(text);
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("The selection construct with the selection header "
- "8[%8] does not dominate the merge block 10[%10]\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The selection construct with the selection header "
+ "8[%8] does not structurally dominate the merge block 10[%10]\n"));
}
TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
@@ -4624,6 +4555,36 @@
"must be different labels"));
}
+TEST_F(ValidateCFG, BadBackEdgeUnreachableContinue) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%4 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpLoopMerge %6 %7 None
+OpBranch %8
+%8 = OpLabel
+OpBranch %5
+%7 = OpLabel
+OpUnreachable
+%6 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("The continue construct with the continue target 7[%7] "
+ "does not structurally dominate the back-edge block 8[%8]"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_conversion_test.cpp b/third_party/SPIRV-Tools/test/val/val_conversion_test.cpp
index 94bd27d..f6f37b3 100644
--- a/third_party/SPIRV-Tools/test/val/val_conversion_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_conversion_test.cpp
@@ -1641,6 +1641,131 @@
"integer type to have a 64-bit width for Vulkan environment."));
}
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureU32Vec2) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u32vec2ptr_func = OpTypePointer Function %u32vec2
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u32vec2ptr_func Function
+%load = OpLoad %u32vec2 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureSuccessU64) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u64_func = OpTypePointer Function %u64
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u64_func Function
+%load = OpLoad %u64 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureResult) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u32vec2ptr_func = OpTypePointer Function %u32vec2
+%typeRQ = OpTypeRayQueryKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u32vec2ptr_func Function
+%load = OpLoad %u32vec2 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeRQ %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected Result Type to be a Acceleration Structure"));
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureU32) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u32ptr_func = OpTypePointer Function %u32
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u32ptr_func Function
+%load = OpLoad %u32 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit "
+ "uint vector as input"));
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureS64) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%s64ptr_func = OpTypePointer Function %s64
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %s64ptr_func Function
+%load = OpLoad %s64 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit "
+ "uint vector as input"));
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureS32Vec2) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%s32vec2ptr_func = OpTypePointer Function %s32vec2
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %s32vec2ptr_func Function
+%load = OpLoad %s32vec2 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit "
+ "uint vector as input"));
+}
+
using ValidateSmallConversions = spvtest::ValidateBase<std::string>;
CodeGenerator GetSmallConversionsCodeGenerator() {
diff --git a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
index e7ecb61..77526bf 100644
--- a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
@@ -42,6 +42,7 @@
};
using ValidateDecorations = spvtest::ValidateBase<bool>;
+using ValidateDecorationString = spvtest::ValidateBase<std::string>;
using ValidateVulkanCombineDecorationResult =
spvtest::ValidateBase<std::tuple<const char*, const char*, TestResult>>;
@@ -50,20 +51,20 @@
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
- OpDecorate %1 ArrayStride 4
- OpDecorate %1 RelaxedPrecision
+ OpDecorate %1 Location 4
+ OpDecorate %1 Centroid
%2 = OpTypeFloat 32
- %1 = OpTypeRuntimeArray %2
+ %3 = OpTypePointer Output %2
+ %1 = OpVariable %3 Output
; Since %1 is used first in Decoration, it gets id 1.
)";
const uint32_t id = 1;
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
// Must have 2 decorations.
- EXPECT_THAT(
- vstate_->id_decorations(id),
- Eq(std::vector<Decoration>{Decoration(SpvDecorationArrayStride, {4}),
- Decoration(SpvDecorationRelaxedPrecision)}));
+ EXPECT_THAT(vstate_->id_decorations(id),
+ Eq(std::set<Decoration>{Decoration(SpvDecorationLocation, {4}),
+ Decoration(SpvDecorationCentroid)}));
}
TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) {
@@ -88,15 +89,15 @@
const uint32_t arr_id = 1;
EXPECT_THAT(
vstate_->id_decorations(arr_id),
- Eq(std::vector<Decoration>{Decoration(SpvDecorationArrayStride, {4})}));
+ Eq(std::set<Decoration>{Decoration(SpvDecorationArrayStride, {4})}));
// The struct must have 3 decorations.
const uint32_t struct_id = 2;
EXPECT_THAT(
vstate_->id_decorations(struct_id),
- Eq(std::vector<Decoration>{Decoration(SpvDecorationNonReadable, {}, 2),
- Decoration(SpvDecorationOffset, {2}, 2),
- Decoration(SpvDecorationBufferBlock)}));
+ Eq(std::set<Decoration>{Decoration(SpvDecorationNonReadable, {}, 2),
+ Decoration(SpvDecorationOffset, {2}, 2),
+ Decoration(SpvDecorationBufferBlock)}));
}
TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) {
@@ -151,9 +152,9 @@
// Decoration group has 3 decorations.
auto expected_decorations =
- std::vector<Decoration>{Decoration(SpvDecorationDescriptorSet, {0}),
- Decoration(SpvDecorationRelaxedPrecision),
- Decoration(SpvDecorationRestrict)};
+ std::set<Decoration>{Decoration(SpvDecorationDescriptorSet, {0}),
+ Decoration(SpvDecorationRelaxedPrecision),
+ Decoration(SpvDecorationRestrict)};
// Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is
// the decoration group id) also has all the decorations.
@@ -181,7 +182,7 @@
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
// Decoration group has 1 decoration.
auto expected_decorations =
- std::vector<Decoration>{Decoration(SpvDecorationOffset, {3}, 3)};
+ std::set<Decoration>{Decoration(SpvDecorationOffset, {3}, 3)};
// Decoration group is applied to id 2, 3, and 4.
EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations));
@@ -8022,6 +8023,7 @@
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8058,6 +8060,7 @@
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8093,6 +8096,7 @@
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8129,6 +8133,7 @@
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 RowMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8363,6 +8368,642 @@
HasSubstr("PerVertexKHR must be declared as arrays"));
}
+TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnNumericTypeBad) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %float RelaxedPrecision
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("RelaxPrecision decoration cannot be applied to a type"));
+}
+
+TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnStructMember) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpMemberDecorate %struct 0 RelaxedPrecision
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %struct = OpTypeStruct %float
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
+TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceGood) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Geometry
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %layer %gl_Layer
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %layer Location 0
+ OpDecorate %gl_Layer Flat
+ OpDecorate %gl_Layer BuiltIn Layer
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %layer = OpVariable %_ptr_Output_int Output
+%_ptr_Input_int = OpTypePointer Input %int
+ %gl_Layer = OpVariable %_ptr_Input_int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %int %gl_Layer
+ OpStore %layer %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceBad) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Geometry
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %layer %gl_Layer
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %layer Location 0
+ OpDecorate %gl_Layer BuiltIn Layer
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %layer = OpVariable %_ptr_Output_int Output
+%_ptr_Input_int = OpTypePointer Input %int
+ %gl_Layer = OpVariable %_ptr_Input_int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %int %gl_Layer
+ OpStore %layer %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-04744"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Fragment OpEntryPoint operand 4 with Input interfaces with integer "
+ "or float type must have a Flat decoration for Entry Point id 2."));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatFloat32) {
+ std::string spirv = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Input_float = OpTypePointer Input %float
+ %in = OpVariable %_ptr_Input_float Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_float Function
+ %11 = OpLoad %float %in
+ OpStore %b %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatFloat64) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Float64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %double = OpTypeFloat 64
+%_ptr_Function_double = OpTypePointer Function %double
+%_ptr_Input_double = OpTypePointer Input %double
+ %in = OpVariable %_ptr_Input_double Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_double Function
+ %11 = OpLoad %double %in
+ OpStore %b %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-04744"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Fragment OpEntryPoint operand 3 with Input interfaces with integer "
+ "or float type must have a Flat decoration for Entry Point id 2."));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatVectorFloat64) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Float64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %double = OpTypeFloat 64
+ %v2double = OpTypeVector %double 2
+%_ptr_Function_v2double = OpTypePointer Function %v2double
+%_ptr_Input_v2double = OpTypePointer Input %v2double
+ %in = OpVariable %_ptr_Input_v2double Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_v2double Function
+ %11 = OpLoad %v2double %in
+ OpStore %b %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatIntVector) {
+ std::string spirv = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %v2int = OpTypeVector %int 2
+%_ptr_Function_v2int = OpTypePointer Function %v2int
+%_ptr_Input_v2int = OpTypePointer Input %v2int
+ %in = OpVariable %_ptr_Input_v2int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_v2int Function
+ %12 = OpLoad %v2int %in
+ OpStore %b %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-04744"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Fragment OpEntryPoint operand 3 with Input interfaces with integer "
+ "or float type must have a Flat decoration for Entry Point id 2."));
+}
+
+TEST_P(ValidateDecorationString, VulkanOutputInvalidInterface) {
+ const std::string decoration = GetParam();
+ std::stringstream ss;
+ ss << R"(
+ OpCapability Shader
+ OpCapability SampleRateShading
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %out
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %out )"
+ << decoration << R"(
+ OpDecorate %out Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %out = OpVariable %_ptr_Output_int Output
+ %int_1 = OpConstant %int 1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpStore %out %int_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-06201"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "OpEntryPoint interfaces variable must not be fragment execution "
+ "model with an output storage class for Entry Point id 2."));
+}
+
+TEST_P(ValidateDecorationString, VulkanVertexInputInvalidInterface) {
+ const std::string decoration = GetParam();
+ std::stringstream ss;
+ ss << R"(
+ OpCapability Shader
+ OpCapability SampleRateShading
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %out %in
+ OpSource GLSL 450
+ OpDecorate %in )"
+ << decoration << R"(
+ OpDecorate %out Location 0
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %out = OpVariable %_ptr_Output_int Output
+%_ptr_Input_int = OpTypePointer Input %int
+ %in = OpVariable %_ptr_Input_int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %int %in
+ OpStore %out %11
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-06202"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpEntryPoint interfaces variable must not be vertex execution "
+ "model with an input storage class for Entry Point id 2."));
+}
+
+INSTANTIATE_TEST_SUITE_P(FragmentInputInterface, ValidateDecorationString,
+ ::testing::Values("Flat", "NoPerspective", "Sample",
+ "Centroid"));
+
+TEST_F(ValidateDecorations, NVBindlessSamplerArrayInBlock) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %UBO "UBO"
+ OpMemberName %UBO 0 "uboSampler"
+ OpName %_ ""
+ OpDecorate %array ArrayStride 16
+ OpMemberDecorate %UBO 0 Offset 0
+ OpDecorate %UBO Block
+ OpDecorate %_ DescriptorSet 0
+ OpDecorate %_ Binding 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+ %array = OpTypeArray %8 %uint_3
+ %UBO = OpTypeStruct %array
+ %pointer = OpTypePointer Uniform %UBO
+ %_ = OpVariable %pointer Uniform
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateDecorations, Std140ColMajorMat2x2) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, Std140RowMajorMat2x2) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 RowMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, Std140ColMajorMat4x2) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 4
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, Std140ColMajorMat2x3) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 12
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float3 = OpTypeVector %float 3
+%matrix = OpTypeMatrix %float3 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("member 0 is a matrix with stride 12 not satisfying "
+ "alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, MatrixMissingMajornessUniform) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateDecorations, MatrixMissingMajornessStorageBuffer) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr_block StorageBuffer
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateDecorations, MatrixMissingMajornessPushConstant) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer PushConstant %block
+%var = OpVariable %ptr_block PushConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateDecorations, StructWithRowAndColMajor) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 1 Offset 32
+OpMemberDecorate %block 1 MatrixStride 16
+OpMemberDecorate %block 1 RowMajor
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix %matrix
+%ptr_block = OpTypePointer PushConstant %block
+%var = OpVariable %ptr_block PushConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_id_test.cpp b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
index 69257a5..b7e5042 100644
--- a/third_party/SPIRV-Tools/test/val/val_id_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
@@ -6570,6 +6570,35 @@
"Operand 3[%_ptr_Uniform__struct_2] requires a previous definition"));
}
+TEST_F(ValidateIdWithMessage, NVBindlessSamplerInStruct) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+ %9 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+ %10 = OpTypeSampler
+ %UBO = OpTypeStruct %8 %9 %10
+%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
+ %_ = OpVariable %_ptr_Uniform_UBO Uniform
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_image_test.cpp b/third_party/SPIRV-Tools/test/val/val_image_test.cpp
index 76af29c..c4de60a 100644
--- a/third_party/SPIRV-Tools/test/val/val_image_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_image_test.cpp
@@ -6248,6 +6248,138 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
}
+TEST_F(ValidateImage, NVBindlessSamplerBuiltins) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability Int64
+ OpCapability Image1D
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %s2D "s2D"
+ OpName %textureHandle "textureHandle"
+ OpName %i1D "i1D"
+ OpName %s "s"
+ OpName %temp "temp"
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+%_ptr_Function_8 = OpTypePointer Function %8
+ %ulong = OpTypeInt 64 0
+%_ptr_Private_ulong = OpTypePointer Private %ulong
+%textureHandle = OpVariable %_ptr_Private_ulong Private
+ %16 = OpTypeImage %float 1D 0 0 0 2 Rgba32f
+%_ptr_Function_16 = OpTypePointer Function %16
+ %21 = OpTypeSampler
+%_ptr_Function_21 = OpTypePointer Function %21
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %s2D = OpVariable %_ptr_Function_8 Function
+ %i1D = OpVariable %_ptr_Function_16 Function
+ %s = OpVariable %_ptr_Function_21 Function
+ %temp = OpVariable %_ptr_Function_ulong Function
+ %14 = OpLoad %ulong %textureHandle
+ %15 = OpConvertUToSampledImageNV %8 %14
+ OpStore %s2D %15
+ %19 = OpLoad %ulong %textureHandle
+ %20 = OpConvertUToImageNV %16 %19
+ OpStore %i1D %20
+ %24 = OpLoad %ulong %textureHandle
+ %25 = OpConvertUToSamplerNV %21 %24
+ OpStore %s %25
+ %28 = OpLoad %8 %s2D
+ %29 = OpConvertSampledImageToUNV %ulong %28
+ OpStore %temp %29
+ %30 = OpLoad %16 %i1D
+ %31 = OpConvertImageToUNV %ulong %30
+ OpStore %temp %31
+ %32 = OpLoad %21 %s
+ %33 = OpConvertSamplerToUNV %ulong %32
+ OpStore %temp %33
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateImage, NVBindlessAddressingMode64) {
+ std::string text = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateImage, NVBindlessAddressingMode32) {
+ std::string text = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 32
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateImage, NVBindlessInvalidAddressingMode) {
+ std::string text = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 0
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpSamplerImageAddressingModeNV bitwidth should be 64 or 32"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp b/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
index bec8d02..d9c3748 100644
--- a/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
@@ -711,7 +711,9 @@
OpEntryPoint Fragment %main "main" %var1 %var2
OpExecutionMode %main OriginUpperLeft
OpDecorate %var1 Location 0
+OpDecorate %var1 Flat
OpDecorate %var2 Location 1
+OpDecorate %var2 Flat
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%float = OpTypeFloat 32
diff --git a/third_party/SPIRV-Tools/test/val/val_layout_test.cpp b/third_party/SPIRV-Tools/test/val/val_layout_test.cpp
index 7ebd7c0..8cca96f 100644
--- a/third_party/SPIRV-Tools/test/val/val_layout_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_layout_test.cpp
@@ -667,6 +667,98 @@
// TODO(umar): Test optional instructions
+TEST_F(ValidateLayout, ValidNVBindlessTexturelayout) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateLayout, InvalidValidNVBindlessTexturelayout) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %func "main"
+ OpSamplerImageAddressingModeNV 64
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "SamplerImageAddressingModeNV is in an invalid layout section"));
+}
+
+TEST_F(ValidateLayout, MissingNVBindlessAddressModeFromLayout) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Missing required OpSamplerImageAddressingModeNV instruction"));
+}
+
+TEST_F(ValidateLayout, NVBindlessAddressModeFromLayoutSpecifiedTwice) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpSamplerImageAddressingModeNV 64
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpSamplerImageAddressingModeNV should only be provided once"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_logicals_test.cpp b/third_party/SPIRV-Tools/test/val/val_logicals_test.cpp
index 1b76c85..c140672 100644
--- a/third_party/SPIRV-Tools/test/val/val_logicals_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_logicals_test.cpp
@@ -1159,6 +1159,61 @@
"condition to be equal: Select"));
}
+TEST_F(ValidateLogicals, SelectNVBindlessSamplers) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpSourceExtension "GL_NV_bindless_texture"
+ OpName %main "main"
+ OpName %s2D "s2D"
+ OpName %pickhandle "pickhandle"
+ OpName %Sampler1 "Sampler1"
+ OpName %Sampler2 "Sampler2"
+ OpDecorate %pickhandle Flat
+ OpDecorate %Sampler1 DescriptorSet 0
+ OpDecorate %Sampler1 Binding 0
+ OpDecorate %Sampler1 BindlessSamplerNV
+ OpDecorate %Sampler2 DescriptorSet 0
+ OpDecorate %Sampler2 Binding 1
+ OpDecorate %Sampler2 BindlessSamplerNV
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+%_ptr_Function_8 = OpTypePointer Function %8
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %int_0 = OpConstant %int 0
+ %bool = OpTypeBool
+%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8
+ %Sampler1 = OpVariable %_ptr_UniformConstant_8 UniformConstant
+ %Sampler2 = OpVariable %_ptr_UniformConstant_8 UniformConstant
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %s2D = OpVariable %_ptr_Function_8 Function
+ %pickhandle = OpVariable %_ptr_Function_int Function
+ %14 = OpLoad %int %pickhandle
+ %17 = OpIEqual %bool %14 %int_0
+ %20 = OpLoad %8 %Sampler1
+ %22 = OpLoad %8 %Sampler2
+ %23 = OpSelect %8 %17 %20 %22
+ OpStore %s2D %23
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_memory_test.cpp b/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
index 5fb43f7..ec1a000 100644
--- a/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
@@ -203,9 +203,11 @@
)";
CompileSuccessfully(src, SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\n"
+ HasSubstr("From Vulkan spec:\n"
"Variables identified with the Uniform storage class are used "
"to access transparent buffer backed resources. Such variables "
"must be typed as OpTypeStruct, or an array of this type"));
@@ -277,9 +279,11 @@
)";
CompileSuccessfully(src, SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\n"
+ HasSubstr("From Vulkan spec:\n"
"Variables identified with the Uniform storage class are used "
"to access transparent buffer backed resources. Such variables "
"must be typed as OpTypeStruct, or an array of this type"));
@@ -318,9 +322,11 @@
)";
CompileSuccessfully(src, SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\n"
+ HasSubstr("From Vulkan spec:\n"
"Variables identified with the Uniform storage class are used "
"to access transparent buffer backed resources. Such variables "
"must be typed as OpTypeStruct, or an array of this type"));
@@ -833,6 +839,8 @@
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06808"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal "
@@ -867,6 +875,8 @@
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06808"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("PushConstant OpVariable <id> '10[%10]' has illegal "
@@ -3490,6 +3500,8 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3531,6 +3543,8 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3603,6 +3617,8 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3647,6 +3663,8 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3687,6 +3705,8 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -4212,9 +4232,11 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+ HasSubstr("From Vulkan spec:\nVariables identified with "
"the StorageBuffer storage class are used to access "
"transparent buffer backed resources. Such variables must be "
"typed as OpTypeStruct, or an array of this type"));
@@ -4243,9 +4265,11 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+ HasSubstr("From Vulkan spec:\nVariables identified with "
"the StorageBuffer storage class are used to access "
"transparent buffer backed resources. Such variables must be "
"typed as OpTypeStruct, or an array of this type"));
@@ -4273,9 +4297,11 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+ HasSubstr("From Vulkan spec:\nVariables identified with "
"the StorageBuffer storage class are used to access "
"transparent buffer backed resources. Such variables must be "
"typed as OpTypeStruct, or an array of this type"));
diff --git a/third_party/SPIRV-Tools/test/val/val_modes_test.cpp b/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
index a37989b..689f0ba 100644
--- a/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
@@ -1101,6 +1101,89 @@
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefFrontTooManyModesBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessFrontAMD
+OpExecutionMode %main StencilRefGreaterFrontAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedFrontAMD, "
+ "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
+ "execution modes."));
+}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefBackTooManyModesBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessBackAMD
+OpExecutionMode %main StencilRefGreaterBackAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedBackAMD, "
+ "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
+ "execution modes."));
+}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefFrontGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessFrontAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefBackGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessBackAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+}
+
TEST_F(ValidateMode, FragmentShaderDemoteVertexBad) {
const std::string spirv = R"(
OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/val/val_ray_query.cpp b/third_party/SPIRV-Tools/test/val/val_ray_query.cpp
new file mode 100644
index 0000000..e9b9696
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/val/val_ray_query.cpp
@@ -0,0 +1,578 @@
+// Copyright (c) 2022 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.
+
+// Tests ray query instructions from SPV_KHR_ray_query.
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateRayQuery = spvtest::ValidateBase<bool>;
+
+std::string GenerateShaderCode(
+ const std::string& body,
+ const std::string& capabilities_and_extensions = "",
+ const std::string& declarations = "") {
+ std::ostringstream ss;
+ ss << R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability Float64
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+
+ ss << capabilities_and_extensions;
+
+ ss << R"(
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+OpDecorate %top_level_as DescriptorSet 0
+OpDecorate %top_level_as Binding 0
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%type_rq = OpTypeRayQueryKHR
+%type_as = OpTypeAccelerationStructureKHR
+
+%s32vec2 = OpTypeVector %s32 2
+%u32vec2 = OpTypeVector %u32 2
+%f32vec2 = OpTypeVector %f32 2
+%u32vec3 = OpTypeVector %u32 3
+%s32vec3 = OpTypeVector %s32 3
+%f32vec3 = OpTypeVector %f32 3
+%u32vec4 = OpTypeVector %u32 4
+%s32vec4 = OpTypeVector %s32 4
+%f32vec4 = OpTypeVector %f32 4
+
+%mat4x3 = OpTypeMatrix %f32vec3 4
+
+%f32_0 = OpConstant %f32 0
+%f64_0 = OpConstant %f64 0
+%s32_0 = OpConstant %s32 0
+%u32_0 = OpConstant %u32 0
+%u64_0 = OpConstant %u64 0
+
+%u32vec3_0 = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0
+%f32vec3_0 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0
+%f32vec4_0 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0
+
+%ptr_rq = OpTypePointer Private %type_rq
+%ray_query = OpVariable %ptr_rq Private
+
+%ptr_as = OpTypePointer UniformConstant %type_as
+%top_level_as = OpVariable %ptr_as UniformConstant
+
+%ptr_function_u32 = OpTypePointer Function %u32
+%ptr_function_f32 = OpTypePointer Function %f32
+%ptr_function_f32vec3 = OpTypePointer Function %f32vec3
+)";
+
+ ss << declarations;
+
+ ss << R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+ ss << body;
+
+ ss << R"(
+OpReturn
+OpFunctionEnd)";
+ return ss.str();
+}
+
+std::string RayQueryResult(std::string opcode) {
+ if (opcode.compare("OpRayQueryProceedKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 ||
+ opcode.compare("OpRayQueryGetRayTMinKHR") == 0 ||
+ opcode.compare("OpRayQueryGetRayFlagsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord"
+ "OffsetKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionCandidateAABBOpaqueKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) {
+ return "%result =";
+ }
+ return "";
+}
+
+std::string RayQueryResultType(std::string opcode, bool valid) {
+ if (opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 ||
+ opcode.compare("OpRayQueryGetRayFlagsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord"
+ "OffsetKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0) {
+ return valid ? "%u32" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetRayTMinKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTKHR") == 0) {
+ return valid ? "%f32" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0) {
+ return valid ? "%f32vec2" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayOriginKHR") == 0) {
+ return valid ? "%f32vec3" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryProceedKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionCandidateAABBOpaqueKHR") == 0) {
+ return valid ? "%bool" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) {
+ return valid ? "%mat4x3" : "%f64";
+ }
+ return "";
+}
+
+std::string RayQueryIntersection(std::string opcode, bool valid) {
+ if (opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord"
+ "OffsetKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) {
+ return valid ? "%s32_0" : "%f32_0";
+ }
+ return "";
+}
+
+using RayQueryCommon = spvtest::ValidateBase<std::string>;
+
+TEST_P(RayQueryCommon, Success) {
+ std::string opcode = GetParam();
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << RayQueryResultType(opcode, true);
+ ss << " %ray_query ";
+ ss << RayQueryIntersection(opcode, true);
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(RayQueryCommon, BadQuery) {
+ std::string opcode = GetParam();
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << RayQueryResultType(opcode, true);
+ ss << " %top_level_as ";
+ ss << RayQueryIntersection(opcode, true);
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR"));
+}
+
+TEST_P(RayQueryCommon, BadResult) {
+ std::string opcode = GetParam();
+ std::string result_type = RayQueryResultType(opcode, false);
+ if (!result_type.empty()) {
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << result_type;
+ ss << " %ray_query ";
+ ss << RayQueryIntersection(opcode, true);
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+
+ std::string correct_result_type = RayQueryResultType(opcode, true);
+ if (correct_result_type.compare("%u32") == 0) {
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit int scalar type"));
+ } else if (correct_result_type.compare("%f32") == 0) {
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit float scalar type"));
+ } else if (correct_result_type.compare("%f32vec2") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit float "
+ "2-component vector type"));
+ } else if (correct_result_type.compare("%f32vec3") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit float "
+ "3-component vector type"));
+ } else if (correct_result_type.compare("%bool") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be bool scalar type"));
+ } else if (correct_result_type.compare("%mat4x3") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected matrix type as Result Type"));
+ }
+ }
+}
+
+TEST_P(RayQueryCommon, BadIntersection) {
+ std::string opcode = GetParam();
+ std::string intersection = RayQueryIntersection(opcode, false);
+ if (!intersection.empty()) {
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << RayQueryResultType(opcode, true);
+ ss << " %ray_query ";
+ ss << intersection;
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "expected Intersection ID to be a constant 32-bit int scalar"));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ValidateRayQueryCommon, RayQueryCommon,
+ Values("OpRayQueryTerminateKHR", "OpRayQueryConfirmIntersectionKHR",
+ "OpRayQueryProceedKHR", "OpRayQueryGetIntersectionTypeKHR",
+ "OpRayQueryGetRayTMinKHR", "OpRayQueryGetRayFlagsKHR",
+ "OpRayQueryGetWorldRayDirectionKHR",
+ "OpRayQueryGetWorldRayOriginKHR", "OpRayQueryGetIntersectionTKHR",
+ "OpRayQueryGetIntersectionInstanceCustomIndexKHR",
+ "OpRayQueryGetIntersectionInstanceIdKHR",
+ "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR",
+ "OpRayQueryGetIntersectionGeometryIndexKHR",
+ "OpRayQueryGetIntersectionPrimitiveIndexKHR",
+ "OpRayQueryGetIntersectionBarycentricsKHR",
+ "OpRayQueryGetIntersectionFrontFaceKHR",
+ "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR",
+ "OpRayQueryGetIntersectionObjectRayDirectionKHR",
+ "OpRayQueryGetIntersectionObjectRayOriginKHR",
+ "OpRayQueryGetIntersectionObjectToWorldKHR",
+ "OpRayQueryGetIntersectionWorldToObjectKHR"));
+
+// tests various Intersection operand types
+TEST_F(ValidateRayQuery, IntersectionSuccess) {
+ const std::string body = R"(
+%result_1 = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %s32_0
+%result_2 = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %u32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, IntersectionVector) {
+ const std::string body = R"(
+%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %u32vec3_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Intersection ID to be a constant 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, IntersectionNonConstantVariable) {
+ const std::string body = R"(
+%var = OpVariable %ptr_function_u32 Function
+%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %var
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Intersection ID to be a constant 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, IntersectionNonConstantLoad) {
+ const std::string body = R"(
+%var = OpVariable %ptr_function_u32 Function
+%load = OpLoad %u32 %var
+%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Intersection ID to be a constant 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeSuccess) {
+ const std::string body = R"(
+%var_u32 = OpVariable %ptr_function_u32 Function
+%var_f32 = OpVariable %ptr_function_f32 Function
+%var_f32vec3 = OpVariable %ptr_function_f32vec3 Function
+
+%as = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %as %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+
+%_u32 = OpLoad %u32 %var_u32
+%_f32 = OpLoad %f32 %var_f32
+%_f32vec3 = OpLoad %f32vec3 %var_f32vec3
+OpRayQueryInitializeKHR %ray_query %as %_u32 %_u32 %_f32vec3 %_f32 %_f32vec3 %_f32
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, InitializeFunctionSuccess) {
+ const std::string declaration = R"(
+%rq_ptr = OpTypePointer Private %type_rq
+%rq_func_type = OpTypeFunction %void %rq_ptr
+%rq_var_1 = OpVariable %rq_ptr Private
+%rq_var_2 = OpVariable %rq_ptr Private
+)";
+
+ const std::string body = R"(
+%fcall_1 = OpFunctionCall %void %rq_func %rq_var_1
+%as_1 = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %rq_var_1 %as_1 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+%fcall_2 = OpFunctionCall %void %rq_func %rq_var_2
+OpReturn
+OpFunctionEnd
+%rq_func = OpFunction %void None %rq_func_type
+%rq_param = OpFunctionParameter %rq_ptr
+%label = OpLabel
+%as_2 = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %rq_param %as_2 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, "", declaration).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayQuery) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %top_level_as %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadAS) {
+ const std::string body = R"(
+OpRayQueryInitializeKHR %ray_query %ray_query %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected Acceleration Structure to be of type "
+ "OpTypeAccelerationStructureKHR"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayFlags64) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u64_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Flags must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayFlagsVector) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32vec2 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Operand 15[%v2uint] cannot be a type"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadCullMask) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %f32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Cull Mask must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayOriginVec4) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec4_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayOriginFloat) {
+ const std::string body = R"(
+%var_f32 = OpVariable %ptr_function_f32 Function
+%_f32 = OpLoad %f32 %var_f32
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %_f32 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayOriginInt) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %u32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayTMin) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %u32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray TMin must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayDirection) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec4_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Direction must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayTMax) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f64_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray TMax must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayQuery, GenerateIntersectionSuccess) {
+ const std::string body = R"(
+%var = OpVariable %ptr_function_f32 Function
+%load = OpLoad %f32 %var
+OpRayQueryGenerateIntersectionKHR %ray_query %f32_0
+OpRayQueryGenerateIntersectionKHR %ray_query %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, GenerateIntersectionBadRayQuery) {
+ const std::string body = R"(
+OpRayQueryGenerateIntersectionKHR %top_level_as %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR"));
+}
+
+TEST_F(ValidateRayQuery, GenerateIntersectionBadHitT) {
+ const std::string body = R"(
+OpRayQueryGenerateIntersectionKHR %ray_query %u32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Hit T must be a 32-bit float scalar"));
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/tools/io.h b/third_party/SPIRV-Tools/tools/io.h
index 83a85c1..9dc834e 100644
--- a/third_party/SPIRV-Tools/tools/io.h
+++ b/third_party/SPIRV-Tools/tools/io.h
@@ -26,9 +26,15 @@
#define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY);
#define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT);
+#define SET_STDOUT_TO_BINARY_MODE() _setmode(_fileno(stdout), O_BINARY);
+#define SET_STDOUT_TO_TEXT_MODE() _setmode(_fileno(stdout), O_TEXT);
+#define SET_STDOUT_MODE(mode) _setmode(_fileno(stdout), mode);
#else
#define SET_STDIN_TO_BINARY_MODE()
#define SET_STDIN_TO_TEXT_MODE()
+#define SET_STDOUT_TO_BINARY_MODE() 0
+#define SET_STDOUT_TO_TEXT_MODE() 0
+#define SET_STDOUT_MODE(mode)
#endif
// Appends the contents of the |file| to |data|, assuming each element in the
@@ -115,6 +121,44 @@
return succeeded;
}
+namespace {
+// A class to create and manage a file for outputting data.
+class OutputFile {
+ public:
+ // Opens |filename| in the given mode. If |filename| is nullptr, the empty
+ // string or "-", stdout will be set to the given mode.
+ OutputFile(const char* filename, const char* mode) {
+ const bool use_stdout =
+ !filename || (filename[0] == '-' && filename[1] == '\0');
+ if (use_stdout) {
+ if (strchr(mode, 'b')) {
+ old_mode_ = SET_STDOUT_TO_BINARY_MODE();
+ } else {
+ old_mode_ = SET_STDOUT_TO_TEXT_MODE();
+ }
+ fp_ = stdout;
+ } else {
+ fp_ = fopen(filename, mode);
+ }
+ }
+
+ ~OutputFile() {
+ if (fp_ == stdout) {
+ SET_STDOUT_MODE(old_mode_);
+ } else if (fp_ != nullptr) {
+ fclose(fp_);
+ }
+ }
+
+ // Returns a file handle to the file.
+ FILE* GetFileHandle() const { return fp_; }
+
+ private:
+ FILE* fp_;
+ int old_mode_;
+};
+} // namespace
+
// Writes the given |data| into the file named as |filename| using the given
// |mode|, assuming |data| is an array of |count| elements of type |T|. If
// |filename| is nullptr or "-", writes to standard output. If any error occurs,
@@ -122,20 +166,19 @@
template <typename T>
bool WriteFile(const char* filename, const char* mode, const T* data,
size_t count) {
- const bool use_stdout =
- !filename || (filename[0] == '-' && filename[1] == '\0');
- if (FILE* fp = (use_stdout ? stdout : fopen(filename, mode))) {
- size_t written = fwrite(data, sizeof(T), count, fp);
- if (count != written) {
- fprintf(stderr, "error: could not write to file '%s'\n", filename);
- if (!use_stdout) fclose(fp);
- return false;
- }
- if (!use_stdout) fclose(fp);
- } else {
+ OutputFile file(filename, mode);
+ FILE* fp = file.GetFileHandle();
+ if (fp == nullptr) {
fprintf(stderr, "error: could not open file '%s'\n", filename);
return false;
}
+
+ size_t written = fwrite(data, sizeof(T), count, fp);
+ if (count != written) {
+ fprintf(stderr, "error: could not write to file '%s'\n", filename);
+ return false;
+ }
+
return true;
}