Squashed 'third_party/SPIRV-Tools/' changes from 9668d2e4e..bd325d298
bd325d298 spirv-diff: Basic support for OpTypeForwardPointer (#4761)
0c670ef1d spirv-as: Add opcode name when possible (#4757)
40cd21839 spirv-diff: Use GetSingleWord*Operand (#4768)
7841afd98 BUILD.gn: Fix standalone GN builds (#4765)
05745cc9d Handle shaders without execution model in spread-volatile-semantics (#4766)
fa5d42483 spirv-val: Add more Vulkan VUID labels (#4764)
a3fbc9331 Support SPV_KHR_uniform_group_instructions (#4734)
48c8363f0 spirv-diff: Refactor instruction grouping and matching (#4760)
90728d2df spirv-val: Clean up VariablePointers logic (#4755)
cab0b7715 Use types have same widths in loop condition. (#4763)
b3c179063 spirv-val: Add Vulkan 32-bit bit op Base (#4758)
git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: bd325d298442bd9d895f33f372bb16a976b7747f
diff --git a/BUILD.gn b/BUILD.gn
index 5910e5e..0ce6c35 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -20,10 +20,15 @@
}
# SPIRV-Tools may be part of multiple projects in the Chromium tree.
-# Only enable building executables if this is the main copy, or standalone.
+# Only enable building executables if this is the main copy.
abspath = get_path_info(".", "abspath")
-spvtools_chromium_third_party = (abspath == "//third_party/SPIRV-Tools/")
-spvtools_build_executables = !build_with_chromium || spvtools_chromium_third_party
+spvtools_chromium_third_party = (abspath == "//third_party/vulkan-deps/spirv-tools/src/")
+spvtools_build_executables = build_with_chromium && spvtools_chromium_third_party
+# Fuchsia also requires building the executables.
+# TODO(b/158002593): Avoid the use of dependent-specific variables.
+if (defined(is_fuchsia_tree) && is_fuchsia_tree) {
+ spvtools_build_executables = true
+}
spirv_headers = spirv_tools_spirv_headers_dir
spirv_is_winuwp = is_win && target_os == "winuwp"
diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp
index 12172bf..4ddbbd3 100644
--- a/source/diff/diff.cpp
+++ b/source/diff/diff.cpp
@@ -40,10 +40,12 @@
// A list of ids with some similar property, for example functions with the same
// name.
using IdGroup = std::vector<uint32_t>;
-// A map of function names to function ids with the same name. This is an
-// ordered map so different implementations produce identical results.
+// A map of names to ids with the same name. This is an ordered map so
+// different implementations produce identical results.
using IdGroupMapByName = std::map<std::string, IdGroup>;
using IdGroupMapByTypeId = std::map<uint32_t, IdGroup>;
+using IdGroupMapByOp = std::map<SpvOp, IdGroup>;
+using IdGroupMapByStorageClass = std::map<SpvStorageClass, IdGroup>;
// A set of potential id mappings that haven't been resolved yet. Any id in src
// may map in any id in dst. Note that ids are added in the same order as they
@@ -177,7 +179,8 @@
IdInstructions(const opt::Module* module)
: inst_map_(module->IdBound(), nullptr),
name_map_(module->IdBound()),
- decoration_map_(module->IdBound()) {
+ decoration_map_(module->IdBound()),
+ forward_pointer_map_(module->IdBound()) {
// Map ids from all sections to instructions that define them.
MapIdsToInstruction(module->ext_inst_imports());
MapIdsToInstruction(module->types_values());
@@ -195,6 +198,7 @@
// between src and dst modules.
MapIdsToInfos(module->debugs2());
MapIdsToInfos(module->annotations());
+ MapIdsToInfos(module->types_values());
}
void MapIdToInstruction(uint32_t id, const opt::Instruction* inst);
@@ -207,6 +211,7 @@
IdToInstructionMap inst_map_;
IdToInfoMap name_map_;
IdToInfoMap decoration_map_;
+ IdToInstructionMap forward_pointer_map_;
};
class Differ {
@@ -234,6 +239,7 @@
void MatchMemoryModel();
void MatchEntryPointIds();
void MatchExecutionModes();
+ void MatchTypeForwardPointers();
void MatchTypeIds();
void MatchConstants();
void MatchVariableIds();
@@ -268,7 +274,7 @@
// Helper functions that match ids between src and dst
void PoolPotentialIds(
opt::IteratorRange<opt::Module::const_inst_iterator> section,
- std::vector<uint32_t>& ids,
+ std::vector<uint32_t>& ids, bool is_src,
std::function<bool(const opt::Instruction&)> filter,
std::function<uint32_t(const opt::Instruction&)> get_id);
void MatchIds(
@@ -292,6 +298,46 @@
opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts);
+ // Get various properties from an id. These Helper functions are passed to
+ // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument).
+ uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id);
+ SpvStorageClass GroupIdsHelperGetTypePointerStorageClass(
+ const IdInstructions& id_to, uint32_t id);
+ SpvOp GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
+ uint32_t id);
+
+ // Given a list of ids, groups them based on some value. The `get_group`
+ // function extracts a piece of information corresponding to each id, and the
+ // ids are bucketed based on that (and output in `groups`). This is useful to
+ // attempt to match ids between src and dst only when said property is
+ // identical.
+ template <typename T>
+ void GroupIds(const IdGroup& ids, bool is_src, std::map<T, IdGroup>* groups,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t));
+
+ // Calls GroupIds to bucket ids in src and dst based on a property returned by
+ // `get_group`. This function then calls `match_group` for each bucket (i.e.
+ // "group") with identical values for said property.
+ //
+ // For example, say src and dst ids have the following properties
+ // correspondingly:
+ //
+ // - src ids' properties: {id0: A, id1: A, id2: B, id3: C, id4: B}
+ // - dst ids' properties: {id0': B, id1': C, id2': B, id3': D, id4': B}
+ //
+ // Then `match_group` is called 2 times:
+ //
+ // - Once with: ([id2, id4], [id0', id2', id4']) corresponding to B
+ // - Once with: ([id3], [id2']) corresponding to C
+ //
+ // Ids corresponding to A and D cannot match based on this property.
+ template <typename T>
+ void GroupIdsAndMatch(
+ const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t),
+ std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
+ match_group);
+
// Helper functions that determine if two instructions match
bool DoIdsMatch(uint32_t src_id, uint32_t dst_id);
bool DoesOperandMatch(const opt::Operand& src_operand,
@@ -325,6 +371,10 @@
bool MatchPerVertexVariable(const opt::Instruction* src_inst,
const opt::Instruction* dst_inst);
+ // Helper functions for matching OpTypeForwardPointer
+ void MatchTypeForwardPointersByName(const IdGroup& src, const IdGroup& dst);
+ void MatchTypeForwardPointersByTypeOp(const IdGroup& src, const IdGroup& dst);
+
// Helper functions for function matching.
using FunctionMap = std::map<uint32_t, const opt::Function*>;
@@ -335,14 +385,6 @@
FunctionInstMap* function_insts);
void GetFunctionHeaderInstructions(const opt::Module* module,
FunctionInstMap* function_insts);
- void GroupIdsByName(const IdGroup& functions, bool is_src,
- IdGroupMapByName* groups);
- void GroupIdsByTypeId(const IdGroup& functions, bool is_src,
- IdGroupMapByTypeId* groups);
- template <typename T>
- void GroupIds(const IdGroup& functions, bool is_src,
- std::map<T, IdGroup>* groups,
- std::function<T(const IdInstructions, uint32_t)> get_group);
void BestEffortMatchFunctions(const IdGroup& src_func_ids,
const IdGroup& dst_func_ids,
const FunctionInstMap& src_func_insts,
@@ -374,14 +416,20 @@
uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id);
SpvExecutionModel GetExecutionModel(const opt::Module* module,
uint32_t entry_point_id);
+ bool HasName(const IdInstructions& id_to, uint32_t id);
+ // Get the OpName associated with an id
std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name);
- std::string GetFunctionName(const IdInstructions& id_to, uint32_t id);
+ // Get the OpName associated with an id, with argument types stripped for
+ // functions. Some tools don't encode function argument types in the OpName
+ // string, and this improves diff between SPIR-V from those tools and others.
+ std::string GetSanitizedName(const IdInstructions& id_to, uint32_t id);
uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
SpvStorageClass* storage_class);
bool GetDecorationValue(const IdInstructions& id_to, uint32_t id,
SpvDecoration decoration, uint32_t* decoration_value);
+ const opt::Instruction* GetForwardPointerInst(const IdInstructions& id_to,
+ uint32_t id);
bool IsIntType(const IdInstructions& id_to, uint32_t type_id);
- // bool IsUintType(const IdInstructions& id_to, uint32_t type_id);
bool IsFloatType(const IdInstructions& id_to, uint32_t type_id);
bool IsConstantUint(const IdInstructions& id_to, uint32_t id);
bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id);
@@ -525,6 +573,14 @@
case SpvOpMemberDecorate:
info_map = &decoration_map_;
break;
+ case SpvOpTypeForwardPointer: {
+ uint32_t id = inst.GetSingleWordOperand(0);
+ assert(id != 0);
+
+ assert(id < forward_pointer_map_.size());
+ forward_pointer_map_[id] = &inst;
+ continue;
+ }
default:
// Currently unsupported instruction, don't attempt to use it for
// matching.
@@ -548,18 +604,27 @@
void Differ::PoolPotentialIds(
opt::IteratorRange<opt::Module::const_inst_iterator> section,
- std::vector<uint32_t>& ids,
+ std::vector<uint32_t>& ids, bool is_src,
std::function<bool(const opt::Instruction&)> filter,
std::function<uint32_t(const opt::Instruction&)> get_id) {
for (const opt::Instruction& inst : section) {
if (!filter(inst)) {
continue;
}
+
uint32_t result_id = get_id(inst);
assert(result_id != 0);
assert(std::find(ids.begin(), ids.end(), result_id) == ids.end());
+ // Don't include ids that are already matched, for example through
+ // OpTypeForwardPointer.
+ const bool is_matched = is_src ? id_map_.IsSrcMapped(result_id)
+ : id_map_.IsDstMapped(result_id);
+ if (is_matched) {
+ continue;
+ }
+
ids.push_back(result_id);
}
}
@@ -668,9 +733,9 @@
// the sorted list of instructions between src and dst modules.
if (a->opcode() == SpvOpExecutionMode) {
const SpvExecutionModel src_model =
- GetExecutionModel(src_inst_module, a->GetOperand(0).AsId());
+ GetExecutionModel(src_inst_module, a->GetSingleWordOperand(0));
const SpvExecutionModel dst_model =
- GetExecutionModel(dst_inst_module, b->GetOperand(0).AsId());
+ GetExecutionModel(dst_inst_module, b->GetSingleWordOperand(0));
if (src_model < dst_model) {
return -1;
@@ -748,6 +813,81 @@
}
}
+uint32_t Differ::GroupIdsHelperGetTypeId(const IdInstructions& id_to,
+ uint32_t id) {
+ return GetInst(id_to, id)->type_id();
+}
+
+SpvStorageClass Differ::GroupIdsHelperGetTypePointerStorageClass(
+ const IdInstructions& id_to, uint32_t id) {
+ const opt::Instruction* inst = GetInst(id_to, id);
+ assert(inst && inst->opcode() == SpvOpTypePointer);
+ return SpvStorageClass(inst->GetSingleWordInOperand(0));
+}
+
+SpvOp Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
+ uint32_t id) {
+ const opt::Instruction* inst = GetInst(id_to, id);
+ assert(inst && inst->opcode() == SpvOpTypePointer);
+
+ const uint32_t type_id = inst->GetSingleWordInOperand(1);
+ const opt::Instruction* type_inst = GetInst(id_to, type_id);
+ assert(type_inst);
+
+ return type_inst->opcode();
+}
+
+template <typename T>
+void Differ::GroupIds(const IdGroup& ids, bool is_src,
+ std::map<T, IdGroup>* groups,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t)) {
+ assert(groups->empty());
+
+ const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_;
+
+ for (const uint32_t id : ids) {
+ // Don't include ids that are already matched, for example through
+ // OpEntryPoint.
+ const bool is_matched =
+ is_src ? id_map_.IsSrcMapped(id) : id_map_.IsDstMapped(id);
+ if (is_matched) {
+ continue;
+ }
+
+ T group = (this->*get_group)(id_to, id);
+ (*groups)[group].push_back(id);
+ }
+}
+
+template <typename T>
+void Differ::GroupIdsAndMatch(
+ const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t),
+ std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
+ match_group) {
+ // Group the ids based on a key (get_group)
+ std::map<T, IdGroup> src_groups;
+ std::map<T, IdGroup> dst_groups;
+
+ GroupIds<T>(src_ids, true, &src_groups, get_group);
+ GroupIds<T>(dst_ids, false, &dst_groups, get_group);
+
+ // Iterate over the groups, and match those with identical keys
+ for (const auto& iter : src_groups) {
+ const T& key = iter.first;
+ const IdGroup& src_group = iter.second;
+
+ if (key == invalid_group_key) {
+ continue;
+ }
+
+ const IdGroup& dst_group = dst_groups[key];
+
+ // Let the caller match the groups as appropriate.
+ match_group(src_group, dst_group);
+ }
+}
+
bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) {
assert(dst_id != 0);
return id_map_.MappedDstId(src_id) == dst_id;
@@ -1253,13 +1393,59 @@
bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst,
const opt::Instruction* dst_inst) {
SpvStorageClass src_storage_class =
- SpvStorageClass(src_inst->GetInOperand(0).words[0]);
+ SpvStorageClass(src_inst->GetSingleWordInOperand(0));
SpvStorageClass dst_storage_class =
- SpvStorageClass(dst_inst->GetInOperand(0).words[0]);
+ SpvStorageClass(dst_inst->GetSingleWordInOperand(0));
return src_storage_class == dst_storage_class;
}
+void Differ::MatchTypeForwardPointersByName(const IdGroup& src,
+ const IdGroup& dst) {
+ // Given two sets of compatible groups of OpTypeForwardPointer instructions,
+ // attempts to match them by name.
+
+ // Group them by debug info and loop over them.
+ GroupIdsAndMatch<std::string>(
+ src, dst, "", &Differ::GetSanitizedName,
+ [this](const IdGroup& src_group, const IdGroup& dst_group) {
+
+ // Match only if there's a unique forward declaration with this debug
+ // name.
+ if (src_group.size() == 1 && dst_group.size() == 1) {
+ id_map_.MapIds(src_group[0], dst_group[0]);
+ }
+ });
+}
+
+void Differ::MatchTypeForwardPointersByTypeOp(const IdGroup& src,
+ const IdGroup& dst) {
+ // Given two sets of compatible groups of OpTypeForwardPointer instructions,
+ // attempts to match them by type op. Must be called after
+ // MatchTypeForwardPointersByName to match as many as possible by debug info.
+
+ // Remove ids that are matched with debug info in
+ // MatchTypeForwardPointersByName.
+ IdGroup src_unmatched_ids;
+ IdGroup dst_unmatched_ids;
+
+ std::copy_if(src.begin(), src.end(), std::back_inserter(src_unmatched_ids),
+ [this](uint32_t id) { return !id_map_.IsSrcMapped(id); });
+ std::copy_if(dst.begin(), dst.end(), std::back_inserter(dst_unmatched_ids),
+ [this](uint32_t id) { return !id_map_.IsDstMapped(id); });
+
+ // Match only if there's a unique forward declaration with this
+ // storage class and type opcode. If both have debug info, they
+ // must not have been matchable.
+ if (src_unmatched_ids.size() == 1 && dst_unmatched_ids.size() == 1) {
+ uint32_t src_id = src_unmatched_ids[0];
+ uint32_t dst_id = dst_unmatched_ids[0];
+ if (!HasName(src_id_to_, src_id) || !HasName(dst_id_to_, dst_id)) {
+ id_map_.MapIds(src_id, dst_id);
+ }
+ }
+}
+
InstructionList Differ::GetFunctionBody(opt::IRContext* context,
opt::Function& function) {
// Canonicalize the blocks of the function to produce better diff, for example
@@ -1319,28 +1505,6 @@
}
}
-template <typename T>
-void Differ::GroupIds(
- const IdGroup& functions, bool is_src, std::map<T, IdGroup>* groups,
- std::function<T(const IdInstructions, uint32_t)> get_group) {
- assert(groups->empty());
-
- const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_;
-
- for (const uint32_t func_id : functions) {
- // Don't include functions that are already matched, for example through
- // OpEntryPoint.
- const bool is_matched =
- is_src ? id_map_.IsSrcMapped(func_id) : id_map_.IsDstMapped(func_id);
- if (is_matched) {
- continue;
- }
-
- T group = get_group(id_to, func_id);
- (*groups)[group].push_back(func_id);
- }
-}
-
void Differ::BestEffortMatchFunctions(const IdGroup& src_func_ids,
const IdGroup& dst_func_ids,
const FunctionInstMap& src_func_insts,
@@ -1361,7 +1525,7 @@
if (id_map_.IsSrcMapped(src_func_id)) {
continue;
}
- const std::string src_name = GetFunctionName(src_id_to_, src_func_id);
+ const std::string src_name = GetSanitizedName(src_id_to_, src_func_id);
for (const uint32_t dst_func_id : dst_func_ids) {
if (id_map_.IsDstMapped(dst_func_id)) {
@@ -1369,7 +1533,7 @@
}
// Don't match functions that are named, but the names are different.
- const std::string dst_name = GetFunctionName(dst_id_to_, dst_func_id);
+ const std::string dst_name = GetSanitizedName(dst_id_to_, dst_func_id);
if (src_name != "" && dst_name != "" && src_name != dst_name) {
continue;
}
@@ -1406,22 +1570,6 @@
}
}
-void Differ::GroupIdsByName(const IdGroup& functions, bool is_src,
- IdGroupMapByName* groups) {
- GroupIds<std::string>(functions, is_src, groups,
- [this](const IdInstructions& id_to, uint32_t func_id) {
- return GetFunctionName(id_to, func_id);
- });
-}
-
-void Differ::GroupIdsByTypeId(const IdGroup& functions, bool is_src,
- IdGroupMapByTypeId* groups) {
- GroupIds<uint32_t>(functions, is_src, groups,
- [this](const IdInstructions& id_to, uint32_t func_id) {
- return GetInst(id_to, func_id)->type_id();
- });
-}
-
void Differ::MatchFunctionParamIds(const opt::Function* src_func,
const opt::Function* dst_func) {
IdGroup src_params;
@@ -1437,52 +1585,33 @@
},
false);
- IdGroupMapByName src_param_groups;
- IdGroupMapByName dst_param_groups;
+ GroupIdsAndMatch<std::string>(
+ src_params, dst_params, "", &Differ::GetSanitizedName,
+ [this](const IdGroup& src_group, const IdGroup& dst_group) {
- GroupIdsByName(src_params, true, &src_param_groups);
- GroupIdsByName(dst_params, false, &dst_param_groups);
-
- // Match parameters with identical names.
- for (const auto& src_param_group : src_param_groups) {
- const std::string& name = src_param_group.first;
- const IdGroup& src_group = src_param_group.second;
-
- if (name == "") {
- continue;
- }
-
- const IdGroup& dst_group = dst_param_groups[name];
-
- // There shouldn't be two parameters with the same name, so the ids should
- // match. There is nothing restricting the SPIR-V however to have two
- // parameters with the same name, so be resilient against that.
- if (src_group.size() == 1 && dst_group.size() == 1) {
- id_map_.MapIds(src_group[0], dst_group[0]);
- }
- }
+ // There shouldn't be two parameters with the same name, so the ids
+ // should match. There is nothing restricting the SPIR-V however to have
+ // two parameters with the same name, so be resilient against that.
+ if (src_group.size() == 1 && dst_group.size() == 1) {
+ id_map_.MapIds(src_group[0], dst_group[0]);
+ }
+ });
// Then match the parameters by their type. If there are multiple of them,
// match them by their order.
- IdGroupMapByTypeId src_param_groups_by_type_id;
- IdGroupMapByTypeId dst_param_groups_by_type_id;
+ GroupIdsAndMatch<uint32_t>(
+ src_params, dst_params, 0, &Differ::GroupIdsHelperGetTypeId,
+ [this](const IdGroup& src_group_by_type_id,
+ const IdGroup& dst_group_by_type_id) {
- GroupIdsByTypeId(src_params, true, &src_param_groups_by_type_id);
- GroupIdsByTypeId(dst_params, false, &dst_param_groups_by_type_id);
+ const size_t shared_param_count =
+ std::min(src_group_by_type_id.size(), dst_group_by_type_id.size());
- for (const auto& src_param_group_by_type_id : src_param_groups_by_type_id) {
- const uint32_t type_id = src_param_group_by_type_id.first;
- const IdGroup& src_group_by_type_id = src_param_group_by_type_id.second;
- const IdGroup& dst_group_by_type_id = dst_param_groups_by_type_id[type_id];
-
- const size_t shared_param_count =
- std::min(src_group_by_type_id.size(), dst_group_by_type_id.size());
-
- for (size_t param_index = 0; param_index < shared_param_count;
- ++param_index) {
- id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
- }
- }
+ for (size_t param_index = 0; param_index < shared_param_count;
+ ++param_index) {
+ id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
+ }
+ });
}
float Differ::MatchFunctionBodies(const InstructionList& src_body,
@@ -1564,8 +1693,8 @@
case SpvOpInBoundsPtrAccessChain:
case SpvOpLoad:
case SpvOpStore:
- const uint32_t src_pointer_id = src_inst->GetInOperand(0).AsId();
- const uint32_t dst_pointer_id = dst_inst->GetInOperand(0).AsId();
+ const uint32_t src_pointer_id = src_inst->GetSingleWordInOperand(0);
+ const uint32_t dst_pointer_id = dst_inst->GetSingleWordInOperand(0);
if (IsVariable(src_id_to_, src_pointer_id) &&
IsVariable(dst_id_to_, dst_pointer_id) &&
!id_map_.IsSrcMapped(src_pointer_id) &&
@@ -1594,15 +1723,15 @@
assert(constant_inst->opcode() == SpvOpConstant);
assert(GetInst(id_to, constant_inst->type_id())->opcode() == SpvOpTypeInt);
- return constant_inst->GetInOperand(0).words[0];
+ return constant_inst->GetSingleWordInOperand(0);
}
SpvExecutionModel Differ::GetExecutionModel(const opt::Module* module,
uint32_t entry_point_id) {
for (const opt::Instruction& inst : module->entry_points()) {
assert(inst.opcode() == SpvOpEntryPoint);
- if (inst.GetOperand(1).AsId() == entry_point_id) {
- return SpvExecutionModel(inst.GetOperand(0).words[0]);
+ if (inst.GetSingleWordOperand(1) == entry_point_id) {
+ return SpvExecutionModel(inst.GetSingleWordOperand(0));
}
}
@@ -1610,6 +1739,19 @@
return SpvExecutionModel(0xFFF);
}
+bool Differ::HasName(const IdInstructions& id_to, uint32_t id) {
+ assert(id != 0);
+ assert(id < id_to.name_map_.size());
+
+ for (const opt::Instruction* inst : id_to.name_map_[id]) {
+ if (inst->opcode() == SpvOpName) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
std::string Differ::GetName(const IdInstructions& id_to, uint32_t id,
bool* has_name) {
assert(id != 0);
@@ -1626,7 +1768,7 @@
return "";
}
-std::string Differ::GetFunctionName(const IdInstructions& id_to, uint32_t id) {
+std::string Differ::GetSanitizedName(const IdInstructions& id_to, uint32_t id) {
bool has_name = false;
std::string name = GetName(id_to, id, &has_name);
@@ -1634,7 +1776,7 @@
return "";
}
- // Remove args from the name
+ // Remove args from the name, in case this is a function name
return name.substr(0, name.find('('));
}
@@ -1643,14 +1785,14 @@
const opt::Instruction* var_inst = GetInst(id_to, var_id);
assert(var_inst->opcode() == SpvOpVariable);
- *storage_class = SpvStorageClass(var_inst->GetInOperand(0).words[0]);
+ *storage_class = SpvStorageClass(var_inst->GetSingleWordInOperand(0));
// Get the type pointer from the variable.
const uint32_t type_pointer_id = var_inst->type_id();
const opt::Instruction* type_pointer_inst = GetInst(id_to, type_pointer_id);
// Get the type from the type pointer.
- return type_pointer_inst->GetInOperand(1).AsId();
+ return type_pointer_inst->GetSingleWordInOperand(1);
}
bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id,
@@ -1660,9 +1802,10 @@
assert(id < id_to.decoration_map_.size());
for (const opt::Instruction* inst : id_to.decoration_map_[id]) {
- if (inst->opcode() == SpvOpDecorate && inst->GetOperand(0).AsId() == id &&
- inst->GetOperand(1).words[0] == decoration) {
- *decoration_value = inst->GetOperand(2).words[0];
+ if (inst->opcode() == SpvOpDecorate &&
+ inst->GetSingleWordOperand(0) == id &&
+ inst->GetSingleWordOperand(1) == decoration) {
+ *decoration_value = inst->GetSingleWordOperand(2);
return true;
}
}
@@ -1670,20 +1813,16 @@
return false;
}
-bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) {
- return IsOp(id_to, type_id, SpvOpTypeInt);
-#if 0
- const opt::Instruction *type_inst = GetInst(id_to, type_id);
- return type_inst->opcode() == SpvOpTypeInt && type_inst->GetInOperand(1).words[0] != 0;
-#endif
+const opt::Instruction* Differ::GetForwardPointerInst(
+ const IdInstructions& id_to, uint32_t id) {
+ assert(id != 0);
+ assert(id < id_to.forward_pointer_map_.size());
+ return id_to.forward_pointer_map_[id];
}
-#if 0
-bool Differ::IsUintType(const IdInstructions& id_to, uint32_t type_id) {
- const opt::Instruction *type_inst = GetInst(id_to, type_id);
- return type_inst->opcode() == SpvOpTypeInt && type_inst->GetInOperand(1).words[0] == 0;
+bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) {
+ return IsOp(id_to, type_id, SpvOpTypeInt);
}
-#endif
bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) {
return IsOp(id_to, type_id, SpvOpTypeFloat);
@@ -1713,9 +1852,9 @@
for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) {
if (inst->opcode() == SpvOpMemberDecorate &&
- inst->GetOperand(0).AsId() == type_id &&
- inst->GetOperand(2).words[0] == SpvDecorationBuiltIn) {
- SpvBuiltIn built_in = SpvBuiltIn(inst->GetOperand(3).words[0]);
+ inst->GetSingleWordOperand(0) == type_id &&
+ inst->GetSingleWordOperand(2) == SpvDecorationBuiltIn) {
+ SpvBuiltIn built_in = SpvBuiltIn(inst->GetSingleWordOperand(3));
// Only gl_PerVertex can have, and it can only have, the following
// built-in decorations.
@@ -1737,7 +1876,7 @@
// If array, get the element type.
if (type_inst->opcode() == SpvOpTypeArray) {
- type_id = type_inst->GetInOperand(0).AsId();
+ type_id = type_inst->GetSingleWordInOperand(0);
}
// Now check if the type is gl_PerVertex.
@@ -1751,14 +1890,14 @@
case SpvOpTypeArray:
// The gl_PerVertex instance could be an array, look for a variable of
// the array type instead.
- if (inst.GetInOperand(0).AsId() == type_id) {
+ if (inst.GetSingleWordInOperand(0) == type_id) {
type_id = inst.result_id();
}
break;
case SpvOpTypePointer:
// Find the storage class of the pointer to this type.
- if (inst.GetInOperand(1).AsId() == type_id) {
- return SpvStorageClass(inst.GetInOperand(0).words[0]);
+ if (inst.GetSingleWordInOperand(1) == type_id) {
+ return SpvStorageClass(inst.GetSingleWordInOperand(0));
}
break;
default:
@@ -1800,7 +1939,7 @@
case SpvOpSpecConstant:
// Same kind of number as the selector (OpSwitch) or the type
// (Op*Constant).
- return GetTypeNumberKind(id_to, inst.GetOperand(0).AsId(),
+ return GetTypeNumberKind(id_to, inst.GetSingleWordOperand(0),
number_bit_width);
default:
assert(false && "Unreachable");
@@ -1824,12 +1963,12 @@
switch (type_inst->opcode()) {
case SpvOpTypeInt:
- *number_bit_width = type_inst->GetOperand(1).words[0];
- return type_inst->GetOperand(2).words[0] == 0 ? SPV_NUMBER_UNSIGNED_INT
- : SPV_NUMBER_SIGNED_INT;
+ *number_bit_width = type_inst->GetSingleWordOperand(1);
+ return type_inst->GetSingleWordOperand(2) == 0 ? SPV_NUMBER_UNSIGNED_INT
+ : SPV_NUMBER_SIGNED_INT;
break;
case SpvOpTypeFloat:
- *number_bit_width = type_inst->GetOperand(1).words[0];
+ *number_bit_width = type_inst->GetSingleWordOperand(1);
return SPV_NUMBER_FLOATING;
default:
assert(false && "Unreachable");
@@ -1853,9 +1992,9 @@
};
auto accept_all = [](const opt::Instruction&) { return true; };
- PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids,
+ PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids, true,
accept_all, get_result_id);
- PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids,
+ PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids, false,
accept_all, get_result_id);
// Then match the ids.
@@ -1887,12 +2026,12 @@
std::set<uint32_t> all_execution_models;
for (const opt::Instruction& src_inst : src_->entry_points()) {
- uint32_t execution_model = src_inst.GetOperand(0).words[0];
+ uint32_t execution_model = src_inst.GetSingleWordOperand(0);
src_entry_points_map[execution_model].push_back(&src_inst);
all_execution_models.insert(execution_model);
}
for (const opt::Instruction& dst_inst : dst_->entry_points()) {
- uint32_t execution_model = dst_inst.GetOperand(0).words[0];
+ uint32_t execution_model = dst_inst.GetSingleWordOperand(0);
dst_entry_points_map[execution_model].push_back(&dst_inst);
all_execution_models.insert(execution_model);
}
@@ -1905,8 +2044,8 @@
// If there is only one entry point in src and dst with that model, match
// them unconditionally.
if (src_insts.size() == 1 && dst_insts.size() == 1) {
- uint32_t src_id = src_insts[0]->GetOperand(1).AsId();
- uint32_t dst_id = dst_insts[0]->GetOperand(1).AsId();
+ uint32_t src_id = src_insts[0]->GetSingleWordOperand(1);
+ uint32_t dst_id = dst_insts[0]->GetSingleWordOperand(1);
id_map_.MapIds(src_id, dst_id);
id_map_.MapInsts(src_insts[0], dst_insts[0]);
continue;
@@ -1920,8 +2059,8 @@
const opt::Operand& dst_name = dst_inst->GetOperand(2);
if (src_name.AsString() == dst_name.AsString()) {
- uint32_t src_id = src_inst->GetOperand(1).AsId();
- uint32_t dst_id = dst_inst->GetOperand(1).AsId();
+ uint32_t src_id = src_inst->GetSingleWordOperand(1);
+ uint32_t dst_id = dst_inst->GetSingleWordOperand(1);
id_map_.MapIds(src_id, dst_id);
id_map_.MapInsts(src_inst, dst_inst);
matched = true;
@@ -1939,6 +2078,80 @@
MatchPreambleInstructions(src_->execution_modes(), dst_->execution_modes());
}
+void Differ::MatchTypeForwardPointers() {
+ // Bunch all of type forward pointers as potential matches.
+ PotentialIdMap potential_id_map;
+ auto get_pointer_type_id = [](const opt::Instruction& inst) {
+ return inst.GetSingleWordOperand(0);
+ };
+ auto accept_type_forward_pointer_ops = [](const opt::Instruction& inst) {
+ return inst.opcode() == SpvOpTypeForwardPointer;
+ };
+
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
+ accept_type_forward_pointer_ops, get_pointer_type_id);
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
+ accept_type_forward_pointer_ops, get_pointer_type_id);
+
+ // Matching types with cyclical references (i.e. in the style of linked lists)
+ // can get very complex. Currently, the diff tool matches types bottom up, so
+ // on every instruction it expects to know if its operands are already matched
+ // or not. With cyclical references, it cannot know that. Type matching may
+ // need significant modifications to be able to support this use case.
+ //
+ // Currently, forwarded types are only matched by storage class and debug
+ // info, with minimal matching of the type being forwarded:
+ //
+ // - Group by class
+ // - Group by OpType being pointed to
+ // - Group by debug info
+ // - If same name and unique, match
+ // - If leftover is unique, match
+
+ // Group forwarded pointers by storage class first and loop over them.
+ GroupIdsAndMatch<SpvStorageClass>(
+ potential_id_map.src_ids, potential_id_map.dst_ids, SpvStorageClassMax,
+ &Differ::GroupIdsHelperGetTypePointerStorageClass,
+ [this](const IdGroup& src_group_by_storage_class,
+ const IdGroup& dst_group_by_storage_class) {
+
+ // Group them further by the type they are pointing to and loop over
+ // them.
+ GroupIdsAndMatch<SpvOp>(
+ src_group_by_storage_class, dst_group_by_storage_class, SpvOpMax,
+ &Differ::GroupIdsHelperGetTypePointerTypeOp,
+ [this](const IdGroup& src_group_by_type_op,
+ const IdGroup& dst_group_by_type_op) {
+
+ // Group them even further by debug info, if possible and match by
+ // debug name.
+ MatchTypeForwardPointersByName(src_group_by_type_op,
+ dst_group_by_type_op);
+
+ // Match the leftovers only if they lack debug info and there is
+ // only one instance of them.
+ MatchTypeForwardPointersByTypeOp(src_group_by_type_op,
+ dst_group_by_type_op);
+ });
+ });
+
+ // Match the instructions that forward declare the same type themselves
+ for (uint32_t src_id : potential_id_map.src_ids) {
+ uint32_t dst_id = id_map_.MappedDstId(src_id);
+ if (dst_id == 0) continue;
+
+ const opt::Instruction* src_forward_inst =
+ GetForwardPointerInst(src_id_to_, src_id);
+ const opt::Instruction* dst_forward_inst =
+ GetForwardPointerInst(dst_id_to_, dst_id);
+
+ assert(src_forward_inst);
+ assert(dst_forward_inst);
+
+ id_map_.MapInsts(src_forward_inst, dst_forward_inst);
+ }
+}
+
void Differ::MatchTypeIds() {
// Bunch all of type ids as potential matches.
PotentialIdMap potential_id_map;
@@ -1949,9 +2162,9 @@
return spvOpcodeGeneratesType(inst.opcode());
};
- PoolPotentialIds(src_->types_values(), potential_id_map.src_ids,
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
accept_type_ops, get_result_id);
- PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids,
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
accept_type_ops, get_result_id);
// Then match the ids. Start with exact matches, then match the leftover with
@@ -2007,8 +2220,8 @@
return false;
}
- if (AreIdenticalUintConstants(src_inst->GetInOperand(1).AsId(),
- dst_inst->GetInOperand(1).AsId())) {
+ if (AreIdenticalUintConstants(src_inst->GetSingleWordInOperand(1),
+ dst_inst->GetSingleWordInOperand(1))) {
return true;
}
@@ -2036,9 +2249,9 @@
return spvOpcodeIsConstant(inst.opcode());
};
- PoolPotentialIds(src_->types_values(), potential_id_map.src_ids,
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
accept_type_ops, get_result_id);
- PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids,
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
accept_type_ops, get_result_id);
// Then match the ids. Constants are matched exactly, except for float types
@@ -2115,9 +2328,9 @@
return inst.opcode() == SpvOpVariable;
};
- PoolPotentialIds(src_->types_values(), potential_id_map.src_ids,
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
accept_type_ops, get_result_id);
- PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids,
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
accept_type_ops, get_result_id);
// Then match the ids. Start with exact matches, then match the leftover with
@@ -2148,49 +2361,31 @@
}
// Base the matching of functions on debug info when available.
- IdGroupMapByName src_func_groups;
- IdGroupMapByName dst_func_groups;
+ GroupIdsAndMatch<std::string>(
+ src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName,
+ [this](const IdGroup& src_group, const IdGroup& dst_group) {
- GroupIdsByName(src_func_ids, true, &src_func_groups);
- GroupIdsByName(dst_func_ids, false, &dst_func_groups);
+ // If there is a single function with this name in src and dst, it's a
+ // definite match.
+ if (src_group.size() == 1 && dst_group.size() == 1) {
+ id_map_.MapIds(src_group[0], dst_group[0]);
+ return;
+ }
- // Match functions with identical names.
- for (const auto& src_func_group : src_func_groups) {
- const std::string& name = src_func_group.first;
- const IdGroup& src_group = src_func_group.second;
+ // If there are multiple functions with the same name, group them by
+ // type, and match only if the types match (and are unique).
+ GroupIdsAndMatch<uint32_t>(src_group, dst_group, 0,
+ &Differ::GroupIdsHelperGetTypeId,
+ [this](const IdGroup& src_group_by_type_id,
+ const IdGroup& dst_group_by_type_id) {
- if (name == "") {
- continue;
- }
-
- const IdGroup& dst_group = dst_func_groups[name];
-
- // If there is a single function with this name in src and dst, it's a
- // definite match.
- if (src_group.size() == 1 && dst_group.size() == 1) {
- id_map_.MapIds(src_group[0], dst_group[0]);
- continue;
- }
-
- // If there are multiple functions with the same name, group them by type,
- // and match only if the types match (and are unique).
- IdGroupMapByTypeId src_func_groups_by_type_id;
- IdGroupMapByTypeId dst_func_groups_by_type_id;
-
- GroupIdsByTypeId(src_group, true, &src_func_groups_by_type_id);
- GroupIdsByTypeId(dst_group, false, &dst_func_groups_by_type_id);
-
- for (const auto& src_func_group_by_type_id : src_func_groups_by_type_id) {
- const uint32_t type_id = src_func_group_by_type_id.first;
- const IdGroup& src_group_by_type_id = src_func_group_by_type_id.second;
- const IdGroup& dst_group_by_type_id = dst_func_groups_by_type_id[type_id];
-
- if (src_group_by_type_id.size() == 1 &&
- dst_group_by_type_id.size() == 1) {
- id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
- }
- }
- }
+ if (src_group_by_type_id.size() == 1 &&
+ dst_group_by_type_id.size() == 1) {
+ id_map_.MapIds(src_group_by_type_id[0],
+ dst_group_by_type_id[0]);
+ }
+ });
+ });
// Any functions that are left are pooled together and matched as if unnamed,
// with the only exception that two functions with mismatching names are not
@@ -2224,20 +2419,14 @@
}
// Best effort match functions with matching type.
- IdGroupMapByTypeId src_func_groups_by_type_id;
- IdGroupMapByTypeId dst_func_groups_by_type_id;
+ GroupIdsAndMatch<uint32_t>(
+ src_func_ids, dst_func_ids, 0, &Differ::GroupIdsHelperGetTypeId,
+ [this](const IdGroup& src_group_by_type_id,
+ const IdGroup& dst_group_by_type_id) {
- GroupIdsByTypeId(src_func_ids, true, &src_func_groups_by_type_id);
- GroupIdsByTypeId(dst_func_ids, false, &dst_func_groups_by_type_id);
-
- for (const auto& src_func_group_by_type_id : src_func_groups_by_type_id) {
- const uint32_t type_id = src_func_group_by_type_id.first;
- const IdGroup& src_group_by_type_id = src_func_group_by_type_id.second;
- const IdGroup& dst_group_by_type_id = dst_func_groups_by_type_id[type_id];
-
- BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id,
- src_func_insts_, dst_func_insts_);
- }
+ BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id,
+ src_func_insts_, dst_func_insts_);
+ });
// Any function that's left, best effort match them.
BestEffortMatchFunctions(src_func_ids, dst_func_ids, src_func_insts_,
@@ -2400,9 +2589,10 @@
parsed_inst->opcode = static_cast<uint16_t>(inst.opcode());
parsed_inst->ext_inst_type =
inst.opcode() == SpvOpExtInst
- ? GetExtInstType(id_to, original_inst.GetInOperand(0).AsId())
+ ? GetExtInstType(id_to, original_inst.GetSingleWordInOperand(0))
: SPV_EXT_INST_TYPE_NONE;
- parsed_inst->type_id = inst.HasResultType() ? inst.GetOperand(0).AsId() : 0;
+ parsed_inst->type_id =
+ inst.HasResultType() ? inst.GetSingleWordOperand(0) : 0;
parsed_inst->result_id = inst.HasResultId() ? inst.result_id() : 0;
parsed_inst->operands = parsed_operands.data();
parsed_inst->num_operands = static_cast<uint16_t>(parsed_operands.size());
@@ -2642,6 +2832,7 @@
differ.MatchMemoryModel();
differ.MatchEntryPointIds();
differ.MatchExecutionModes();
+ differ.MatchTypeForwardPointers();
differ.MatchTypeIds();
differ.MatchConstants();
differ.MatchVariableIds();
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 9827c53..0473752 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -967,6 +967,7 @@
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
});
}
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index d2059f5..0c6d0c2 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -434,6 +434,7 @@
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
});
}
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index f48c56a..33c8bdf 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -286,6 +286,7 @@
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
});
}
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 123d03b..f22b191 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -139,6 +139,7 @@
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
});
}
bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp
index 17a4c72..a1d3432 100644
--- a/source/opt/spread_volatile_semantics.cpp
+++ b/source/opt/spread_volatile_semantics.cpp
@@ -92,6 +92,10 @@
} // namespace
Pass::Status SpreadVolatileSemantics::Process() {
+ if (HasNoExecutionModel()) {
+ return Status::SuccessWithoutChange;
+ }
+
if (!HasOnlyEntryPointsAsFunctions(context(), get_module())) {
return Status::Failure;
}
diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h
index 3d0a183..531a21d 100644
--- a/source/opt/spread_volatile_semantics.h
+++ b/source/opt/spread_volatile_semantics.h
@@ -35,6 +35,13 @@
}
private:
+ // Returns true if it does not have an execution model. Linkage shaders do not
+ // have an execution model.
+ bool HasNoExecutionModel() {
+ return get_module()->entry_points().empty() &&
+ context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage);
+ }
+
// Iterates interface variables and spreads the Volatile semantics if it has
// load instructions for the Volatile semantics.
Pass::Status SpreadVolatileSemanticsToVariables(
diff --git a/source/text.cpp b/source/text.cpp
index 97d00cb..90f69c5 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -623,7 +623,8 @@
break;
} else {
return context->diagnostic()
- << "Expected operand, found end of stream.";
+ << "Expected operand for " << opcodeName
+ << " instruction, but found the end of the stream.";
}
}
assert(error == SPV_SUCCESS && "Somebody added another way to fail");
@@ -633,7 +634,8 @@
break;
} else {
return context->diagnostic()
- << "Expected operand, found next instruction instead.";
+ << "Expected operand for " << opcodeName
+ << " instruction, but found the next instruction instead.";
}
}
@@ -667,7 +669,7 @@
if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
return context->diagnostic()
- << "Instruction too long: " << pInst->words.size()
+ << opcodeName << " Instruction too long: " << pInst->words.size()
<< " words, but the limit is "
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX;
}
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index a27cf16..bef7ef9 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -334,7 +334,7 @@
sc != SpvStorageClassIncomingCallableDataKHR &&
sc != SpvStorageClassShaderRecordBufferKHR) {
return _.diag(SPV_ERROR_INVALID_ID, target)
- << LogStringForDecoration(dec)
+ << _.VkErrorID(6672) << LogStringForDecoration(dec)
<< " decoration must not be applied to this storage class";
}
break;
@@ -355,7 +355,7 @@
break;
case SpvDecorationInputAttachmentIndex:
if (sc != SpvStorageClassUniformConstant) {
- return fail(0) << "must be in the UniformConstant storage class";
+ return fail(6678) << "must be in the UniformConstant storage class";
}
break;
case SpvDecorationFlat:
diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp
index d46b3fc..e6e97c4 100644
--- a/source/val/validate_bitwise.cpp
+++ b/source/val/validate_bitwise.cpp
@@ -14,16 +14,48 @@
// Validates correctness of bitwise instructions.
-#include "source/val/validate.h"
-
#include "source/diagnostic.h"
#include "source/opcode.h"
+#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
+#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
namespace val {
+// Validates when base and result need to be the same type
+spv_result_t ValidateBaseType(ValidationState_t& _, const Instruction* inst,
+ const uint32_t base_type) {
+ const SpvOp opcode = inst->opcode();
+
+ if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(4781)
+ << "Expected int scalar or vector type for Base operand: "
+ << spvOpcodeString(opcode);
+ }
+
+ // Vulkan has a restriction to 32 bit for base
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (_.GetBitWidth(base_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(4781)
+ << "Expected 32-bit int type for Base operand: "
+ << spvOpcodeString(opcode);
+ }
+ }
+
+ // OpBitCount just needs same number of components
+ if (base_type != inst->type_id() && opcode != SpvOpBitCount) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Base Type to be equal to Result Type: "
+ << spvOpcodeString(opcode);
+ }
+
+ return SPV_SUCCESS;
+}
+
// Validates correctness of bitwise instructions.
spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
const SpvOp opcode = inst->opcode();
@@ -109,20 +141,14 @@
}
case SpvOpBitFieldInsert: {
- if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected int scalar or vector type as Result Type: "
- << spvOpcodeString(opcode);
-
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
const uint32_t insert_type = _.GetOperandTypeId(inst, 3);
const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
const uint32_t count_type = _.GetOperandTypeId(inst, 5);
- if (base_type != result_type)
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be equal to Result Type: "
- << spvOpcodeString(opcode);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
if (insert_type != result_type)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -143,19 +169,13 @@
case SpvOpBitFieldSExtract:
case SpvOpBitFieldUExtract: {
- if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected int scalar or vector type as Result Type: "
- << spvOpcodeString(opcode);
-
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
const uint32_t offset_type = _.GetOperandTypeId(inst, 3);
const uint32_t count_type = _.GetOperandTypeId(inst, 4);
- if (base_type != result_type)
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be equal to Result Type: "
- << spvOpcodeString(opcode);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
if (!offset_type || !_.IsIntScalarType(offset_type))
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -170,17 +190,12 @@
}
case SpvOpBitReverse: {
- if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected int scalar or vector type as Result Type: "
- << spvOpcodeString(opcode);
-
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
- if (base_type != result_type)
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be equal to Result Type: "
- << spvOpcodeString(opcode);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
+
break;
}
@@ -191,15 +206,13 @@
<< spvOpcodeString(opcode);
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
- if (!base_type ||
- (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be int scalar or vector: "
- << spvOpcodeString(opcode);
-
const uint32_t base_dimension = _.GetDimension(base_type);
const uint32_t result_dimension = _.GetDimension(result_type);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
+
if (base_dimension != result_dimension)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected Base dimension to be equal to Result Type "
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 88abd75..dd605d2 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -55,8 +55,7 @@
}
if (_.IsPointerType(inst->type_id()) &&
_.addressing_model() == SpvAddressingModelLogical) {
- if (!_.features().variable_pointers &&
- !_.features().variable_pointers_storage_buffer) {
+ if (!_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Using pointers with OpPhi requires capability "
<< "VariablePointers or VariablePointersStorageBuffer";
@@ -249,13 +248,9 @@
<< _.getIdName(value->type_id()) << "' is missing or void.";
}
- const bool uses_variable_pointer =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
-
if (_.addressing_model() == SpvAddressingModelLogical &&
- SpvOpTypePointer == value_type->opcode() && !uses_variable_pointer &&
- !_.options()->relax_logical_pointer) {
+ SpvOpTypePointer == value_type->opcode() &&
+ !_.features().variable_pointers && !_.options()->relax_logical_pointer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpReturnValue value's type <id> '"
<< _.getIdName(value->type_id())
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index e712936..73d512a 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -956,41 +956,41 @@
const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer;
if (spvIsVulkanEnv(vstate.context()->target_env)) {
- // Vulkan 14.5.1: There must be no more than one PushConstant block
- // per entry point.
+ // Vulkan: There must be no more than one PushConstant block per entry
+ // point.
if (push_constant) {
auto entry_points = vstate.EntryPointReferences(var_id);
for (auto ep_id : entry_points) {
const bool already_used = !uses_push_constant.insert(ep_id).second;
if (already_used) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "Entry point id '" << ep_id
+ << vstate.VkErrorID(6674) << "Entry point id '" << ep_id
<< "' uses more than one PushConstant interface.\n"
- << "From Vulkan spec, section 14.5.1:\n"
+ << "From Vulkan spec:\n"
<< "There must be no more than one push constant block "
<< "statically used per shader entry point.";
}
}
}
- // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
+ // Vulkan: Check DescriptorSet and Binding decoration for
// UniformConstant which cannot be a struct.
if (uniform_constant) {
auto entry_points = vstate.EntryPointReferences(var_id);
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "UniformConstant id '" << var_id
+ << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
<< "' is missing DescriptorSet decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "UniformConstant id '" << var_id
+ << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
<< "' is missing Binding decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
@@ -1051,55 +1051,55 @@
hasDecoration(id, SpvDecorationBufferBlock, vstate);
if (storage_buffer && buffer_block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "Storage buffer id '" << var_id
+ << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id
<< " In Vulkan, BufferBlock is disallowed on variables in "
"the StorageBuffer storage class";
}
- // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform
+ // Vulkan: Check Block decoration for PushConstant, Uniform
// and StorageBuffer variables. Uniform can also use BufferBlock.
if (push_constant && !block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "PushConstant id '" << id
+ << vstate.VkErrorID(6675) << "PushConstant id '" << id
<< "' is missing Block decoration.\n"
- << "From Vulkan spec, section 14.5.1:\n"
+ << "From Vulkan spec:\n"
<< "Such variables must be identified with a Block "
"decoration";
}
if (storage_buffer && !block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "StorageBuffer id '" << id
+ << vstate.VkErrorID(6675) << "StorageBuffer id '" << id
<< "' is missing Block decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "Such variables must be identified with a Block "
"decoration";
}
if (uniform && !block && !buffer_block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "Uniform id '" << id
+ << vstate.VkErrorID(6676) << "Uniform id '" << id
<< "' is missing Block or BufferBlock decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "Such variables must be identified with a Block or "
"BufferBlock decoration";
}
- // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
+ // Vulkan: Check DescriptorSet and Binding decoration for
// Uniform and StorageBuffer variables.
if (uniform || storage_buffer) {
auto entry_points = vstate.EntryPointReferences(var_id);
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << sc_str << " id '" << var_id
+ << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
<< "' is missing DescriptorSet decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << sc_str << " id '" << var_id
+ << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
<< "' is missing Binding decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index 656893f..2a5fed8 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -300,7 +300,7 @@
// These are always allowed.
break;
case SpvStorageClassStorageBuffer:
- if (!_.features().variable_pointers_storage_buffer) {
+ if (!_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "StorageBuffer pointer operand "
<< _.getIdName(argument_id)
@@ -316,11 +316,10 @@
// Validate memory object declaration requirements.
if (argument->opcode() != SpvOpVariable &&
argument->opcode() != SpvOpFunctionParameter) {
- const bool ssbo_vptr =
- _.features().variable_pointers_storage_buffer &&
- sc == SpvStorageClassStorageBuffer;
- const bool wg_vptr =
- _.features().variable_pointers && sc == SpvStorageClassWorkgroup;
+ const bool ssbo_vptr = _.features().variable_pointers &&
+ sc == SpvStorageClassStorageBuffer;
+ const bool wg_vptr = _.HasCapability(SpvCapabilityVariablePointers) &&
+ sc == SpvStorageClassWorkgroup;
const bool uc_ptr = sc == SpvStorageClassUniformConstant;
if (!ssbo_vptr && !wg_vptr && !uc_ptr) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index e9164af..f6d7d10 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -980,8 +980,9 @@
if (spvIsVulkanEnv(_.context()->target_env)) {
if (info.sampled != 1) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'Sampled' parameter to be 1 "
- << "for Vulkan environment.";
+ << _.VkErrorID(6671)
+ << "Expected Image 'Sampled' parameter to be 1 for Vulkan "
+ "environment.";
}
} else {
if (info.sampled != 0 && info.sampled != 1) {
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp
index bb35f55..5307988 100644
--- a/source/val/validate_logicals.cpp
+++ b/source/val/validate_logicals.cpp
@@ -163,8 +163,7 @@
switch (type_opcode) {
case SpvOpTypePointer: {
if (_.addressing_model() == SpvAddressingModelLogical &&
- !_.features().variable_pointers &&
- !_.features().variable_pointers_storage_buffer)
+ !_.features().variable_pointers)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Using pointers with OpSelect requires capability "
<< "VariablePointers or VariablePointersStorageBuffer";
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 0b23126..af9da67 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -870,17 +870,14 @@
<< "' is not defined.";
}
- const bool uses_variable_pointers =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
const auto pointer_index = 2;
const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
const auto pointer = _.FindDef(pointer_id);
if (!pointer ||
((_.addressing_model() == SpvAddressingModelLogical) &&
- ((!uses_variable_pointers &&
+ ((!_.features().variable_pointers &&
!spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
- (uses_variable_pointers &&
+ (_.features().variable_pointers &&
!spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpLoad Pointer <id> '" << _.getIdName(pointer_id)
@@ -926,17 +923,14 @@
}
spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
- const bool uses_variable_pointer =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
const auto pointer_index = 0;
const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
const auto pointer = _.FindDef(pointer_id);
if (!pointer ||
(_.addressing_model() == SpvAddressingModelLogical &&
- ((!uses_variable_pointer &&
+ ((!_.features().variable_pointers &&
!spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
- (uses_variable_pointer &&
+ (_.features().variable_pointers &&
!spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpStore Pointer <id> '" << _.getIdName(pointer_id)
@@ -1362,8 +1356,7 @@
spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
const Instruction* inst) {
if (_.addressing_model() == SpvAddressingModelLogical) {
- if (!_.features().variable_pointers &&
- !_.features().variable_pointers_storage_buffer) {
+ if (!_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Generating variable pointers requires capability "
<< "VariablePointers or VariablePointersStorageBuffer";
@@ -1481,18 +1474,15 @@
}
}
- const bool uses_variable_pointers =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
const auto pointer_index =
(inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 2u : 0u;
const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
const auto pointer = _.FindDef(pointer_id);
if (!pointer ||
((_.addressing_model() == SpvAddressingModelLogical) &&
- ((!uses_variable_pointers &&
+ ((!_.features().variable_pointers &&
!spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
- (uses_variable_pointers &&
+ (_.features().variable_pointers &&
!spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< opname << " Pointer <id> '" << _.getIdName(pointer_id)
@@ -1564,10 +1554,10 @@
spv_result_t ValidatePtrComparison(ValidationState_t& _,
const Instruction* inst) {
if (_.addressing_model() == SpvAddressingModelLogical &&
- !_.features().variable_pointers_storage_buffer) {
+ !_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Instruction cannot be used without a variable pointers "
- "capability";
+ << "Instruction cannot for logical addressing model be used without "
+ "a variable pointers capability";
}
const auto result_type = _.FindDef(inst->type_id());
@@ -1602,7 +1592,8 @@
<< "Invalid pointer storage class";
}
- if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) {
+ if (sc == SpvStorageClassWorkgroup &&
+ !_.HasCapability(SpvCapabilityVariablePointers)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Workgroup storage class pointer requires VariablePointers "
"capability to be specified";
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 65c1dd6..9aa6c63 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -392,11 +392,8 @@
features_.free_fp_rounding_mode = true;
break;
case SpvCapabilityVariablePointers:
- features_.variable_pointers = true;
- features_.variable_pointers_storage_buffer = true;
- break;
case SpvCapabilityVariablePointersStorageBuffer:
- features_.variable_pointers_storage_buffer = true;
+ features_.variable_pointers = true;
break;
default:
// TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses
@@ -541,7 +538,7 @@
if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst));
// Some validation checks are easier by getting all the consumers
- for (uint16_t i = 0; i < inst->operands().size(); ++i) {
+ for (size_t i = 0; i < inst->operands().size(); ++i) {
const spv_parsed_operand_t& operand = inst->operand(i);
if ((SPV_OPERAND_TYPE_ID == operand.type) ||
(SPV_OPERAND_TYPE_TYPE_ID == operand.type)) {
@@ -1892,6 +1889,8 @@
return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
case 4780:
return VUID_WRAP(VUID-StandaloneSpirv-Result-04780);
+ case 4781:
+ return VUID_WRAP(VUID-StandaloneSpirv-Base-04781);
case 4915:
return VUID_WRAP(VUID-StandaloneSpirv-Location-04915);
case 4916:
@@ -1906,6 +1905,20 @@
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
case 6491:
return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491);
+ case 6671:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671);
+ case 6672:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-06672);
+ case 6674:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674);
+ case 6675:
+ return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06675);
+ case 6676:
+ return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06676);
+ case 6677:
+ return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677);
+ case 6678:
+ return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678);
default:
return ""; // unknown id
}
@@ -1913,4 +1926,4 @@
}
} // namespace val
-} // namespace spvtools
+} // namespace spvtools
\ No newline at end of file
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 89834a0..4888840 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -70,11 +70,9 @@
// and its values to be used without
// requiring any capability
- // Allow functionalities enabled by VariablePointers capability.
+ // Allow functionalities enabled by VariablePointers or
+ // VariablePointersStorageBuffer capability.
bool variable_pointers = false;
- // Allow functionalities enabled by VariablePointersStorageBuffer
- // capability.
- bool variable_pointers_storage_buffer = false;
// Permit group oerations Reduce, InclusiveScan, ExclusiveScan
bool group_ops_reduce_and_scans = false;
diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp
index 1562057..4424d7f 100644
--- a/test/c_interface_test.cpp
+++ b/test/c_interface_test.cpp
@@ -122,7 +122,10 @@
EXPECT_EQ(1u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(12u, position.index);
- EXPECT_STREQ("Expected operand, found end of stream.", message);
+ EXPECT_STREQ(
+ "Expected operand for OpName instruction, but found the end of the "
+ "stream.",
+ message);
});
spv_binary binary = nullptr;
@@ -228,7 +231,10 @@
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
&diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
- EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error);
+ EXPECT_STREQ(
+ "Expected operand for OpName instruction, but found the end of the "
+ "stream.",
+ diagnostic->error);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp
new file mode 100644
index 0000000..af252b1
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp
@@ -0,0 +1,136 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Basic test that OpTypeForwardPointer is matched
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpName %structptr2 "structptr2"
+ OpTypeForwardPointer %structptr UniformConstant
+ OpTypeForwardPointer %structptr2 Function
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+ %structptr2 = OpTypePointer Function %structt1
+)";
+
+TEST(DiffTest, OptypeforwardpointerBasic) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 7
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %1 "structptr"
++OpName %7 "structptr2"
+ OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %7 Function
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
++%7 = OpTypePointer Function %3
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerBasicNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ OpTypeForwardPointer %structptr2 Function
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+ %structptr2 = OpTypePointer Function %structt1
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 7
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %7 Function
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
++%7 = OpTypePointer Function %3
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm
new file mode 100644
index 0000000..0c6e0cb
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm
@@ -0,0 +1,15 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpName %structptr2 "structptr2"
+ OpTypeForwardPointer %structptr UniformConstant
+ OpTypeForwardPointer %structptr2 Function
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+ %structptr2 = OpTypePointer Function %structt1
diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm
new file mode 100644
index 0000000..408ec98
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm
@@ -0,0 +1,13 @@
+;; Basic test that OpTypeForwardPointer is matched
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp
new file mode 100644
index 0000000..f2c9008
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp
@@ -0,0 +1,138 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded types whose declarations are intertwined match
+// correctly
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ OpTypeForwardPointer %Bptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %B = OpTypeStruct %uint %Aptr %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Bptr UniformConstant
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %B = OpTypeStruct %uint %Aptr %Bptr %uint
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
+)";
+
+TEST(DiffTest, OptypeforwardpointerIntertwined) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 7
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %1 "Aptr"
+ OpName %2 "Bptr"
+ OpTypeForwardPointer %1 UniformConstant
+ OpTypeForwardPointer %2 UniformConstant
+ %3 = OpTypeInt 32 0
++%6 = OpTypeStruct %3 %1 %2 %3
+ %4 = OpTypeStruct %1 %3 %2
+-%5 = OpTypeStruct %3 %1 %2
+ %1 = OpTypePointer UniformConstant %4
+-%2 = OpTypePointer UniformConstant %5
++%2 = OpTypePointer UniformConstant %6
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerIntertwinedNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ OpTypeForwardPointer %Bptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %B = OpTypeStruct %uint %Aptr %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Bptr UniformConstant
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %B = OpTypeStruct %uint %Aptr %Bptr %uint
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 10
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpTypeForwardPointer %1 UniformConstant
+-OpTypeForwardPointer %2 UniformConstant
++OpTypeForwardPointer %6 UniformConstant
++OpTypeForwardPointer %7 UniformConstant
+ %3 = OpTypeInt 32 0
+-%4 = OpTypeStruct %1 %3 %2
+-%5 = OpTypeStruct %3 %1 %2
+-%1 = OpTypePointer UniformConstant %4
+-%2 = OpTypePointer UniformConstant %5
++%8 = OpTypeStruct %3 %7 %6 %3
++%9 = OpTypeStruct %7 %3 %6
++%7 = OpTypePointer UniformConstant %9
++%6 = OpTypePointer UniformConstant %8
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm
new file mode 100644
index 0000000..bd73501
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm
@@ -0,0 +1,13 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Bptr UniformConstant
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %B = OpTypeStruct %uint %Aptr %Bptr %uint
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm
new file mode 100644
index 0000000..8fdaf28
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm
@@ -0,0 +1,15 @@
+;; Tests that two forwarded types whose declarations are intertwined match
+;; correctly
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ OpTypeForwardPointer %Bptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %B = OpTypeStruct %uint %Aptr %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp
new file mode 100644
index 0000000..0a59be3
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp
@@ -0,0 +1,116 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded type pointers with mismatching storage classes
+// aren't matched
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr Function
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer Function %A
+)";
+
+TEST(DiffTest, OptypeforwardpointerMismatchingClass) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 6
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpName %1 "Aptr"
++OpName %4 "Aptr"
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 Function
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%5 = OpTypeStruct %4 %2
++%4 = OpTypePointer Function %5
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerMismatchingClassNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr Function
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer Function %A
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 6
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 Function
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%5 = OpTypeStruct %4 %2
++%4 = OpTypePointer Function %5
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm
new file mode 100644
index 0000000..e874a0c
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm
@@ -0,0 +1,9 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr Function
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer Function %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm
new file mode 100644
index 0000000..8a33933
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm
@@ -0,0 +1,11 @@
+;; Tests that two forwarded type pointers with mismatching storage classes
+;; aren't matched
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp
new file mode 100644
index 0000000..0067cdf
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp
@@ -0,0 +1,111 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded type pointers with mismatching types aren't matched
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %Aptr = OpTypePointer UniformConstant %uint
+)";
+
+TEST(DiffTest, OptypeforwardpointerMismatchingType) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 5
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpName %1 "Aptr"
++OpName %4 "Aptr"
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 UniformConstant
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%4 = OpTypePointer UniformConstant %2
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerMismatchingTypeNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %Aptr = OpTypePointer UniformConstant %uint
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 5
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 UniformConstant
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%4 = OpTypePointer UniformConstant %2
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm
new file mode 100644
index 0000000..ee3d35c
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm
@@ -0,0 +1,8 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %Aptr = OpTypePointer UniformConstant %uint
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm
new file mode 100644
index 0000000..a4596a0
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm
@@ -0,0 +1,10 @@
+;; Tests that two forwarded type pointers with mismatching types aren't matched
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp
new file mode 100644
index 0000000..d66c28a
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp
@@ -0,0 +1,127 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded declarations match even if the type pointer is used
+// in a nested struct declaration, and in multiple places
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr %uint
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+
+TEST(DiffTest, OptypeforwardpointerNested) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %1 "Aptr"
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2 %1
+-%4 = OpTypeStruct %3 %1 %2
+-%5 = OpTypeStruct %4 %3 %4
++%6 = OpTypeStruct %3 %1
++%7 = OpTypeStruct %6 %3 %6
+-%1 = OpTypePointer UniformConstant %5
++%1 = OpTypePointer UniformConstant %7
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerNestedNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr %uint
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2 %1
+-%4 = OpTypeStruct %3 %1 %2
+-%5 = OpTypeStruct %4 %3 %4
++%6 = OpTypeStruct %3 %1
++%7 = OpTypeStruct %6 %3 %6
+-%1 = OpTypePointer UniformConstant %5
++%1 = OpTypePointer UniformConstant %7
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm
new file mode 100644
index 0000000..e248355
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm
@@ -0,0 +1,11 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm
new file mode 100644
index 0000000..035410e
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm
@@ -0,0 +1,13 @@
+;; Tests that two forwarded declarations match even if the type pointer is used
+;; in a nested struct declaration, and in multiple places
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr %uint
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp
new file mode 100644
index 0000000..df86fef
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp
@@ -0,0 +1,124 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug
+// info
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+
+TEST(DiffTest, OptypeforwardpointerOnesidedDebug) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 7
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpName %1 "structptr"
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerOnesidedDebugNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 7
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm
new file mode 100644
index 0000000..7e25710
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm
@@ -0,0 +1,11 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm
new file mode 100644
index 0000000..e949b27
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm
@@ -0,0 +1,14 @@
+;; Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug
+;; info
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake
index f472480..c64eaab 100644
--- a/test/diff/diff_files/diff_test_files_autogen.cmake
+++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -18,6 +18,12 @@
list(APPEND DIFF_TEST_FILES
"diff_files/OpExtInst_in_dst_only_autogen.cpp"
"diff_files/OpExtInst_in_src_only_autogen.cpp"
+"diff_files/OpTypeForwardPointer_basic_autogen.cpp"
+"diff_files/OpTypeForwardPointer_intertwined_autogen.cpp"
+"diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp"
+"diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp"
+"diff_files/OpTypeForwardPointer_nested_autogen.cpp"
+"diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp"
"diff_files/basic_autogen.cpp"
"diff_files/constant_array_size_autogen.cpp"
"diff_files/different_decorations_fragment_autogen.cpp"
diff --git a/test/diff/diff_files/generate_tests.py b/test/diff/diff_files/generate_tests.py
index 1c380c9..cc3175d 100755
--- a/test/diff/diff_files/generate_tests.py
+++ b/test/diff/diff_files/generate_tests.py
@@ -114,7 +114,7 @@
(in_basename, in_ext) = os.path.splitext(in_path)
out_name = in_basename + '_no_dbg' + in_ext
out_path = os.path.join(tmp_dir, out_name)
-
+
with open(in_path, 'r') as fin:
with open(out_path, 'w') as fout:
for line in fin:
diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp
index 83b2dcf..fdabd92 100644
--- a/test/opt/spread_volatile_semantics_test.cpp
+++ b/test/opt/spread_volatile_semantics_test.cpp
@@ -1113,6 +1113,26 @@
SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
}
+TEST_F(VolatileSpreadTest, SkipIfItHasNoExecutionModel) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ Pass::Status status;
+ std::tie(std::ignore, status) =
+ SinglePassRunToBinary<SpreadVolatileSemantics>(text,
+ /* skip_nop = */ false);
+ EXPECT_EQ(status, Pass::Status::SuccessWithoutChange);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/text_to_binary.annotation_test.cpp b/test/text_to_binary.annotation_test.cpp
index 61bdf64..76776de 100644
--- a/test/text_to_binary.annotation_test.cpp
+++ b/test/text_to_binary.annotation_test.cpp
@@ -398,7 +398,8 @@
TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) {
EXPECT_THAT(CompileFailure("OpGroupMemberDecorate"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGroupMemberDecorate instruction, but "
+ "found the end of the stream."));
}
TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidGroupId) {
@@ -413,7 +414,8 @@
TEST_F(TextToBinaryTest, GroupMemberDecorateMissingTargetMemberNumber) {
EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGroupMemberDecorate instruction, but "
+ "found the end of the stream."));
}
TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetMemberNumber) {
@@ -428,7 +430,8 @@
TEST_F(TextToBinaryTest, GroupMemberDecorateMissingSecondTargetMemberNumber) {
EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGroupMemberDecorate instruction, but "
+ "found the end of the stream."));
}
TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetMemberNumber) {
diff --git a/test/text_to_binary.barrier_test.cpp b/test/text_to_binary.barrier_test.cpp
index 545d26f..f1cb4fbe 100644
--- a/test/text_to_binary.barrier_test.cpp
+++ b/test/text_to_binary.barrier_test.cpp
@@ -44,7 +44,8 @@
TEST_F(OpMemoryBarrier, BadMissingScopeId) {
const std::string input = "OpMemoryBarrier\n";
EXPECT_THAT(CompileFailure(input),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryBarrier instruction, but found "
+ "the end of the stream."));
}
TEST_F(OpMemoryBarrier, BadInvalidScopeId) {
@@ -55,7 +56,8 @@
TEST_F(OpMemoryBarrier, BadMissingMemorySemanticsId) {
const std::string input = "OpMemoryBarrier %scope\n";
EXPECT_THAT(CompileFailure(input),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryBarrier instruction, but found "
+ "the end of the stream."));
}
TEST_F(OpMemoryBarrier, BadInvalidMemorySemanticsId) {
@@ -92,13 +94,16 @@
TEST_F(NamedMemoryBarrierTest, ArgumentCount) {
EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryNamedBarrier instruction, but "
+ "found the end of the stream."));
EXPECT_THAT(
CompileFailure("OpMemoryNamedBarrier %bar", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the "
+ "end of the stream."));
EXPECT_THAT(
CompileFailure("OpMemoryNamedBarrier %bar %scope", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the "
+ "end of the stream."));
EXPECT_THAT(
CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics",
SPV_ENV_UNIVERSAL_1_1),
@@ -151,10 +156,12 @@
TEST_F(NamedBarrierInitializeTest, ArgumentCount) {
EXPECT_THAT(
CompileFailure("%bar = OpNamedBarrierInitialize", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpNamedBarrierInitialize instruction, but found "
+ "the end of the stream."));
EXPECT_THAT(CompileFailure("%bar = OpNamedBarrierInitialize %ype",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpNamedBarrierInitialize instruction, "
+ "but found the end of the stream."));
EXPECT_THAT(
CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count",
SPV_ENV_UNIVERSAL_1_1),
diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp
index abae6a2..472cb6d 100644
--- a/test/text_to_binary.control_flow_test.cpp
+++ b/test/text_to_binary.control_flow_test.cpp
@@ -163,7 +163,8 @@
TEST_F(TextToBinaryTest, SwitchBadMissingSelector) {
EXPECT_THAT(CompileFailure("OpSwitch"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpSwitch instruction, but found the end "
+ "of the stream."));
}
TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) {
@@ -173,7 +174,8 @@
TEST_F(TextToBinaryTest, SwitchBadMissingDefault) {
EXPECT_THAT(CompileFailure("OpSwitch %selector"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpSwitch instruction, but found the end "
+ "of the stream."));
}
TEST_F(TextToBinaryTest, SwitchBadInvalidDefault) {
@@ -195,7 +197,8 @@
EXPECT_THAT(CompileFailure("%1 = OpTypeInt 32 0\n"
"%2 = OpConstant %1 52\n"
"OpSwitch %2 %default 12"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpSwitch instruction, but found the end "
+ "of the stream."));
}
// A test case for an OpSwitch.
diff --git a/test/text_to_binary.device_side_enqueue_test.cpp b/test/text_to_binary.device_side_enqueue_test.cpp
index 03d7e74..2f4dd70 100644
--- a/test/text_to_binary.device_side_enqueue_test.cpp
+++ b/test/text_to_binary.device_side_enqueue_test.cpp
@@ -83,7 +83,8 @@
CompileFailure(
"%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events"
" %wait_events %ret_event %invoke %param %param_size"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpEnqueueKernel instruction, but found the end "
+ "of the stream."));
}
TEST_F(OpKernelEnqueueBad, InvalidLastOperand) {
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index e5f152e..3a592a0 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -1034,5 +1034,46 @@
{SpvCapabilityBitInstructions})},
})));
+// SPV_KHR_uniform_group_instructions
+
+INSTANTIATE_TEST_SUITE_P(
+ SPV_KHR_uniform_group_instructions, ExtensionRoundTripTest,
+ Combine(
+ Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5,
+ SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1,
+ SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3),
+ ValuesIn(std::vector<AssemblyCase>{
+ {"OpExtension \"SPV_KHR_uniform_group_instructions\"\n",
+ MakeInstruction(SpvOpExtension,
+ MakeVector("SPV_KHR_uniform_group_instructions"))},
+ {"OpCapability GroupUniformArithmeticKHR\n",
+ MakeInstruction(SpvOpCapability,
+ {SpvCapabilityGroupUniformArithmeticKHR})},
+ {"%2 = OpGroupIMulKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupIMulKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupFMulKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupFMulKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupBitwiseAndKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupBitwiseAndKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupBitwiseOrKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupBitwiseOrKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupBitwiseXorKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupBitwiseXorKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupLogicalAndKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupLogicalAndKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupLogicalOrKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupLogicalOrKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupLogicalXorKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupLogicalXorKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ })));
+
} // namespace
} // namespace spvtools
diff --git a/test/text_to_binary.image_test.cpp b/test/text_to_binary.image_test.cpp
index d445369..8d8ff43 100644
--- a/test/text_to_binary.image_test.cpp
+++ b/test/text_to_binary.image_test.cpp
@@ -123,7 +123,8 @@
TEST_F(OpImageTest, MissingSampledImageOperand) {
EXPECT_THAT(CompileFailure("%2 = OpImage %1"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpImage instruction, but found the end "
+ "of the stream."));
}
TEST_F(OpImageTest, InvalidSampledImageOperand) {
@@ -222,7 +223,8 @@
TEST_F(OpImageSparseReadTest, MissingImageOperand) {
EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpImageSparseRead instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpImageSparseReadTest, InvalidImageOperand) {
@@ -232,7 +234,8 @@
TEST_F(OpImageSparseReadTest, MissingCoordinateOperand) {
EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 %2"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpImageSparseRead instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpImageSparseReadTest, InvalidCoordinateOperand) {
diff --git a/test/text_to_binary.memory_test.cpp b/test/text_to_binary.memory_test.cpp
index 7b09ed5..f94c134 100644
--- a/test/text_to_binary.memory_test.cpp
+++ b/test/text_to_binary.memory_test.cpp
@@ -166,7 +166,8 @@
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) {
std::string spirv = "OpCopyMemory %1\n";
std::string err = CompileFailure(spirv);
- EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
+ EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, "
+ "but found the end of the stream."));
}
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) {
@@ -295,7 +296,8 @@
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) {
std::string spirv = "OpCopyMemorySized %1 %2\n";
std::string err = CompileFailure(spirv);
- EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
+ EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized "
+ "instruction, but found the end of the stream."));
}
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) {
diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp
index 647bb3d..7f15c8b 100644
--- a/test/text_to_binary.mode_setting_test.cpp
+++ b/test/text_to_binary.mode_setting_test.cpp
@@ -290,7 +290,8 @@
TEST_F(TextToBinaryCapability, BadMissingCapability) {
EXPECT_THAT(CompileFailure("OpCapability"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpCapability instruction, but found the "
+ "end of the stream."));
}
TEST_F(TextToBinaryCapability, BadInvalidCapability) {
diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp
index f74dbcf..955f5ef 100644
--- a/test/text_to_binary.pipe_storage_test.cpp
+++ b/test/text_to_binary.pipe_storage_test.cpp
@@ -59,10 +59,12 @@
"'OpConstantPipeStorage'."));
EXPECT_THAT(
CompileFailure("%1 = OpConstantPipeStorage", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpConstantPipeStorage instruction, but found "
+ "the end of the stream."));
EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpConstantPipeStorage instruction, but "
+ "found the end of the stream."));
EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5",
SPV_ENV_UNIVERSAL_1_1),
Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5})));
@@ -101,10 +103,12 @@
"'OpCreatePipeFromPipeStorage'."));
EXPECT_THAT(
CompileFailure("%1 = OpCreatePipeFromPipeStorage", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpCreatePipeFromPipeStorage instruction, but "
+ "found the end of the stream."));
EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 OpNop",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found next instruction instead."));
+ Eq("Expected operand for OpCreatePipeFromPipeStorage "
+ "instruction, but found the next instruction instead."));
EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3",
SPV_ENV_UNIVERSAL_1_1),
Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3})));
diff --git a/test/text_to_binary.subgroup_dispatch_test.cpp b/test/text_to_binary.subgroup_dispatch_test.cpp
index 967e3c3..8c40445 100644
--- a/test/text_to_binary.subgroup_dispatch_test.cpp
+++ b/test/text_to_binary.subgroup_dispatch_test.cpp
@@ -46,11 +46,13 @@
"found 'OpGetKernelLocalSizeForSubgroupCount'."));
EXPECT_THAT(CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount "
+ "instruction, but found the end of the stream."));
EXPECT_THAT(
CompileFailure("%1 = OpGetKernelLocalSizeForSubgroupCount %2 %3 %4 %5 %6",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount "
+ "instruction, but found the end of the stream."));
EXPECT_THAT(
CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type "
"%sgcount %invoke %param %param_size %param_align",
@@ -93,10 +95,12 @@
"'OpGetKernelMaxNumSubgroups'."));
EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, "
+ "but found the end of the stream."));
EXPECT_THAT(CompileFailure("%1 = OpGetKernelMaxNumSubgroups %2 %3 %4 %5",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, "
+ "but found the end of the stream."));
EXPECT_THAT(
CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type "
"%invoke %param %param_size %param_align",
diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp
index 1589188..65a2355 100644
--- a/test/text_to_binary.type_declaration_test.cpp
+++ b/test/text_to_binary.type_declaration_test.cpp
@@ -223,12 +223,14 @@
TEST_F(OpTypeForwardPointerTest, MissingType) {
EXPECT_THAT(CompileFailure("OpTypeForwardPointer"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpTypeForwardPointer instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpTypeForwardPointerTest, MissingClass) {
EXPECT_THAT(CompileFailure("OpTypeForwardPointer %pt"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpTypeForwardPointer instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpTypeForwardPointerTest, WrongClass) {
@@ -252,7 +254,8 @@
Eq("Expected <result-id> at the beginning of an instruction, found "
"'OpSizeOf'."));
EXPECT_THAT(CompileFailure("%res = OpSizeOf OpNop", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found next instruction instead."));
+ Eq("Expected operand for OpSizeOf instruction, but found the "
+ "next instruction instead."));
EXPECT_THAT(
CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_1),
Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3})));
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp
index b711ce7..bb30de0 100644
--- a/test/val/val_annotation_test.cpp
+++ b/test/val/val_annotation_test.cpp
@@ -754,6 +754,8 @@
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-06672"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("decoration must not be applied to this storage class"));
diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp
index 1001def..bebaa84 100644
--- a/test/val/val_bitwise_test.cpp
+++ b/test/val/val_bitwise_test.cpp
@@ -340,6 +340,16 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitFieldInsertVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitFieldInsert %u32 %u32_1 %u32_2 %s32_1 %s32_2
+%val2 = OpBitFieldInsert %s32vec2 %s32vec2_12 %s32vec2_12 %s32_1 %u32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) {
const std::string body = R"(
%val1 = OpBitFieldInsert %bool %u64_1 %u64_2 %s32_1 %s32_2
@@ -350,7 +360,7 @@
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "Expected int scalar or vector type as Result Type: BitFieldInsert"));
+ "Expected Base Type to be equal to Result Type: BitFieldInsert"));
}
TEST_F(ValidateBitwise, OpBitFieldInsertWrongBaseType) {
@@ -403,6 +413,20 @@
HasSubstr("Expected Count Type to be int scalar: BitFieldInsert"));
}
+TEST_F(ValidateBitwise, OpBitFieldInsertNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitFieldInsert"));
+}
+
TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) {
const std::string body = R"(
%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2
@@ -413,6 +437,16 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitFieldSExtractVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitFieldSExtract %u32 %u32_1 %s32_1 %s32_2
+%val2 = OpBitFieldSExtract %s32vec2 %s32vec2_12 %s32_1 %u32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) {
const std::string body = R"(
%val1 = OpBitFieldSExtract %bool %u64_1 %s32_1 %s32_2
@@ -420,9 +454,10 @@
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Expected int scalar or vector type as Result Type: "
- "BitFieldSExtract"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Expected Base Type to be equal to Result Type: BitFieldSExtract"));
}
TEST_F(ValidateBitwise, OpBitFieldSExtractWrongBaseType) {
@@ -462,6 +497,20 @@
HasSubstr("Expected Count Type to be int scalar: BitFieldSExtract"));
}
+TEST_F(ValidateBitwise, OpBitFieldSExtractNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitFieldSExtract"));
+}
+
TEST_F(ValidateBitwise, OpBitReverseSuccess) {
const std::string body = R"(
%val1 = OpBitReverse %u64 %u64_1
@@ -472,6 +521,16 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitReverseVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitReverse %u32 %u32_1
+%val2 = OpBitReverse %s32vec2 %s32vec2_12
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitReverseWrongResultType) {
const std::string body = R"(
%val1 = OpBitReverse %bool %u64_1
@@ -481,8 +540,7 @@
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr(
- "Expected int scalar or vector type as Result Type: BitReverse"));
+ HasSubstr("Expected Base Type to be equal to Result Type: BitReverse"));
}
TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) {
@@ -497,16 +555,41 @@
HasSubstr("Expected Base Type to be equal to Result Type: BitReverse"));
}
+TEST_F(ValidateBitwise, OpBitReverseNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitReverse %u64 %u64_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitReverse"));
+}
+
TEST_F(ValidateBitwise, OpBitCountSuccess) {
const std::string body = R"(
%val1 = OpBitCount %s32 %u64_1
%val2 = OpBitCount %u32vec2 %s32vec2_12
+%val3 = OpBitCount %s64 %s64_1
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitCountVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitCount %s32 %u32_1
+%val2 = OpBitCount %u32vec2 %s32vec2_12
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitCountWrongResultType) {
const std::string body = R"(
%val1 = OpBitCount %bool %u64_1
@@ -524,11 +607,14 @@
%val1 = OpBitCount %u32 %f64_1
)";
- CompileSuccessfully(GenerateShaderCode(body).c_str());
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("Expected Base Type to be int scalar or vector: BitCount"));
+ HasSubstr(
+ "Expected int scalar or vector type for Base operand: BitCount"));
}
TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) {
@@ -544,6 +630,19 @@
"BitCount"));
}
+TEST_F(ValidateBitwise, OpBitCountNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitCount %s64 %s64_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitCount"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 9eaf825..2db44a4 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -2687,6 +2687,8 @@
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("In Vulkan, BufferBlock is disallowed on variables in "
"the StorageBuffer storage class"));
}
@@ -2881,8 +2883,10 @@
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("PushConstant id '2' is missing Block decoration.\n"
- "From Vulkan spec, section 14.5.1:\n"
+ "From Vulkan spec:\n"
"Such variables must be identified with a Block "
"decoration"));
}
@@ -3033,11 +3037,13 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Entry point id '1' uses more than one PushConstant interface.\n"
- "From Vulkan spec, section 14.5.1:\n"
+ "From Vulkan spec:\n"
"There must be no more than one push constant block "
"statically used per shader entry point."));
}
@@ -3144,11 +3150,13 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Entry point id '1' uses more than one PushConstant interface.\n"
- "From Vulkan spec, section 14.5.1:\n"
+ "From Vulkan spec:\n"
"There must be no more than one push constant block "
"statically used per shader entry point."));
}
@@ -3186,8 +3194,10 @@
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3225,8 +3235,10 @@
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("Uniform id '3' is missing Binding decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3256,10 +3268,12 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3289,10 +3303,12 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("UniformConstant id '2' is missing Binding decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3329,10 +3345,12 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3370,8 +3388,10 @@
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3414,10 +3434,12 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -7158,7 +7180,9 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables "
"must be identified with a Block decoration"));
}
@@ -7186,7 +7210,9 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables "
"must be identified with a Block decoration"));
}
@@ -7215,7 +7241,9 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables "
"must be identified with a Block decoration"));
}
@@ -7287,10 +7315,11 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
- "identified with a Block or BufferBlock decoration"));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06676"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables must be "
+ "identified with a Block or BufferBlock decoration"));
}
TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) {
@@ -7315,10 +7344,11 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
- "identified with a Block or BufferBlock decoration"));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06676"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables must be "
+ "identified with a Block or BufferBlock decoration"));
}
TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) {
@@ -7344,10 +7374,11 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
- "identified with a Block or BufferBlock decoration"));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06676"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables must be "
+ "identified with a Block or BufferBlock decoration"));
}
TEST_F(ValidateDecorations, VulkanArrayStrideZero) {
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index d8f6b44..76af29c 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -1043,6 +1043,26 @@
HasSubstr("Expected Sampler to be of type OpTypeSampler"));
}
+TEST_F(ValidateImage, SampledImageIsStorage) {
+ const std::string declarations = R"(
+%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002
+)";
+ const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "",
+ SPV_ENV_UNIVERSAL_1_0, "GLSL450",
+ declarations)
+ .c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Sampled image type requires an image type with "
+ "\"Sampled\" operand set to 0 or 1"));
+}
+
TEST_F(ValidateImage, ImageTexelPointerSuccess) {
const std::string body = R"(
%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index f4369c6..5fb43f7 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -3252,8 +3252,8 @@
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Instruction cannot be used without a variable "
- "pointers capability"));
+ HasSubstr("Instruction cannot for logical addressing model be "
+ "used without a variable pointers capability"));
}
}