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: