Squashed 'third_party/SPIRV-Tools/' changes from eed5c76a5..7014be600
7014be600 Add support for SPV_KHR_subgroup_rotate (#4786)
2c7fb9707 Handle dontinline function in spread-volatile-semantics (#4776)
58dc37ea6 Add SpvBuiltInCullMaskKHR to a switch (#4790)
1295dca8e Reapply "Add folding rule to generate Fma instructions (#4783)" (#4789)
edaf51038 linker: Recalculate interface variables (#4784)
46492aa45 spirv-opt: skips if_conversion when dontflatten is set (#4770)
671f6e633 Revert "Add folding rule to generate Fma instructions (#4783)" (#4785)
2b2b0282a Add folding rule to generate Fma instructions (#4783)
cb96abbf7 Fix CMake for librt (#4773)
898ba64d2 Use cmake 3.23 on Windows. (#4782)
92c17edde Don't try to unroll loop with step count 0. (#4769)
c1bb0b941 Start SPIRV-Tools v2022.3-dev
7826e1941 Finalize SPIRV-Tools v2022.2
67fdf9409 Update CHANGES
78a0f075a Fix gen_build_version on Windows (#4780)
git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: 7014be600c7a588ef1886c1a1356f3fc0fea2ebb
Change-Id: I301663a32dfb1397cb1b1e32bb6e1e4d0ed9f93a
diff --git a/Android.mk b/Android.mk
index e7616f9..b9fbcc8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -302,7 +302,7 @@
$(LOCAL_PATH)/utils/update_build_version.py \
$(LOCAL_PATH)/CHANGES
@$(HOST_PYTHON) $(LOCAL_PATH)/utils/update_build_version.py \
- $(LOCAL_PATH) $(1)/build-version.inc
+ $(LOCAL_PATH)/CHANGES $(1)/build-version.inc
@echo "[$(TARGET_ARCH_ABI)] Generate : build-version.inc <= CHANGES"
$(LOCAL_PATH)/source/software_version.cpp: $(1)/build-version.inc
endef
diff --git a/BUILD.bazel b/BUILD.bazel
index c86ebbe..914619a 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -94,8 +94,8 @@
name = "gen_build_version",
srcs = ["CHANGES"],
outs = ["build-version.inc"],
- cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $$(dirname $(location CHANGES)) $(location build-version.inc)",
- cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location //:update_build_version) \"$(location CHANGES)\\..\" $(location build-version.inc)",
+ cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $(location CHANGES) $(location build-version.inc)",
+ cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location //:update_build_version) $(location CHANGES) $(location build-version.inc)",
tools = [":update_build_version"],
)
diff --git a/BUILD.gn b/BUILD.gn
index 0ce6c35..ba05497 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -256,12 +256,12 @@
action("spvtools_build_version") {
script = "utils/update_build_version.py"
- src_dir = "."
+ changes_file = "CHANGES"
inc_file = "${target_gen_dir}/build-version.inc"
outputs = [ inc_file ]
args = [
- rebase_path(src_dir, root_build_dir),
+ rebase_path(changes_file, root_build_dir),
rebase_path(inc_file, root_build_dir),
]
}
diff --git a/CHANGES b/CHANGES
index 27f1f88..7984461 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Revision history for SPIRV-Tools
-v2022.2-dev 2022-04-04
+v2022.3-dev 2022-04-07
+ - Start v2022.3-dev
+
+v2022.2 2022-04-07
- General
- Add OpModuleProcessed to debug opcode (#4694)
- Optimizer
diff --git a/DEPS b/DEPS
index 051b2de..abc3a76 100644
--- a/DEPS
+++ b/DEPS
@@ -6,7 +6,7 @@
'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133',
'googletest_revision': '25dcdc7e8bfac8967f20fb2c0a628f5cf442188d',
're2_revision': '0c5616df9c0aaa44c9440d87422012423d91c7d1',
- 'spirv_headers_revision': '4995a2f2723c401eb0ea3e10c81298906bf1422b',
+ 'spirv_headers_revision': 'b765c355f488837ca4c77980ba69484f3ff277f5',
}
deps = {
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index 24e29cc..8c9d689 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -22,7 +22,7 @@
set VS_VERSION=%2
:: Force usage of python 3.6
-set PATH=C:\python36;"C:\Program Files\CMake\bin";%PATH%
+set PATH=C:\python36;"C:\Program Files\cmake-3.23.1-windows-x86_64\bin";%PATH%
cd %SRC%
git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index f0dcadd..98559b8 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -197,7 +197,7 @@
add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC}
COMMAND ${PYTHON_EXECUTABLE}
${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
- ${spirv-tools_SOURCE_DIR} ${SPIRV_TOOLS_BUILD_VERSION_INC}
+ ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC}
DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
${SPIRV_TOOLS_CHANGES_FILE}
COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).")
@@ -407,7 +407,7 @@
find_library(LIBRT rt)
if(LIBRT)
foreach(target ${SPIRV_TOOLS_TARGETS})
- target_link_libraries(${target} ${LIBRT})
+ target_link_libraries(${target} rt)
endforeach()
endif()
endif()
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 76ce775..3b388cc 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -34,6 +34,7 @@
#include "source/opt/ir_loader.h"
#include "source/opt/pass_manager.h"
#include "source/opt/remove_duplicates_pass.h"
+#include "source/opt/remove_unused_interface_variables_pass.h"
#include "source/opt/type_manager.h"
#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
@@ -807,11 +808,16 @@
pass_res = manager.Run(&linked_context);
if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
- // Phase 11: Warn if SPIR-V limits were exceeded
+ // Phase 11: Recompute EntryPoint variables
+ manager.AddPass<opt::RemoveUnusedInterfaceVariablesPass>();
+ pass_res = manager.Run(&linked_context);
+ if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
+
+ // Phase 12: Warn if SPIR-V limits were exceeded
res = VerifyLimits(consumer, linked_context);
if (res != SPV_SUCCESS) return res;
- // Phase 12: Output the module
+ // Phase 13: Output the module
linked_context.module()->ToBinary(linked_binary, true);
return SPV_SUCCESS;
diff --git a/source/opcode.cpp b/source/opcode.cpp
index c9c425d..2584d51 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -528,6 +528,7 @@
case SpvOpGroupNonUniformLogicalXor:
case SpvOpGroupNonUniformQuadBroadcast:
case SpvOpGroupNonUniformQuadSwap:
+ case SpvOpGroupNonUniformRotateKHR:
return true;
default:
return false;
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index c879a0c..d15ad04 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -1430,6 +1430,64 @@
};
}
+// Replaces |inst| inplace with an FMA instruction |(x*y)+a|.
+void ReplaceWithFma(Instruction* inst, uint32_t x, uint32_t y, uint32_t a) {
+ uint32_t ext =
+ inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+
+ if (ext == 0) {
+ inst->context()->AddExtInstImport("GLSL.std.450");
+ ext = inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ assert(ext != 0 &&
+ "Could not add the GLSL.std.450 extended instruction set");
+ }
+
+ std::vector<Operand> operands;
+ operands.push_back({SPV_OPERAND_TYPE_ID, {ext}});
+ operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {x}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {y}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {a}});
+
+ inst->SetOpcode(SpvOpExtInst);
+ inst->SetInOperands(std::move(operands));
+}
+
+// Folds a multiple and add into an Fma.
+//
+// Cases:
+// (x * y) + a = Fma x y a
+// a + (x * y) = Fma x y a
+bool MergeMulAddArithmetic(IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ assert(inst->opcode() == SpvOpFAdd);
+
+ if (!inst->IsFloatingPointFoldingAllowed()) {
+ return false;
+ }
+
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ for (int i = 0; i < 2; i++) {
+ uint32_t op_id = inst->GetSingleWordInOperand(i);
+ Instruction* op_inst = def_use_mgr->GetDef(op_id);
+
+ if (op_inst->opcode() != SpvOpFMul) {
+ continue;
+ }
+
+ if (!op_inst->IsFloatingPointFoldingAllowed()) {
+ continue;
+ }
+
+ uint32_t x = op_inst->GetSingleWordInOperand(0);
+ uint32_t y = op_inst->GetSingleWordInOperand(1);
+ uint32_t a = inst->GetSingleWordInOperand((i + 1) % 2);
+ ReplaceWithFma(inst, x, y, a);
+ return true;
+ }
+ return false;
+}
+
FoldingRule IntMultipleBy1() {
return [](IRContext*, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
@@ -2543,6 +2601,7 @@
rules_[SpvOpFAdd].push_back(MergeAddSubArithmetic());
rules_[SpvOpFAdd].push_back(MergeGenericAddSubArithmetic());
rules_[SpvOpFAdd].push_back(FactorAddMuls());
+ rules_[SpvOpFAdd].push_back(MergeMulAddArithmetic);
rules_[SpvOpFDiv].push_back(RedundantFDiv());
rules_[SpvOpFDiv].push_back(ReciprocalFDiv());
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp
index 4920661..d1debd0 100644
--- a/source/opt/if_conversion.cpp
+++ b/source/opt/if_conversion.cpp
@@ -169,6 +169,8 @@
if (branch->opcode() != SpvOpBranchConditional) return false;
auto merge = (*common)->GetMergeInst();
if (!merge || merge->opcode() != SpvOpSelectionMerge) return false;
+ if (merge->GetSingleWordInOperand(1) == SpvSelectionControlDontFlattenMask)
+ return false;
if ((*common)->MergeBlockIdIfAny() != block->id()) return false;
return true;
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index a80d4f2..c9c3f1b 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -926,6 +926,19 @@
return modified;
}
+void IRContext::CollectCallTreeFromRoots(unsigned entryId,
+ std::unordered_set<uint32_t>* funcs) {
+ std::queue<uint32_t> roots;
+ roots.push(entryId);
+ while (!roots.empty()) {
+ const uint32_t fi = roots.front();
+ roots.pop();
+ funcs->insert(fi);
+ Function* fn = GetFunction(fi);
+ AddCalls(fn, &roots);
+ }
+}
+
void IRContext::EmitErrorMessage(std::string message, Instruction* inst) {
if (!consumer()) {
return;
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 946f9e9..f9f5153 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -411,6 +411,10 @@
void CollectNonSemanticTree(Instruction* inst,
std::unordered_set<Instruction*>* to_kill);
+ // Collect function reachable from |entryId|, returns |funcs|
+ void CollectCallTreeFromRoots(unsigned entryId,
+ std::unordered_set<uint32_t>* funcs);
+
// Returns true if all of the given analyses are valid.
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp
index 9bc495e..4feb64e 100644
--- a/source/opt/loop_descriptor.cpp
+++ b/source/opt/loop_descriptor.cpp
@@ -754,6 +754,10 @@
// |step_value| is NOT cleanly divisible then we add one to the sum.
int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value,
int64_t init_value, int64_t step_value) const {
+ if (step_value == 0) {
+ return 0;
+ }
+
int64_t diff = 0;
switch (condition) {
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h
index e88ff93..df01227 100644
--- a/source/opt/loop_descriptor.h
+++ b/source/opt/loop_descriptor.h
@@ -398,7 +398,8 @@
// Each different loop |condition| affects how we calculate the number of
// iterations using the |condition_value|, |init_value|, and |step_values| of
// the induction variable. This method will return the number of iterations in
- // a loop with those values for a given |condition|.
+ // a loop with those values for a given |condition|. Returns 0 if the number
+ // of iterations could not be computed.
int64_t GetIterations(SpvOp condition, int64_t condition_value,
int64_t init_value, int64_t step_value) const;
diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp
index a1d3432..b61fd0f 100644
--- a/source/opt/spread_volatile_semantics.cpp
+++ b/source/opt/spread_volatile_semantics.cpp
@@ -68,38 +68,12 @@
return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile);
}
-bool HasOnlyEntryPointsAsFunctions(IRContext* context, Module* module) {
- std::unordered_set<uint32_t> entry_function_ids;
- for (Instruction& entry_point : module->entry_points()) {
- entry_function_ids.insert(
- entry_point.GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint));
- }
- for (auto& function : *module) {
- if (entry_function_ids.find(function.result_id()) ==
- entry_function_ids.end()) {
- std::string message(
- "Functions of SPIR-V for spread-volatile-semantics pass input must "
- "be inlined except entry points");
- message += "\n " + function.DefInst().PrettyPrint(
- SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- context->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
- return false;
- }
- }
- return true;
-}
-
} // namespace
Pass::Status SpreadVolatileSemantics::Process() {
if (HasNoExecutionModel()) {
return Status::SuccessWithoutChange;
}
-
- if (!HasOnlyEntryPointsAsFunctions(context(), get_module())) {
- return Status::Failure;
- }
-
const bool is_vk_memory_model_enabled =
context()->get_feature_mgr()->HasCapability(
SpvCapabilityVulkanMemoryModel);
@@ -142,6 +116,8 @@
uint32_t var_id, Instruction* entry_point) {
uint32_t entry_function_id =
entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
+ std::unordered_set<uint32_t> funcs;
+ context()->CollectCallTreeFromRoots(entry_function_id, &funcs);
return !VisitLoadsOfPointersToVariableInEntries(
var_id,
[](Instruction* load) {
@@ -154,7 +130,7 @@
load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
return (memory_operands & SpvMemoryAccessVolatileMask) != 0;
},
- {entry_function_id});
+ funcs);
}
bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
@@ -225,7 +201,7 @@
bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
- const std::unordered_set<uint32_t>& entry_function_ids) {
+ const std::unordered_set<uint32_t>& function_ids) {
std::vector<uint32_t> worklist({var_id});
auto* def_use_mgr = context()->get_def_use_mgr();
while (!worklist.empty()) {
@@ -233,11 +209,11 @@
worklist.pop_back();
bool finish_traversal = !def_use_mgr->WhileEachUser(
ptr_id, [this, &worklist, &ptr_id, handle_load,
- &entry_function_ids](Instruction* user) {
+ &function_ids](Instruction* user) {
BasicBlock* block = context()->get_instr_block(user);
if (block == nullptr ||
- entry_function_ids.find(block->GetParent()->result_id()) ==
- entry_function_ids.end()) {
+ function_ids.find(block->GetParent()->result_id()) ==
+ function_ids.end()) {
return true;
}
@@ -266,21 +242,25 @@
Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids) {
// Set Volatile memory operand for all load instructions if they do not have
// it.
- VisitLoadsOfPointersToVariableInEntries(
- var->result_id(),
- [](Instruction* load) {
- if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
- load->AddOperand(
- {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessVolatileMask}});
+ for (auto entry_id : entry_function_ids) {
+ std::unordered_set<uint32_t> funcs;
+ context()->CollectCallTreeFromRoots(entry_id, &funcs);
+ VisitLoadsOfPointersToVariableInEntries(
+ var->result_id(),
+ [](Instruction* load) {
+ if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
+ load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
+ {SpvMemoryAccessVolatileMask}});
+ return true;
+ }
+ uint32_t memory_operands =
+ load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
+ memory_operands |= SpvMemoryAccessVolatileMask;
+ load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
return true;
- }
- uint32_t memory_operands =
- load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
- memory_operands |= SpvMemoryAccessVolatileMask;
- load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
- return true;
- },
- entry_function_ids);
+ },
+ funcs);
+ }
}
bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h
index 531a21d..014858d 100644
--- a/source/opt/spread_volatile_semantics.h
+++ b/source/opt/spread_volatile_semantics.h
@@ -72,15 +72,14 @@
Instruction* entry_point);
// Visits load instructions of pointers to variable whose result id is
- // |var_id| if the load instructions are in entry points whose
- // function id is one of |entry_function_ids|. |handle_load| is a function to
- // do some actions for the load instructions. Finishes the traversal and
- // returns false if |handle_load| returns false for a load instruction.
- // Otherwise, returns true after running |handle_load| for all the load
- // instructions.
+ // |var_id| if the load instructions are in reachable functions from entry
+ // points. |handle_load| is a function to do some actions for the load
+ // instructions. Finishes the traversal and returns false if |handle_load|
+ // returns false for a load instruction. Otherwise, returns true after running
+ // |handle_load| for all the load instructions.
bool VisitLoadsOfPointersToVariableInEntries(
uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
- const std::unordered_set<uint32_t>& entry_function_ids);
+ const std::unordered_set<uint32_t>& function_ids);
// Sets Memory Operands of OpLoad instructions that load |var| or pointers
// of |var| as Volatile if the function id of the OpLoad instruction is
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 57dde8a..6a2e919 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -4185,6 +4185,7 @@
case SpvBuiltInBaryCoordNV:
case SpvBuiltInBaryCoordNoPerspNV:
case SpvBuiltInCurrentRayTimeNV:
+ case SpvBuiltInCullMaskKHR:
// No validation rules (for the moment).
break;
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp
index 2b6eb8b..6d4f8a2 100644
--- a/source/val/validate_non_uniform.cpp
+++ b/source/val/validate_non_uniform.cpp
@@ -63,6 +63,59 @@
return SPV_SUCCESS;
}
+spv_result_t ValidateGroupNonUniformRotateKHR(ValidationState_t& _,
+ const Instruction* inst) {
+ // Scope is already checked by ValidateExecutionScope() above.
+ const uint32_t result_type = inst->type_id();
+ if (!_.IsIntScalarOrVectorType(result_type) &&
+ !_.IsFloatScalarOrVectorType(result_type) &&
+ !_.IsBoolScalarOrVectorType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Result Type to be a scalar or vector of "
+ "floating-point, integer or boolean type.";
+ }
+
+ const uint32_t value_type = _.GetTypeId(inst->GetOperandAs<uint32_t>(3));
+ if (value_type != result_type) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Result Type must be the same as the type of Value.";
+ }
+
+ const uint32_t delta_type = _.GetTypeId(inst->GetOperandAs<uint32_t>(4));
+ if (!_.IsUnsignedIntScalarType(delta_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Delta must be a scalar of integer type, whose Signedness "
+ "operand is 0.";
+ }
+
+ if (inst->words().size() > 6) {
+ const uint32_t cluster_size_op_id = inst->GetOperandAs<uint32_t>(5);
+ const uint32_t cluster_size_type = _.GetTypeId(cluster_size_op_id);
+ if (!_.IsUnsignedIntScalarType(cluster_size_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "ClusterSize must be a scalar of integer type, whose "
+ "Signedness operand is 0.";
+ }
+
+ uint64_t cluster_size;
+ if (!_.GetConstantValUint64(cluster_size_op_id, &cluster_size)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "ClusterSize must come from a constant instruction.";
+ }
+
+ if ((cluster_size == 0) || ((cluster_size & (cluster_size - 1)) != 0)) {
+ return _.diag(SPV_WARNING, inst)
+ << "Behavior is undefined unless ClusterSize is at least 1 and a "
+ "power of 2.";
+ }
+
+ // TODO(kpet) Warn about undefined behavior when ClusterSize is greater than
+ // the declared SubGroupSize
+ }
+
+ return SPV_SUCCESS;
+}
+
} // namespace
// Validates correctness of non-uniform group instructions.
@@ -79,6 +132,8 @@
switch (opcode) {
case SpvOpGroupNonUniformBallotBitCount:
return ValidateGroupNonUniformBallotBitCount(_, inst);
+ case SpvOpGroupNonUniformRotateKHR:
+ return ValidateGroupNonUniformRotateKHR(_, inst);
default:
break;
}
diff --git a/test/link/entry_points_test.cpp b/test/link/entry_points_test.cpp
index df7ea20..edf9f42 100644
--- a/test/link/entry_points_test.cpp
+++ b/test/link/entry_points_test.cpp
@@ -104,5 +104,48 @@
"GLCompute, was already defined."));
}
+TEST_F(EntryPoints, LinkedVariables) {
+ const std::string body1 = R"(
+ OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpDecorate %7 LinkageAttributes "foo" Export
+%1 = OpTypeInt 32 0
+%2 = OpTypeVector %1 3
+%3 = OpTypePointer Input %2
+%4 = OpVariable %3 Input
+%5 = OpTypeVoid
+%6 = OpTypeFunction %5
+%7 = OpFunction %5 None %6
+%8 = OpLabel
+%9 = OpLoad %2 %4 Aligned 32
+OpReturn
+OpFunctionEnd
+)";
+ const std::string body2 = R"(
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %4 "bar"
+OpDecorate %3 LinkageAttributes "foo" Import
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+OpFunctionEnd
+%4 = OpFunction %1 None %2
+%5 = OpLabel
+%6 = OpFunctionCall %1 %3
+OpReturn
+OpFunctionEnd
+)";
+
+ spvtest::Binary linked_binary;
+ EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
+ EXPECT_THAT(GetErrorMessage(), std::string());
+ EXPECT_TRUE(Validate(linked_binary));
+ EXPECT_THAT(GetErrorMessage(), std::string());
+}
+
} // namespace
} // namespace spvtools
diff --git a/test/link/linker_fixture.h b/test/link/linker_fixture.h
index 7bb1223..d005288 100644
--- a/test/link/linker_fixture.h
+++ b/test/link/linker_fixture.h
@@ -208,6 +208,10 @@
// Returns the accumulated error messages for the test.
std::string GetErrorMessage() const { return error_message_; }
+ bool Validate(const spvtest::Binary& binary) {
+ return tools_.Validate(binary);
+ }
+
private:
spvtools::Context context_;
spvtools::SpirvTools
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index 7565ca7..2ca3256 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -7108,6 +7108,214 @@
3, true)
));
+INSTANTIATE_TEST_SUITE_P(FmaGenerationMatchingTest, MatchingInstructionFoldingTest,
+::testing::Values(
+ // Test case 0: (x * y) + a = Fma(x, y, a)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 1: a + (x * y) = Fma(x, y, a)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %la %mul\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 2: (x * y) + a = Fma(x, y, a) with vectors
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_v4float Function\n" +
+ "%y = OpVariable %_ptr_v4float Function\n" +
+ "%a = OpVariable %_ptr_v4float Function\n" +
+ "%lx = OpLoad %v4float %x\n" +
+ "%ly = OpLoad %v4float %y\n" +
+ "%mul = OpFMul %v4float %lx %ly\n" +
+ "%la = OpLoad %v4float %a\n" +
+ "%3 = OpFAdd %v4float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 3: a + (x * y) = Fma(x, y, a) with vectors
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %la %mul\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test 5: that the OpExtInstImport instruction is generated if it is missing.
+ InstructionFoldingCase<bool>(
+ std::string() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "OpCapability Shader\n" +
+ "OpMemoryModel Logical GLSL450\n" +
+ "OpEntryPoint Fragment %main \"main\"\n" +
+ "OpExecutionMode %main OriginUpperLeft\n" +
+ "OpSource GLSL 140\n" +
+ "OpName %main \"main\"\n" +
+ "%void = OpTypeVoid\n" +
+ "%void_func = OpTypeFunction %void\n" +
+ "%bool = OpTypeBool\n" +
+ "%float = OpTypeFloat 32\n" +
+ "%_ptr_float = OpTypePointer Function %float\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test 5: Don't fold if the multiple is marked no contract.
+ InstructionFoldingCase<bool>(
+ std::string() +
+ "OpCapability Shader\n" +
+ "OpMemoryModel Logical GLSL450\n" +
+ "OpEntryPoint Fragment %main \"main\"\n" +
+ "OpExecutionMode %main OriginUpperLeft\n" +
+ "OpSource GLSL 140\n" +
+ "OpName %main \"main\"\n" +
+ "OpDecorate %mul NoContraction\n" +
+ "%void = OpTypeVoid\n" +
+ "%void_func = OpTypeFunction %void\n" +
+ "%bool = OpTypeBool\n" +
+ "%float = OpTypeFloat 32\n" +
+ "%_ptr_float = OpTypePointer Function %float\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, false),
+ // Test 6: Don't fold if the add is marked no contract.
+ InstructionFoldingCase<bool>(
+ std::string() +
+ "OpCapability Shader\n" +
+ "OpMemoryModel Logical GLSL450\n" +
+ "OpEntryPoint Fragment %main \"main\"\n" +
+ "OpExecutionMode %main OriginUpperLeft\n" +
+ "OpSource GLSL 140\n" +
+ "OpName %main \"main\"\n" +
+ "OpDecorate %3 NoContraction\n" +
+ "%void = OpTypeVoid\n" +
+ "%void_func = OpTypeFunction %void\n" +
+ "%bool = OpTypeBool\n" +
+ "%float = OpTypeFloat 32\n" +
+ "%_ptr_float = OpTypePointer Function %float\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, false)
+));
+
using MatchingInstructionWithNoResultFoldingTest =
::testing::TestWithParam<InstructionFoldingCase<bool>>;
diff --git a/test/opt/if_conversion_test.cpp b/test/opt/if_conversion_test.cpp
index dee15c3..81e9bb2 100644
--- a/test/opt/if_conversion_test.cpp
+++ b/test/opt/if_conversion_test.cpp
@@ -328,6 +328,40 @@
SinglePassRunAndCheck<IfConversion>(text, text, true, true);
}
+TEST_F(IfConversionTest, DontFlatten) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %1 "func" %2
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%v2uint = OpTypeVector %uint 2
+%10 = OpConstantComposite %v2uint %uint_0 %uint_1
+%11 = OpConstantComposite %v2uint %uint_1 %uint_0
+%_ptr_Output_v2uint = OpTypePointer Output %v2uint
+%2 = OpVariable %_ptr_Output_v2uint Output
+%13 = OpTypeFunction %void
+%1 = OpFunction %void None %13
+%14 = OpLabel
+OpSelectionMerge %15 DontFlatten
+OpBranchConditional %true %16 %17
+%16 = OpLabel
+OpBranch %15
+%17 = OpLabel
+OpBranch %15
+%15 = OpLabel
+%18 = OpPhi %v2uint %10 %16 %11 %17
+OpStore %2 %18
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<IfConversion>(text, text, true, true);
+}
+
TEST_F(IfConversionTest, LoopUntouched) {
const std::string text = R"(OpCapability Shader
OpMemoryModel Logical GLSL450
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index b72305c..299fb2d 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -3789,6 +3789,40 @@
SinglePassRunAndMatch<PartialUnrollerTestPass<2>>(text, true);
}
+TEST_F(PassClassTest, DontUnrollInfiteLoop) {
+ // This is an infinite loop that because the step is 0. We want to make sure
+ // the unroller does not try to unroll it.
+ const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_50 = OpConstant %int 50
+%bool = OpTypeBool
+%int_0_0 = OpConstant %int 0
+%2 = OpFunction %void None %4
+%10 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%12 = OpPhi %int %int_0 %10 %13 %14
+%15 = OpSLessThan %bool %12 %int_50
+OpLoopMerge %16 %14 Unroll
+OpBranchConditional %15 %14 %16
+%14 = OpLabel
+%13 = OpIAdd %int %12 %int_0_0
+OpBranch %11
+%16 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp
index fdabd92..dbb889c 100644
--- a/test/opt/spread_volatile_semantics_test.cpp
+++ b/test/opt/spread_volatile_semantics_test.cpp
@@ -54,6 +54,7 @@
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_KHR_ray_tracing"
OpName %main "main"
+OpName %fn "fn"
OpName %StorageBuffer "StorageBuffer"
OpMemberName %StorageBuffer 0 "index"
OpMemberName %StorageBuffer 1 "red"
@@ -109,6 +110,11 @@
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+%32 = OpFunctionCall %void %fn
+OpReturn
+OpFunctionEnd
+%fn = OpFunction %void None %3
+%33 = OpLabel
OpReturn
OpFunctionEnd
)");
@@ -782,12 +788,7 @@
OpFunctionEnd
)";
- EXPECT_EQ(RunPass(text), Pass::Status::Failure);
- const char expected_error[] =
- "ERROR: 0: Functions of SPIR-V for spread-volatile-semantics pass "
- "input must be inlined except entry points";
- EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(),
- expected_error);
+ EXPECT_EQ(RunPass(text), Pass::Status::SuccessWithoutChange);
}
TEST_F(VolatileSpreadErrorTest, VarNotUsedInEntryPointForVolatile) {
@@ -1133,6 +1134,134 @@
EXPECT_EQ(status, Pass::Status::SuccessWithoutChange);
}
+TEST_F(VolatileSpreadTest, NoInlinedfuncCalls) {
+ const std::string text = R"(
+OpCapability RayTracingNV
+OpCapability VulkanMemoryModel
+OpCapability GroupNonUniform
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical Vulkan
+OpEntryPoint RayGenerationNV %main "main" %SubgroupSize
+OpSource HLSL 630
+OpName %main "main"
+OpName %src_main "src.main"
+OpName %bb_entry "bb.entry"
+OpName %func0 "func0"
+OpName %bb_entry_0 "bb.entry"
+OpName %func2 "func2"
+OpName %bb_entry_1 "bb.entry"
+OpName %param_var_count "param.var.count"
+OpName %func1 "func1"
+OpName %bb_entry_2 "bb.entry"
+OpName %func3 "func3"
+OpName %count "count"
+OpName %bb_entry_3 "bb.entry"
+OpDecorate %SubgroupSize BuiltIn SubgroupSize
+%uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+%25 = OpTypeFunction %void %_ptr_Function_uint
+%SubgroupSize = OpVariable %_ptr_Input_uint Input
+%main = OpFunction %void None %6
+%7 = OpLabel
+%8 = OpFunctionCall %void %src_main
+OpReturn
+OpFunctionEnd
+%src_main = OpFunction %void None %6
+%bb_entry = OpLabel
+%11 = OpFunctionCall %void %func0
+OpReturn
+OpFunctionEnd
+%func0 = OpFunction %void DontInline %6
+%bb_entry_0 = OpLabel
+%14 = OpFunctionCall %void %func2
+%16 = OpFunctionCall %void %func1
+OpReturn
+OpFunctionEnd
+%func2 = OpFunction %void DontInline %6
+%bb_entry_1 = OpLabel
+%param_var_count = OpVariable %_ptr_Function_uint Function
+; CHECK: {{%\w+}} = OpLoad %uint %SubgroupSize Volatile
+%21 = OpLoad %uint %SubgroupSize
+OpStore %param_var_count %21
+%22 = OpFunctionCall %void %func3 %param_var_count
+OpReturn
+OpFunctionEnd
+%func1 = OpFunction %void DontInline %6
+%bb_entry_2 = OpLabel
+OpReturn
+OpFunctionEnd
+%func3 = OpFunction %void DontInline %25
+%count = OpFunctionParameter %_ptr_Function_uint
+%bb_entry_3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+TEST_F(VolatileSpreadErrorTest, NoInlinedMultiEntryfuncCalls) {
+ const std::string text = R"(
+OpCapability RayTracingNV
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationNV %main "main" %SubgroupSize
+OpEntryPoint GLCompute %main2 "main2" %gl_LocalInvocationIndex %SubgroupSize
+OpSource HLSL 630
+OpName %main "main"
+OpName %bb_entry "bb.entry"
+OpName %main2 "main2"
+OpName %bb_entry_0 "bb.entry"
+OpName %func "func"
+OpName %count "count"
+OpName %bb_entry_1 "bb.entry"
+OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %SubgroupSize BuiltIn SubgroupSize
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%void = OpTypeVoid
+%12 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%29 = OpTypeFunction %void %_ptr_Function_v4float
+%34 = OpTypeFunction %void %_ptr_Function_uint
+%SubgroupSize = OpVariable %_ptr_Input_uint Input
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%main = OpFunction %void None %12
+%bb_entry = OpLabel
+%20 = OpFunctionCall %void %func
+OpReturn
+OpFunctionEnd
+%main2 = OpFunction %void None %12
+%bb_entry_0 = OpLabel
+%33 = OpFunctionCall %void %func
+OpReturn
+OpFunctionEnd
+%func = OpFunction %void DontInline %12
+%bb_entry_1 = OpLabel
+%count = OpVariable %_ptr_Function_uint Function
+%35 = OpLoad %uint %SubgroupSize
+OpStore %count %35
+OpReturn
+OpFunctionEnd
+)";
+ EXPECT_EQ(RunPass(text), Pass::Status::Failure);
+ const char expected_error[] =
+ "ERROR: 0: Variable is a target for Volatile semantics for an entry "
+ "point, but it is not for another entry point";
+ EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(),
+ expected_error);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 3a592a0..cf4919a 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -1075,5 +1075,27 @@
{1, 2, 3, SpvGroupOperationReduce, 4})},
})));
+// SPV_KHR_subgroup_rotate
+
+INSTANTIATE_TEST_SUITE_P(
+ SPV_KHR_subgroup_rotate, ExtensionRoundTripTest,
+ Combine(Values(SPV_ENV_UNIVERSAL_1_0, 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, SPV_ENV_OPENCL_2_1),
+ ValuesIn(std::vector<AssemblyCase>{
+ {"OpExtension \"SPV_KHR_subgroup_rotate\"\n",
+ MakeInstruction(SpvOpExtension,
+ MakeVector("SPV_KHR_subgroup_rotate"))},
+ {"OpCapability GroupNonUniformRotateKHR\n",
+ MakeInstruction(SpvOpCapability,
+ {SpvCapabilityGroupNonUniformRotateKHR})},
+ {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5\n",
+ MakeInstruction(SpvOpGroupNonUniformRotateKHR,
+ {1, 2, 3, 4, 5})},
+ {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5 %6\n",
+ MakeInstruction(SpvOpGroupNonUniformRotateKHR,
+ {1, 2, 3, 4, 5, 6})},
+ })));
+
} // namespace
} // namespace spvtools
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 64eba44..65f2791 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -45,6 +45,7 @@
val_extension_spv_khr_integer_dot_product.cpp
val_extension_spv_khr_bit_instructions.cpp
val_extension_spv_khr_terminate_invocation.cpp
+ val_extension_spv_khr_subgroup_rotate.cpp
val_ext_inst_test.cpp
val_ext_inst_debug_test.cpp
${VAL_TEST_COMMON_SRCS}
diff --git a/test/val/val_extension_spv_khr_subgroup_rotate.cpp b/test/val/val_extension_spv_khr_subgroup_rotate.cpp
new file mode 100644
index 0000000..4f156e8
--- /dev/null
+++ b/test/val/val_extension_spv_khr_subgroup_rotate.cpp
@@ -0,0 +1,352 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+struct Case {
+ std::vector<std::string> caps;
+ bool shader;
+ std::string result_type;
+ std::string scope;
+ std::string delta;
+ std::string cluster_size;
+ std::string expected_error; // empty for no error.
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+ out << "\nSPV_KHR_subgroup_rotate Case{{";
+ for (auto& cap : c.caps) {
+ out << cap;
+ }
+ out << "} ";
+ out << (c.shader ? "shader " : "kernel ");
+ out << c.result_type + " ";
+ out << c.scope + " ";
+ out << c.delta + " ";
+ out << c.cluster_size + " ";
+ out << "err'" << c.expected_error << "'";
+ out << "}";
+ return out;
+}
+
+std::string AssemblyForCase(const Case& c) {
+ std::ostringstream ss;
+
+ if (c.shader) {
+ ss << "OpCapability Shader\n";
+ } else {
+ ss << "OpCapability Kernel\n";
+ ss << "OpCapability Addresses\n";
+ }
+ for (auto& cap : c.caps) {
+ ss << "OpCapability " << cap << "\n";
+ }
+ ss << "OpExtension \"SPV_KHR_subgroup_rotate\"\n";
+
+ if (c.shader) {
+ ss << "OpMemoryModel Logical GLSL450\n";
+ ss << "OpEntryPoint GLCompute %main \"main\"\n";
+ } else {
+ ss << "OpMemoryModel Physical32 OpenCL\n";
+ ss << "OpEntryPoint Kernel %main \"main\"\n";
+ }
+
+ ss << R"(
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+ %u32 = OpTypeInt 32 0
+ %float = OpTypeFloat 32
+ %ptr = OpTypePointer Function %u32
+ )";
+
+ if (c.shader) {
+ ss << "%i32 = OpTypeInt 32 1\n";
+ }
+
+ ss << R"(
+ %u32_0 = OpConstant %u32 0
+ %u32_1 = OpConstant %u32 1
+ %u32_15 = OpConstant %u32 15
+ %u32_16 = OpConstant %u32 16
+ %u32_undef = OpUndef %u32
+ %u32_spec_1 = OpSpecConstant %u32 1
+ %u32_spec_16 = OpSpecConstant %u32 16
+ %f32_1 = OpConstant %float 1.0
+ %subgroup = OpConstant %u32 3
+ %workgroup = OpConstant %u32 2
+ %invalid_scope = OpConstant %u32 1
+ %val = OpConstant %u32 42
+ )";
+
+ if (c.shader) {
+ ss << "%i32_1 = OpConstant %i32 1\n";
+ }
+
+ ss << R"(
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ )";
+
+ ss << "%unused = OpGroupNonUniformRotateKHR ";
+ ss << c.result_type + " ";
+ ss << c.scope;
+ ss << " %val ";
+ ss << c.delta;
+ ss << " " + c.cluster_size;
+ ss << "\n";
+
+ ss << R"(
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ return ss.str();
+}
+
+using ValidateSpvKHRSubgroupRotate = spvtest::ValidateBase<Case>;
+
+TEST_P(ValidateSpvKHRSubgroupRotate, Base) {
+ const auto& c = GetParam();
+ const auto& assembly = AssemblyForCase(c);
+ CompileSuccessfully(assembly);
+ if (c.expected_error.empty()) {
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+ } else {
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(c.expected_error));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Valid, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{
+ {"GroupNonUniformRotateKHR"}, false, "%u32", "%subgroup", "%u32_1"},
+ Case{{"GroupNonUniformRotateKHR"}, true, "%u32", "%subgroup", "%u32_1"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_spec_1",
+ "%u32_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_spec_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%workgroup",
+ "%u32_1"},
+ Case{
+ {"GroupNonUniformRotateKHR"}, true, "%u32", "%workgroup", "%u32_1"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%workgroup",
+ "%u32_spec_1"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%workgroup",
+ "%u32_spec_1"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ RequiresCapability, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(Case{{},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Opcode GroupNonUniformRotateKHR requires one of "
+ "these capabilities: "
+ "GroupNonUniformRotateKHR"},
+ Case{{},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Opcode GroupNonUniformRotateKHR requires one of "
+ "these capabilities: "
+ "GroupNonUniformRotateKHR"}));
+
+TEST_F(ValidateSpvKHRSubgroupRotate, RequiresExtension) {
+ const std::string str = R"(
+ OpCapability GroupNonUniformRotateKHR
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "1st operand of Capability: operand GroupNonUniformRotateKHR(6026) "
+ "requires one of these extensions: SPV_KHR_subgroup_rotate"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidExecutionScope, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%invalid_scope",
+ "%u32_1",
+ "",
+ "Execution scope is limited to Subgroup or Workgroup"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%invalid_scope",
+ "%u32_1",
+ "",
+ "Execution scope is limited to Subgroup or Workgroup"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidResultType, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%ptr",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Expected Result Type to be a scalar or vector of "
+ "floating-point, integer or boolean type"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%ptr",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Expected Result Type to be a scalar or vector of "
+ "floating-point, integer or boolean type"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ MismatchedResultAndValueTypes, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%float",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Result Type must be the same as the type of Value"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%float",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Result Type must be the same as the type of Value"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidDelta, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%f32_1",
+ "",
+ "Delta must be a scalar of integer type, whose "
+ "Signedness operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%f32_1",
+ "",
+ "Delta must be a scalar of integer type, whose "
+ "Signedness operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%i32_1",
+ "",
+ "Delta must be a scalar of integer type, whose "
+ "Signedness operand is 0"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidClusterSize, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%f32_1",
+ "ClusterSize must be a scalar of integer type, whose Signedness "
+ "operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%i32_1",
+ "ClusterSize must be a scalar of integer type, whose Signedness "
+ "operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_0",
+ "Behavior is undefined unless ClusterSize is at least 1 and a "
+ "power of 2"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_15",
+ "Behavior is undefined unless ClusterSize is at least 1 and a "
+ "power of 2"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_undef",
+ "ClusterSize must come from a constant instruction"}));
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/utils/update_build_version.py b/utils/update_build_version.py
index 321de74..2a1ca60 100755
--- a/utils/update_build_version.py
+++ b/utils/update_build_version.py
@@ -17,16 +17,16 @@
# Updates an output file with version info unless the new content is the same
# as the existing content.
#
-# Args: <spirv-tools_dir> <output-file>
+# Args: <changes-file> <output-file>
#
# The output file will contain a line of text consisting of two C source syntax
# string literals separated by a comma:
-# - The software version deduced from the CHANGES file in the given directory.
+# - The software version deduced from the given CHANGES file.
# - A longer string with the project name, the software version number, and
-# git commit information for the directory. The commit information
-# is the output of "git describe" if that succeeds, or "git rev-parse HEAD"
-# if that succeeds, or otherwise a message containing the phrase
-# "unknown hash".
+# git commit information for the CHANGES file's directory. The commit
+# information is the output of "git describe" if that succeeds, or "git
+# rev-parse HEAD" if that succeeds, or otherwise a message containing the
+# phrase "unknown hash".
# The string contents are escaped as necessary.
import datetime
@@ -73,9 +73,8 @@
return stdout
-def deduce_software_version(directory):
- """Returns a software version number parsed from the CHANGES file
- in the given directory.
+def deduce_software_version(changes_file):
+ """Returns a software version number parsed from the given CHANGES file.
The CHANGES file describes most recent versions first.
"""
@@ -85,7 +84,6 @@
# unexpected carriage returns on a linefeed-only system such as
# Linux.
pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$')
- changes_file = os.path.join(directory, 'CHANGES')
with open(changes_file, mode='r') as f:
for line in f.readlines():
match = pattern.match(line)
@@ -125,16 +123,17 @@
def main():
if len(sys.argv) != 3:
- print('usage: {} <spirv-tools-dir> <output-file>'.format(sys.argv[0]))
+ print('usage: {} <changes-files> <output-file>'.format(sys.argv[0]))
sys.exit(1)
output_file = sys.argv[2]
mkdir_p(os.path.dirname(output_file))
software_version = deduce_software_version(sys.argv[1])
+ directory = os.path.dirname(sys.argv[1])
new_content = '"{}", "SPIRV-Tools {} {}"\n'.format(
software_version, software_version,
- describe(sys.argv[1]).replace('"', '\\"'))
+ describe(directory).replace('"', '\\"'))
if os.path.isfile(output_file):
with open(output_file, 'r') as f: