diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 578d204ee..3d5c052b0 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-178-g85f3e93d" +"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-186-gb334829a" diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h index dd2526bf3..2f4c7d60b 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h @@ -627,6 +627,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( spv_fuzzer_options options, uint32_t shrinker_step_limit); +// Enables running the validator after every pass is applied during a fuzzing +// run. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options); + // Encodes the given SPIR-V assembly text to its binary representation. The // length parameter specifies the number of bytes for text. Encoded binary will // be stored into *binary. Any error will be written into *diagnostic if diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp index b09fdd457..ceadef8fb 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp @@ -229,6 +229,11 @@ class FuzzerOptions { spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); } + // See spvFuzzerOptionsEnableFuzzerPassValidation. + void enable_fuzzer_pass_validation() { + spvFuzzerOptionsEnableFuzzerPassValidation(options_); + } + private: spv_fuzzer_options options_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index 01b4258d3..20e714d7c 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -65,13 +65,26 @@ void MaybeAddPass( } // namespace struct Fuzzer::Impl { - explicit Impl(spv_target_env env) : target_env(env) {} + explicit Impl(spv_target_env env, uint32_t random_seed, + bool validate_after_each_pass) + : target_env(env), + seed(random_seed), + validate_after_each_fuzzer_pass(validate_after_each_pass) {} - const spv_target_env target_env; // Target environment. - MessageConsumer consumer; // Message consumer. + bool ApplyPassAndCheckValidity(FuzzerPass* pass, + const opt::IRContext& ir_context, + const spvtools::SpirvTools& tools) const; + + const spv_target_env target_env; // Target environment. + const uint32_t seed; // Seed for random number generator. + bool validate_after_each_fuzzer_pass; // Determines whether the validator + // should be invoked after every fuzzer pass. + MessageConsumer consumer; // Message consumer. }; -Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique(env)) {} +Fuzzer::Fuzzer(spv_target_env env, uint32_t seed, + bool validate_after_each_fuzzer_pass) + : impl_(MakeUnique(env, seed, validate_after_each_fuzzer_pass)) {} Fuzzer::~Fuzzer() = default; @@ -79,10 +92,27 @@ void Fuzzer::SetMessageConsumer(MessageConsumer c) { impl_->consumer = std::move(c); } +bool Fuzzer::Impl::ApplyPassAndCheckValidity( + FuzzerPass* pass, const opt::IRContext& ir_context, + const spvtools::SpirvTools& tools) const { + pass->Apply(); + if (validate_after_each_fuzzer_pass) { + std::vector binary_to_validate; + ir_context.module()->ToBinary(&binary_to_validate, false); + if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) { + consumer(SPV_MSG_INFO, nullptr, {}, + "Binary became invalid during fuzzing (set a breakpoint to " + "inspect); stopping."); + return false; + } + } + return true; +} + Fuzzer::FuzzerResultStatus Fuzzer::Run( const std::vector& binary_in, const protobufs::FactSequence& initial_facts, - spv_const_fuzzer_options options, std::vector* binary_out, + std::vector* binary_out, protobufs::TransformationSequence* transformation_sequence_out) const { // Check compatibility between the library version being linked with and the // header files being used. @@ -108,10 +138,8 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size()); assert(ir_context); - // Make a PRNG, either from a given seed or from a random device. - PseudoRandomGenerator random_generator( - options->has_random_seed ? options->random_seed - : static_cast(std::random_device()())); + // Make a PRNG from the seed passed to the fuzzer on creation. + PseudoRandomGenerator random_generator(impl_->seed); // The fuzzer will introduce new ids into the module. The module's id bound // gives the smallest id that can be used for this purpose. We add an offset @@ -128,9 +156,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( // Add some essential ingredients to the module if they are not already // present, such as boolean constants. - FuzzerPassAddUsefulConstructs(ir_context.get(), &fact_manager, - &fuzzer_context, transformation_sequence_out) - .Apply(); + FuzzerPassAddUsefulConstructs add_useful_constructs( + ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); + if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context, + tools)) { + return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; + } // Apply some semantics-preserving passes. std::vector> passes; @@ -168,7 +200,11 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( (is_first || fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) { is_first = false; - passes[fuzzer_context.RandomIndex(passes)]->Apply(); + if (!impl_->ApplyPassAndCheckValidity( + passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context, + tools)) { + return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; + } } // Now apply some passes that it does not make sense to apply repeatedly, @@ -186,15 +222,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); for (auto& pass : final_passes) { - pass->Apply(); - } - - if (fuzzer_context.ChooseEven()) { - FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager, - &fuzzer_context, - transformation_sequence_out) - .Apply(); + if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) { + return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; + } } // Encode the module as a binary. diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.h b/3rdparty/spirv-tools/source/fuzz/fuzzer.h index a257c2a19..c1d2deeef 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.h @@ -32,11 +32,16 @@ class Fuzzer { enum class FuzzerResultStatus { kComplete, kFailedToCreateSpirvToolsInterface, + kFuzzerPassLedToInvalidModule, kInitialBinaryInvalid, }; - // Constructs a fuzzer from the given target environment. - explicit Fuzzer(spv_target_env env); + // Constructs a fuzzer from the given target environment |env|. |seed| is a + // seed for pseudo-random number generation. + // |validate_after_each_fuzzer_pass| controls whether the validator will be + // invoked after every fuzzer pass is applied. + explicit Fuzzer(spv_target_env env, uint32_t seed, + bool validate_after_each_fuzzer_pass); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; @@ -51,14 +56,13 @@ class Fuzzer { void SetMessageConsumer(MessageConsumer consumer); // Transforms |binary_in| to |binary_out| by running a number of randomized - // fuzzer passes, controlled via |options|. Initial facts about the input - // binary and the context in which it will execute are provided via - // |initial_facts|. The transformation sequence that was applied is returned - // via |transformation_sequence_out|. + // fuzzer passes. Initial facts about the input binary and the context in + // which it will execute are provided via |initial_facts|. The transformation + // sequence that was applied is returned via |transformation_sequence_out|. FuzzerResultStatus Run( const std::vector& binary_in, const protobufs::FactSequence& initial_facts, - spv_const_fuzzer_options options, std::vector* binary_out, + std::vector* binary_out, protobufs::TransformationSequence* transformation_sequence_out) const; private: diff --git a/3rdparty/spirv-tools/source/opt/block_merge_util.cpp b/3rdparty/spirv-tools/source/opt/block_merge_util.cpp index 107723dfd..263a069f9 100644 --- a/3rdparty/spirv-tools/source/opt/block_merge_util.cpp +++ b/3rdparty/spirv-tools/source/opt/block_merge_util.cpp @@ -49,6 +49,18 @@ bool IsMerge(IRContext* context, BasicBlock* block) { return IsMerge(context, block->id()); } +// Returns true if |id| is the continue target of a merge instruction. +bool IsContinue(IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->WhileEachUse( + id, [](Instruction* user, uint32_t index) { + SpvOp op = user->opcode(); + if (op == SpvOpLoopMerge && index == 1u) { + return false; + } + return true; + }); +} + // Removes any OpPhi instructions in |block|, which should have exactly one // predecessor, replacing uses of OpPhi ids with the ids associated with the // predecessor. @@ -86,6 +98,11 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { return false; } + if (pred_is_merge && IsContinue(context, lab_id)) { + // Cannot merge a continue target with a merge block. + return false; + } + // Don't bother trying to merge unreachable blocks. if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) { if (!dominators->IsReachable(block)) return false; diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.cpp b/3rdparty/spirv-tools/source/opt/inline_pass.cpp index 36991ec56..3c874a7ef 100644 --- a/3rdparty/spirv-tools/source/opt/inline_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/inline_pass.cpp @@ -27,7 +27,6 @@ static const int kSpvFunctionCallFunctionId = 2; static const int kSpvFunctionCallArgumentId = 3; static const int kSpvReturnValueId = 0; -static const int kSpvLoopMergeContinueTargetIdInIdx = 1; namespace spvtools { namespace opt { @@ -285,19 +284,14 @@ bool InlinePass::GenInlineCode( if (rid != 0) callee_result_ids.insert(rid); }); - // If the caller is in a single-block loop, and the callee has multiple - // blocks, then the normal inlining logic will place the OpLoopMerge in - // the last of several blocks in the loop. Instead, it should be placed - // at the end of the first block. First determine if the caller is in a - // single block loop. We'll wait to move the OpLoopMerge until the end - // of the regular inlining logic, and only if necessary. - bool caller_is_single_block_loop = false; + // If the caller is a loop header and the callee has multiple blocks, then the + // normal inlining logic will place the OpLoopMerge in the last of several + // blocks in the loop. Instead, it should be placed at the end of the first + // block. We'll wait to move the OpLoopMerge until the end of the regular + // inlining logic, and only if necessary. bool caller_is_loop_header = false; - if (auto* loop_merge = call_block_itr->GetLoopMergeInst()) { + if (call_block_itr->GetLoopMergeInst()) { caller_is_loop_header = true; - caller_is_single_block_loop = - call_block_itr->id() == - loop_merge->GetSingleWordInOperand(kSpvLoopMergeContinueTargetIdInIdx); } bool callee_begins_with_structured_header = @@ -611,10 +605,6 @@ bool InlinePass::GenInlineCode( --loop_merge_itr; assert(loop_merge_itr->opcode() == SpvOpLoopMerge); std::unique_ptr cp_inst(loop_merge_itr->Clone(context())); - if (caller_is_single_block_loop) { - // Also, update its continue target to point to the last block. - cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()}); - } first->tail().InsertBefore(std::move(cp_inst)); // Remove the loop merge from the last block. diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index ece1abec3..d1b241722 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -198,28 +198,35 @@ Optimizer& Optimizer::RegisterSizePasses() { .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateEliminateDeadFunctionsPass()) .RegisterPass(CreatePrivateToLocalPass()) - .RegisterPass(CreateScalarReplacementPass()) - .RegisterPass(CreateLocalAccessChainConvertPass()) - .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) - .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) - .RegisterPass(CreateSimplificationPass()) - .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateScalarReplacementPass(0)) .RegisterPass(CreateLocalMultiStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateIfConversionPass()) + .RegisterPass(CreateSimplificationPass()) .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateDeadBranchElimPass()) - .RegisterPass(CreateIfConversionPass()) - .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateBlockMergePass()) - .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCopyPropagateArraysPass()) + .RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateEliminateDeadMembersPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) .RegisterPass(CreateRedundancyEliminationPass()) - .RegisterPass(CreateCFGCleanupPass()) - .RegisterPass(CreateAggressiveDCEPass()); + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCFGCleanupPass()); } Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() { diff --git a/3rdparty/spirv-tools/source/opt/register_pressure.cpp b/3rdparty/spirv-tools/source/opt/register_pressure.cpp index 34dac1d7b..cb2467441 100644 --- a/3rdparty/spirv-tools/source/opt/register_pressure.cpp +++ b/3rdparty/spirv-tools/source/opt/register_pressure.cpp @@ -78,9 +78,16 @@ class ComputeRegisterLiveness { // - Second, walk loop forest to propagate registers crossing back-edges // (add iterative values into the liveness set). void Compute() { - cfg_.ForEachBlockInPostOrder(&*function_->begin(), [this](BasicBlock* bb) { - ComputePartialLiveness(bb); - }); + for (BasicBlock& start_bb : *function_) { + if (reg_pressure_->Get(start_bb.id()) != nullptr) { + continue; + } + cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) { + if (reg_pressure_->Get(bb->id()) == nullptr) { + ComputePartialLiveness(bb); + } + }); + } DoLoopLivenessUnification(); EvaluateRegisterRequirements(); } diff --git a/3rdparty/spirv-tools/source/print.cpp b/3rdparty/spirv-tools/source/print.cpp index 1d9829dfc..128587ae2 100644 --- a/3rdparty/spirv-tools/source/print.cpp +++ b/3rdparty/spirv-tools/source/print.cpp @@ -15,7 +15,8 @@ #include "source/print.h" #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ - defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN) + defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \ + defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA) namespace spvtools { clr::reset::operator const char*() { return "\x1b[0m"; } diff --git a/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp b/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp index ab8903e1e..b407f148a 100644 --- a/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp +++ b/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp @@ -23,7 +23,8 @@ spv_fuzzer_options_t::spv_fuzzer_options_t() : has_random_seed(false), random_seed(0), replay_validation_enabled(false), - shrinker_step_limit(kDefaultStepLimit) {} + shrinker_step_limit(kDefaultStepLimit), + fuzzer_pass_validation_enabled(false) {} SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() { return new spv_fuzzer_options_t(); @@ -48,3 +49,8 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( spv_fuzzer_options options, uint32_t shrinker_step_limit) { options->shrinker_step_limit = shrinker_step_limit; } + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options) { + options->fuzzer_pass_validation_enabled = true; +} diff --git a/3rdparty/spirv-tools/source/spirv_fuzzer_options.h b/3rdparty/spirv-tools/source/spirv_fuzzer_options.h index 7bb16c738..143f77f84 100644 --- a/3rdparty/spirv-tools/source/spirv_fuzzer_options.h +++ b/3rdparty/spirv-tools/source/spirv_fuzzer_options.h @@ -34,6 +34,9 @@ struct spv_fuzzer_options_t { // See spvFuzzerOptionsSetShrinkerStepLimit. uint32_t shrinker_step_limit; + + // See spvFuzzerOptionsValidateAfterEveryPass. + bool fuzzer_pass_validation_enabled; }; #endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_ diff --git a/3rdparty/spirv-tools/source/val/basic_block.h b/3rdparty/spirv-tools/source/val/basic_block.h index efbd243b6..876105c27 100644 --- a/3rdparty/spirv-tools/source/val/basic_block.h +++ b/3rdparty/spirv-tools/source/val/basic_block.h @@ -15,8 +15,8 @@ #ifndef SOURCE_VAL_BASIC_BLOCK_H_ #define SOURCE_VAL_BASIC_BLOCK_H_ -#include #include +#include #include #include #include @@ -28,7 +28,7 @@ namespace val { enum BlockType : uint32_t { kBlockTypeUndefined, - kBlockTypeHeader, + kBlockTypeSelection, kBlockTypeLoop, kBlockTypeMerge, kBlockTypeBreak, diff --git a/3rdparty/spirv-tools/source/val/function.cpp b/3rdparty/spirv-tools/source/val/function.cpp index 876a6081a..028177002 100644 --- a/3rdparty/spirv-tools/source/val/function.cpp +++ b/3rdparty/spirv-tools/source/val/function.cpp @@ -14,9 +14,8 @@ #include "source/val/function.h" -#include - #include +#include #include #include #include @@ -99,7 +98,7 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { RegisterBlock(merge_id, false); BasicBlock& merge_block = blocks_.at(merge_id); - current_block_->set_type(kBlockTypeHeader); + current_block_->set_type(kBlockTypeSelection); merge_block.set_type(kBlockTypeMerge); merge_block_header_[&merge_block] = current_block_; @@ -344,7 +343,7 @@ int Function::GetBlockDepth(BasicBlock* bb) { BasicBlock* header = merge_block_header_[bb]; assert(header); block_depth_[bb] = GetBlockDepth(header); - } else if (bb_dom->is_type(kBlockTypeHeader) || + } else if (bb_dom->is_type(kBlockTypeSelection) || bb_dom->is_type(kBlockTypeLoop)) { // The dominator of the given block is a header block. So, the nesting // depth of this block is: 1 + nesting depth of the header. diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index 4801fc58a..acce1fbde 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/val/validate.h" - #include #include #include @@ -34,6 +32,7 @@ #include "source/val/basic_block.h" #include "source/val/construct.h" #include "source/val/function.h" +#include "source/val/validate.h" #include "source/val/validation_state.h" namespace spvtools { @@ -755,6 +754,26 @@ spv_result_t StructuredControlFlowChecks( << header_name << " " << header->id(); } } + + if (block->is_type(BlockType::kBlockTypeSelection) || + block->is_type(BlockType::kBlockTypeLoop)) { + size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1; + const auto& merge_inst = _.ordered_instructions()[index]; + if (merge_inst.opcode() == SpvOpSelectionMerge || + merge_inst.opcode() == SpvOpLoopMerge) { + uint32_t merge_id = merge_inst.GetOperandAs(0); + auto merge_block = function->GetBlock(merge_id).first; + if (merge_block->reachable() && + !construct_blocks.count(merge_block)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "Header block " << _.getIdName(block->id()) + << " is contained in the " << construct_name + << " construct headed by " << _.getIdName(header->id()) + << ", but it's merge block " << _.getIdName(merge_id) + << " is not"; + } + } + } } // Checks rules for case constructs. diff --git a/3rdparty/spirv-tools/source/val/validate_debug.cpp b/3rdparty/spirv-tools/source/val/validate_debug.cpp index b49890f26..0a25d8ab7 100644 --- a/3rdparty/spirv-tools/source/val/validate_debug.cpp +++ b/3rdparty/spirv-tools/source/val/validate_debug.cpp @@ -56,13 +56,6 @@ spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) { } // namespace spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) { - if (spvIsWebGPUEnv(_.context()->target_env) && - spvOpcodeIsDebug(inst->opcode())) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "Debugging instructions are not allowed in the WebGPU execution " - << "environment."; - } - switch (inst->opcode()) { case SpvOpMemberName: if (auto error = ValidateMemberName(_, inst)) return error; diff --git a/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.cpp b/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.cpp index bc6d4ee6e..1d15ad624 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.cpp @@ -14,6 +14,7 @@ #include "test/fuzz/fuzz_test_util.h" +#include #include #include "tools/io.h" @@ -105,5 +106,20 @@ void DumpShader(const std::vector& binary, const char* filename) { } } +void DumpTransformationsJson( + const protobufs::TransformationSequence& transformations, + const char* filename) { + std::string json_string; + auto json_options = google::protobuf::util::JsonOptions(); + json_options.add_whitespace = true; + auto json_generation_status = google::protobuf::util::MessageToJsonString( + transformations, &json_string, json_options); + if (json_generation_status == google::protobuf::util::Status::OK) { + std::ofstream transformations_json_file(filename); + transformations_json_file << json_string; + transformations_json_file.close(); + } +} + } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.h b/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.h index 93f37e567..9e08bf689 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.h +++ b/3rdparty/spirv-tools/test/fuzz/fuzz_test_util.h @@ -19,6 +19,7 @@ #include +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/opt/build_module.h" #include "source/opt/ir_context.h" #include "spirv-tools/libspirv.h" @@ -100,6 +101,12 @@ void DumpShader(opt::IRContext* context, const char* filename); // Dumps |binary| to file |filename|. Useful for interactive debugging. void DumpShader(const std::vector& binary, const char* filename); +// Dumps |transformations| to file |filename| in JSON format. Useful for +// interactive debugging. +void DumpTransformationsJson( + const protobufs::TransformationSequence& transformations, + const char* filename); + } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp b/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp index 22a26fc6b..17998c5a7 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp @@ -62,13 +62,11 @@ void RunFuzzerAndReplayer(const std::string& shader, for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) { std::vector fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; - spvtools::FuzzerOptions fuzzer_options; - spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed); - Fuzzer fuzzer(env); + Fuzzer fuzzer(env, seed, true); fuzzer.SetMessageConsumer(kSilentConsumer); auto fuzzer_result_status = - fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out, + fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out, &fuzzer_transformation_sequence_out); ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status); ASSERT_TRUE(t.Validate(fuzzer_binary_out)); diff --git a/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp b/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp index 648507031..8b72e3c53 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp @@ -165,12 +165,10 @@ void RunFuzzerAndShrinker(const std::string& shader, // Run the fuzzer and check that it successfully yields a valid binary. std::vector fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; - spvtools::FuzzerOptions fuzzer_options; - spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed); - Fuzzer fuzzer(env); + Fuzzer fuzzer(env, seed, true); fuzzer.SetMessageConsumer(kSilentConsumer); auto fuzzer_result_status = - fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out, + fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out, &fuzzer_transformation_sequence_out); ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status); ASSERT_TRUE(t.Validate(fuzzer_binary_out)); diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp index 8173e7269..5892848e2 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp @@ -209,117 +209,6 @@ TEST(TransformationAddDeadContinueTest, SimpleExample) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -TEST(TransformationAddDeadContinueTest, - DoNotAllowContinueToMergeBlockOfAnotherLoop) { - // A loop header must dominate its merge block if that merge block is - // reachable. We are thus not allowed to add a dead continue that would result - // in violation of this property. This test checks for such a scenario. - - std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %16 %139 - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %8 = OpTypeBool - %14 = OpTypeVector %6 4 - %15 = OpTypePointer Input %14 - %16 = OpVariable %15 Input - %138 = OpTypePointer Output %14 - %139 = OpVariable %138 Output - %400 = OpConstantTrue %8 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpBranch %500 - %500 = OpLabel - OpLoopMerge %501 %502 None - OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502 - %503 = OpLabel - OpLoopMerge %502 %504 None - OpBranchConditional %400 %505 %504 - %505 = OpLabel - OpBranch %502 - %504 = OpLabel - OpBranch %503 - %502 = OpLabel - OpBranchConditional %400 %501 %500 - %501 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - ASSERT_TRUE(IsValid(env, context.get())); - FactManager fact_manager; - - ASSERT_FALSE(TransformationAddDeadContinue(500, true, {}) - .IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(TransformationAddDeadContinue(500, false, {}) - .IsApplicable(context.get(), fact_manager)); -} - -TEST(TransformationAddDeadContinueTest, DoNotAllowContinueToSelectionMerge) { - // A selection header must dominate its merge block if that merge block is - // reachable. We are thus not allowed to add a dead continue that would result - // in violation of this property. This test checks for such a scenario. - - std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %16 %139 - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %8 = OpTypeBool - %14 = OpTypeVector %6 4 - %15 = OpTypePointer Input %14 - %16 = OpVariable %15 Input - %138 = OpTypePointer Output %14 - %139 = OpVariable %138 Output - %400 = OpConstantTrue %8 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpBranch %500 - %500 = OpLabel - OpLoopMerge %501 %502 None - OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502 - %503 = OpLabel - OpSelectionMerge %502 None - OpBranchConditional %400 %505 %504 - %505 = OpLabel - OpBranch %502 - %504 = OpLabel - OpBranch %502 - %502 = OpLabel - OpBranchConditional %400 %501 %500 - %501 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - ASSERT_TRUE(IsValid(env, context.get())); - FactManager fact_manager; - - ASSERT_FALSE(TransformationAddDeadContinue(500, true, {}) - .IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(TransformationAddDeadContinue(500, false, {}) - .IsApplicable(context.get(), fact_manager)); -} - TEST(TransformationAddDeadContinueTest, LoopNest) { // Checks some allowed and disallowed scenarios for a nest of loops, including // continuing a loop from an if or switch. @@ -1420,7 +1309,6 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous2) { OpLoopMerge %1557 %1570 None OpBranchConditional %395 %1562 %1557 %1562 = OpLabel - OpSelectionMerge %1570 None OpBranchConditional %395 %1571 %1570 %1571 = OpLabel OpBranch %1557 diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp index 9d3fde002..b489f7159 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp @@ -291,7 +291,7 @@ TEST(TransformationCopyObjectTest, CheckIllegalCases) { %31 = OpLabel %42 = OpAccessChain %36 %18 %41 %43 = OpLoad %11 %42 - OpSelectionMerge %47 None + OpSelectionMerge %45 None OpSwitch %43 %46 0 %44 1 %45 %46 = OpLabel %69 = OpIAdd %11 %96 %27 diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 75e117ae8..41b611667 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -159,18 +159,20 @@ const std::string kComplexShader = R"( %65 = OpAccessChain %13 %11 %64 %66 = OpLoad %6 %65 %67 = OpSGreaterThan %29 %84 %66 - OpSelectionMerge %69 None + OpSelectionMerge %1000 None OpBranchConditional %67 %68 %72 %68 = OpLabel %71 = OpIAdd %6 %84 %26 - OpBranch %69 + OpBranch %1000 %72 = OpLabel %74 = OpIAdd %6 %84 %64 %205 = OpCopyObject %6 %74 - OpBranch %69 - %69 = OpLabel + OpBranch %1000 + %1000 = OpLabel %86 = OpPhi %6 %71 %68 %74 %72 %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %69 + %69 = OpLabel OpBranch %20 %22 = OpLabel %75 = OpAccessChain %46 %42 %50 @@ -421,18 +423,20 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { %65 = OpAccessChain %13 %11 %64 %66 = OpLoad %6 %65 %67 = OpSGreaterThan %29 %84 %66 - OpSelectionMerge %69 None + OpSelectionMerge %1000 None OpBranchConditional %67 %68 %72 %68 = OpLabel %71 = OpIAdd %6 %84 %26 - OpBranch %69 + OpBranch %1000 %72 = OpLabel %74 = OpIAdd %6 %84 %64 %205 = OpCopyObject %6 %74 - OpBranch %69 - %69 = OpLabel + OpBranch %1000 + %1000 = OpLabel %86 = OpPhi %6 %71 %68 %205 %72 %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %69 + %69 = OpLabel OpBranch %20 %22 = OpLabel %75 = OpAccessChain %46 %42 %50 diff --git a/3rdparty/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp b/3rdparty/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp index 9e5197d5b..e7303baab 100644 --- a/3rdparty/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp +++ b/3rdparty/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp @@ -5932,7 +5932,6 @@ OpBranch %42 %42 = OpLabel %43 = OpLoad %int %i %44 = OpSLessThan %bool %43 %int_1 -OpSelectionMerge %45 None OpBranchConditional %44 %46 %40 %46 = OpLabel %47 = OpLoad %int %i diff --git a/3rdparty/spirv-tools/test/opt/block_merge_test.cpp b/3rdparty/spirv-tools/test/opt/block_merge_test.cpp index f87fd8048..11fba736c 100644 --- a/3rdparty/spirv-tools/test/opt/block_merge_test.cpp +++ b/3rdparty/spirv-tools/test/opt/block_merge_test.cpp @@ -447,10 +447,53 @@ OpBranch %header OpLoopMerge %merge %continue None OpBranch %inner_header %inner_header = OpLabel -OpSelectionMerge %continue None -OpBranchConditional %true %then %continue +OpSelectionMerge %if_merge None +OpBranchConditional %true %then %if_merge %then = OpLabel OpBranch %continue +%if_merge = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranchConditional %false %merge %header +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, CannotMergeContinue) { + const std::string text = R"( +; CHECK: OpBranch [[loop_header:%\w+]] +; CHECK: [[loop_header]] = OpLabel +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]] +; CHECK: [[if_header]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK: [[continue]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %true %inner_header %merge +%inner_header = OpLabel +OpSelectionMerge %if_merge None +OpBranchConditional %true %then %if_merge +%then = OpLabel +OpBranch %continue +%if_merge = OpLabel +OpBranch %continue %continue = OpLabel OpBranchConditional %false %merge %header %merge = OpLabel diff --git a/3rdparty/spirv-tools/test/opt/dead_branch_elim_test.cpp b/3rdparty/spirv-tools/test/opt/dead_branch_elim_test.cpp index 5d41bdc10..e61286762 100644 --- a/3rdparty/spirv-tools/test/opt/dead_branch_elim_test.cpp +++ b/3rdparty/spirv-tools/test/opt/dead_branch_elim_test.cpp @@ -2798,7 +2798,9 @@ OpReturn OpFunctionEnd )"; - SinglePassRunAndMatch(predefs + body, true); + // The selection merge in the loop naming the continue target as merge is + // invalid, but handled by this pass so validation is disabled. + SinglePassRunAndMatch(predefs + body, false); } TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) { @@ -2942,7 +2944,9 @@ OpReturn OpFunctionEnd )"; - SinglePassRunAndMatch(body, true); + // The selection merge in the loop naming the continue target as merge is + // invalid, but handled by this pass so validation is disabled. + SinglePassRunAndMatch(body, false); } TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) { diff --git a/3rdparty/spirv-tools/test/opt/inline_test.cpp b/3rdparty/spirv-tools/test/opt/inline_test.cpp index fac49ca6a..f44c04a05 100644 --- a/3rdparty/spirv-tools/test/opt/inline_test.cpp +++ b/3rdparty/spirv-tools/test/opt/inline_test.cpp @@ -1819,7 +1819,7 @@ OpFunctionEnd %9 = OpLabel OpBranch %10 %10 = OpLabel -OpLoopMerge %12 %13 None +OpLoopMerge %12 %10 None OpBranch %13 %13 = OpLabel OpBranchConditional %true %10 %12 @@ -1980,7 +1980,7 @@ OpFunctionEnd OpBranch %13 %13 = OpLabel %14 = OpCopyObject %bool %false -OpLoopMerge %16 %19 None +OpLoopMerge %16 %13 None OpBranch %17 %17 = OpLabel %18 = OpCopyObject %bool %true @@ -2145,7 +2145,7 @@ OpBranch %19 %19 = OpLabel %20 = OpCopyObject %int %int_2 %25 = OpCopyObject %int %int_0 -OpLoopMerge %23 %26 None +OpLoopMerge %23 %19 None OpBranch %26 %27 = OpLabel %28 = OpCopyObject %int %int_1 diff --git a/3rdparty/spirv-tools/test/opt/local_ssa_elim_test.cpp b/3rdparty/spirv-tools/test/opt/local_ssa_elim_test.cpp index f10d118e7..7afbb4cf4 100644 --- a/3rdparty/spirv-tools/test/opt/local_ssa_elim_test.cpp +++ b/3rdparty/spirv-tools/test/opt/local_ssa_elim_test.cpp @@ -193,7 +193,24 @@ OpDecorate %fo Location 0 )"; const std::string before = - R"(%main = OpFunction %void None %9 + R"( +; CHECK: = OpFunction +; CHECK-NEXT: [[entry:%\w+]] = OpLabel +; CHECK: [[outer_header:%\w+]] = OpLabel +; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]] +; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]] +; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]] +; CHECK: [[inner_pre_header:%\w+]] = OpLabel +; CHECK: [[inner_header:%\w+]] = OpLabel +; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]] +; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]] +; CHECK: [[inner_be]] = OpLabel +; CHECK: [[f_next]] = OpFAdd %float [[inner_f]] +; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1 +; CHECK: [[outer_be]] = OpLabel +; CHECK: [[i_next]] = OpIAdd +; CHECK: OpStore %fo [[outer_f]] +%main = OpFunction %void None %9 %24 = OpLabel %f = OpVariable %_ptr_Function_float Function %i = OpVariable %_ptr_Function_int Function @@ -212,8 +229,8 @@ OpBranch %31 %31 = OpLabel %32 = OpLoad %int %j %33 = OpSLessThan %bool %32 %int_4 -OpLoopMerge %29 %34 None -OpBranchConditional %33 %34 %29 +OpLoopMerge %50 %34 None +OpBranchConditional %33 %34 %50 %34 = OpLabel %35 = OpLoad %float %f %36 = OpLoad %int %i @@ -226,6 +243,8 @@ OpStore %f %40 %42 = OpIAdd %int %41 %int_1 OpStore %j %42 OpBranch %31 +%50 = OpLabel +OpBranch %29 %29 = OpLabel %43 = OpLoad %int %i %44 = OpIAdd %int %43 %int_1 @@ -238,50 +257,7 @@ OpReturn OpFunctionEnd )"; - const std::string after = - R"(%main = OpFunction %void None %9 -%24 = OpLabel -%f = OpVariable %_ptr_Function_float Function -%i = OpVariable %_ptr_Function_int Function -%j = OpVariable %_ptr_Function_int Function -OpStore %f %float_0 -OpStore %i %int_0 -OpBranch %25 -%25 = OpLabel -%47 = OpPhi %float %float_0 %24 %50 %29 -%46 = OpPhi %int %int_0 %24 %44 %29 -%27 = OpSLessThan %bool %46 %int_4 -OpLoopMerge %28 %29 None -OpBranchConditional %27 %30 %28 -%30 = OpLabel -OpStore %j %int_0 -OpBranch %31 -%31 = OpLabel -%50 = OpPhi %float %47 %30 %40 %34 -%48 = OpPhi %int %int_0 %30 %42 %34 -%33 = OpSLessThan %bool %48 %int_4 -OpLoopMerge %29 %34 None -OpBranchConditional %33 %34 %29 -%34 = OpLabel -%38 = OpAccessChain %_ptr_Input_float %BC %46 %48 -%39 = OpLoad %float %38 -%40 = OpFAdd %float %50 %39 -OpStore %f %40 -%42 = OpIAdd %int %48 %int_1 -OpStore %j %42 -OpBranch %31 -%29 = OpLabel -%44 = OpIAdd %int %46 %int_1 -OpStore %i %44 -OpBranch %25 -%28 = OpLabel -OpStore %fo %47 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck(predefs + before, predefs + after, true, - true); + SinglePassRunAndMatch(predefs + before, true); } TEST_F(LocalSSAElimTest, ForLoopWithContinue) { diff --git a/3rdparty/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp b/3rdparty/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp index 41d796fd9..56b0b76f4 100644 --- a/3rdparty/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp +++ b/3rdparty/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp @@ -3177,7 +3177,7 @@ TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) { %21 = OpLabel %29 = OpSMod %6 %96 %28 %30 = OpIEqual %17 %29 %9 - OpSelectionMerge %23 None + OpSelectionMerge %sel_merge None OpBranchConditional %30 %31 %48 %31 = OpLabel %44 = OpAccessChain %7 %41 %91 %96 @@ -3187,8 +3187,10 @@ TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) { OpStore %47 %46 OpBranch %32 %48 = OpLabel - OpBranch %23 + OpBranch %sel_merge %32 = OpLabel + OpBranch %sel_merge + %sel_merge = OpLabel OpBranch %23 %23 = OpLabel %52 = OpIAdd %6 %96 %51 diff --git a/3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp b/3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp index a30568063..411816960 100644 --- a/3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp +++ b/3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp @@ -1390,7 +1390,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) { OpLoopMerge %36 %40 None OpBranch %35 %35 = OpLabel - OpSelectionMerge %40 None OpBranchConditional %77 %39 %40 %39 = OpLabel OpReturnValue %16 @@ -1402,7 +1401,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) { OpLoopMerge %45 %49 None OpBranch %44 %44 = OpLabel - OpSelectionMerge %49 None OpBranchConditional %77 %48 %49 %48 = OpLabel OpReturnValue %16 @@ -1415,7 +1413,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) { OpLoopMerge %64 %68 None OpBranch %63 %63 = OpLabel - OpSelectionMerge %68 None OpBranchConditional %77 %67 %68 %67 = OpLabel OpReturnValue %16 @@ -1813,12 +1810,14 @@ TEST_F(MergeReturnPassTest, PhiInSecondMerge) { OpLoopMerge %11 %12 None OpBranch %13 %13 = OpLabel - OpLoopMerge %12 %14 None - OpBranchConditional %8 %15 %12 + OpLoopMerge %18 %14 None + OpBranchConditional %8 %15 %18 %15 = OpLabel OpReturn %14 = OpLabel OpBranch %13 + %18 = OpLabel + OpBranch %12 %12 = OpLabel %16 = OpUndef %float OpBranchConditional %8 %10 %11 diff --git a/3rdparty/spirv-tools/test/opt/register_liveness.cpp b/3rdparty/spirv-tools/test/opt/register_liveness.cpp index cb973d2e6..7cb210f1e 100644 --- a/3rdparty/spirv-tools/test/opt/register_liveness.cpp +++ b/3rdparty/spirv-tools/test/opt/register_liveness.cpp @@ -1277,6 +1277,46 @@ TEST_F(PassClassTest, FissionSimulation) { } } +// Test that register liveness does not fail when there is an unreachable block. +// We are not testing if the liveness is computed correctly because the specific +// results do not matter for unreachable blocks. +TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginLowerLeft + OpSource GLSL 330 + OpSourceExtension "GL_ARB_shading_language_420pack" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %2 = OpFunction %void None %4 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %9 + %9 = OpLabel + OpBranch %7 + %8 = OpLabel + OpBranch %6 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function* f = &*module->begin(); + LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); + liveness_analysis->Get(f); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/strip_reflect_info_test.cpp b/3rdparty/spirv-tools/test/opt/strip_reflect_info_test.cpp index a9cfd4763..5db34b720 100644 --- a/3rdparty/spirv-tools/test/opt/strip_reflect_info_test.cpp +++ b/3rdparty/spirv-tools/test/opt/strip_reflect_info_test.cpp @@ -13,6 +13,9 @@ // limitations under the License. #include +#include "gmock/gmock.h" + +#include "spirv-tools/optimizer.hpp" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" @@ -23,6 +26,50 @@ namespace { using StripLineReflectInfoTest = PassTest<::testing::Test>; +// This test acts as an end-to-end code example on how to strip +// reflection info from a SPIR-V module. Use this code pattern +// when you have compiled HLSL code with Glslang or DXC using +// option -fhlsl_functionality1 to insert reflection information, +// but then want to filter out the extra instructions before sending +// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. +TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::vector binary_in; + tools.Assemble(before, &binary_in); + + // Instantiate the optimizer, and run the strip-reflection-info + // pass over the |binary_in| module, and place the modified module + // into |binary_out|. + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); + optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass()); + std::vector binary_out; + optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); + + // Check results + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + EXPECT_THAT(disassembly, testing::Eq(after)); +} + +// This test is functionally the same as the end-to-end test above, +// but uses the test SinglePassRunAndCheck test fixture instead. TEST_F(StripLineReflectInfoTest, StripHlslSemantic) { // This is a non-sensical example, but exercises the instructions. std::string before = R"(OpCapability Shader diff --git a/3rdparty/spirv-tools/test/reduce/reducer_test.cpp b/3rdparty/spirv-tools/test/reduce/reducer_test.cpp index a650d3bec..59f280369 100644 --- a/3rdparty/spirv-tools/test/reduce/reducer_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/reducer_test.cpp @@ -125,19 +125,21 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel @@ -193,11 +195,13 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { OpLoopMerge %12 %34 None OpBranchConditional %100 %11 %12 %11 = OpLabel - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %100 %33 %46 %33 = OpLabel - OpBranch %34 + OpBranch %90 %46 = OpLabel + OpBranch %90 + %90 = OpLabel OpBranch %34 %34 = OpLabel OpBranch %10 @@ -345,7 +349,6 @@ const std::string kShaderWithLoopsDivAndMul = R"( OpLoopMerge %33 %38 None OpBranch %32 %32 = OpLabel - OpSelectionMerge %38 None OpBranchConditional %30 %37 %38 %37 = OpLabel OpSelectionMerge %42 None diff --git a/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp b/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp index 4051bac1e..2981c2ed8 100644 --- a/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp @@ -13,7 +13,6 @@ // limitations under the License. #include "source/reduce/reducer.h" - #include "source/reduce/reduction_opportunity.h" #include "source/reduce/remove_instruction_reduction_opportunity.h" #include "test/reduce/reduce_test_util.h" @@ -23,8 +22,8 @@ namespace reduce { namespace { using opt::Function; -using opt::IRContext; using opt::Instruction; +using opt::IRContext; // A dumb reduction opportunity finder that finds opportunities to remove global // values regardless of whether they are referenced. This is very likely to make @@ -181,19 +180,21 @@ TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) { %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel @@ -303,19 +304,21 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) { %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel @@ -392,19 +395,21 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) { %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel diff --git a/3rdparty/spirv-tools/test/tools/opt/flags.py b/3rdparty/spirv-tools/test/tools/opt/flags.py index b34a16884..2f6c0a793 100644 --- a/3rdparty/spirv-tools/test/tools/opt/flags.py +++ b/3rdparty/spirv-tools/test/tools/opt/flags.py @@ -187,28 +187,35 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', - 'eliminate-dead-code-aggressive', + 'eliminate-dead-functions', 'private-to-local', - 'scalar-replacement=100', - 'convert-local-access-chains', - 'eliminate-local-single-block', - 'eliminate-local-single-store', - 'eliminate-dead-code-aggressive', - 'simplify-instructions', - 'eliminate-dead-inserts', + 'scalar-replacement=0', 'ssa-rewrite', - 'eliminate-dead-code-aggressive', 'ccp', + 'loop-unroll', + 'eliminate-dead-branches', + 'simplify-instructions', + 'scalar-replacement=0', + 'eliminate-local-single-store', + 'if-conversion', + 'simplify-instructions', 'eliminate-dead-code-aggressive', 'eliminate-dead-branches', - 'if-conversion', - 'eliminate-dead-code-aggressive', 'merge-blocks', - 'simplify-instructions', - 'eliminate-dead-inserts', - 'redundancy-elimination', - 'cfg-cleanup', + 'convert-local-access-chains', + 'eliminate-local-single-block', 'eliminate-dead-code-aggressive', + 'copy-propagate-arrays', + 'vector-dce', + 'eliminate-dead-inserts', + 'eliminate-dead-members', + 'eliminate-local-single-store', + 'merge-blocks', + 'ssa-rewrite', + 'redundancy-elimination', + 'simplify-instructions', + 'eliminate-dead-code-aggressive', + 'cfg-cleanup', ] shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') output = placeholder.TempFileName('output.spv') diff --git a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp index f06f36c14..22dd11792 100644 --- a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp @@ -23,7 +23,6 @@ #include #include "gmock/gmock.h" - #include "source/diagnostic.h" #include "source/spirv_target_env.h" #include "source/val/validate.h" @@ -1355,7 +1354,7 @@ std::string GetReachableMergeAndContinue(SpvCapability cap, } if (cap == SpvCapabilityShader) { branch.AppendBody("OpLoopMerge %merge %target None\n"); - body.AppendBody("OpSelectionMerge %target None\n"); + body.AppendBody("OpSelectionMerge %f None\n"); } if (!spvIsWebGPUEnv(env)) @@ -1650,25 +1649,27 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) { Block entry("entry"); Block loop1("loop1", SpvOpBranchConditional); Block loop2("loop2", SpvOpBranchConditional); - Block loop2_merge("loop2_merge", SpvOpBranchConditional); + Block loop2_merge("loop2_merge"); + Block loop1_cont("loop1_cont", SpvOpBranchConditional); Block be_block("be_block"); Block exit("exit", SpvOpReturn); entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { - loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n"); + loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n"); loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); } - std::string str = GetDefaultHeader(GetParam()) + - nameOps("loop1", "loop2", "be_block", "loop2_merge") + - types_consts() + - "%func = OpFunction %voidt None %funct\n"; + std::string str = + GetDefaultHeader(GetParam()) + + nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") + + types_consts() + "%func = OpFunction %voidt None %funct\n"; str += entry >> loop1; str += loop1 >> std::vector({loop2, exit}); str += loop2 >> std::vector({loop2, loop2_merge}); - str += loop2_merge >> std::vector({be_block, exit}); + str += loop2_merge >> loop1_cont; + str += loop1_cont >> std::vector({be_block, exit}); str += be_block >> loop1; str += exit; str += "OpFunctionEnd"; @@ -1678,7 +1679,7 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), MatchesRegex("The continue construct with the continue target " - ".\\[%loop2_merge\\] is not post dominated by the " + ".\\[%loop1_cont\\] is not post dominated by the " "back-edge block .\\[%be_block\\]\n" " %be_block = OpLabel\n")); } else { @@ -2037,13 +2038,10 @@ TEST_P(ValidateCFG, EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); } -TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) { - // This example is valid. It shows that the validator can't just add - // an edge from the loop head to the continue target. If that edge - // is added, then the "if_merge" block is both the continue target - // for the loop and also the merge block for the nested selection, but - // then it wouldn't be dominated by "if_head", the header block for the - // nested selection. +TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) { + // The continue construct cannot be the merge target of a nested selection + // because the loop construct must contain "if_merge" because it contains + // "if_head". bool is_shader = GetParam() == SpvCapabilityShader; Block entry("entry"); Block loop("loop"); @@ -2072,7 +2070,16 @@ TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) { str += "OpFunctionEnd"; CompileSuccessfully(str); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); + if (is_shader) { + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Header block 3[%if_head] is contained in the loop construct " + "headed " + "by 2[%loop], but it's merge block 5[%if_merge] is not")); + } else { + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); + } } TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) { @@ -3681,17 +3688,21 @@ OpBranch %7 OpLoopMerge %8 %9 None OpBranch %10 %10 = OpLabel -OpLoopMerge %9 %11 None +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %3 %14 %15 +%15 = OpLabel +OpBranch %8 +%14 = OpLabel OpBranch %12 %12 = OpLabel -OpSelectionMerge %11 None -OpBranchConditional %3 %11 %13 -%13 = OpLabel -OpBranch %8 +OpBranchConditional %3 %10 %11 %11 = OpLabel -OpBranchConditional %3 %9 %10 +OpBranch %9 %9 = OpLabel -OpBranchConditional %3 %8 %7 +OpBranchConditional %3 %7 %8 %8 = OpLabel OpReturn OpFunctionEnd @@ -3700,7 +3711,7 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("block 13[%13] exits the loop headed by " + HasSubstr("block 15[%15] exits the loop headed by " "10[%10], but not via a structured exit")); } @@ -3726,9 +3737,11 @@ OpBranchConditional %undef %11 %12 OpSelectionMerge %31 None OpBranchConditional %undef %30 %31 %30 = OpLabel -OpSelectionMerge %37 None -OpBranchConditional %undef %36 %37 +OpSelectionMerge %38 None +OpBranchConditional %undef %36 %38 %36 = OpLabel +OpBranch %38 +%38 = OpLabel OpBranch %37 %37 = OpLabel OpBranch %10 @@ -4100,6 +4113,80 @@ OpFunctionEnd EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); } +TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %loop "loop" +OpName %continue "continue" +OpName %body "body" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpSelectionMerge %continue None +OpBranchConditional %undef %exit %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Header block 3[%body] is contained in the loop construct headed by " + "1[%loop], but it's merge block 2[%continue] is not")); +} + +TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %loop "loop" +OpName %continue "continue" +OpName %inner "inner" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranchConditional %undef %exit %inner +%inner = OpLabel +OpLoopMerge %continue %inner None +OpBranchConditional %undef %inner %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Header block 3[%inner] is contained in the loop construct headed by " + "1[%loop], but it's merge block 2[%continue] is not")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp b/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp index 8f625551b..e81fc7ce3 100644 --- a/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp @@ -54,117 +54,170 @@ TEST_F(ValidateWebGPU, OpUndefIsDisallowed) { EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed")); } -TEST_F(ValidateWebGPU, OpNameIsDisallowed) { +TEST_F(ValidateWebGPU, OpNameIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpName %1 "foo" -%1 = OpTypeFloat 32 + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpName %1 "foo" + %1 = OpTypeFloat 32 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpName %foo \"foo\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) { +TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpMemberName %2 0 "foo" -%1 = OpTypeFloat 32 -%2 = OpTypeStruct %1 + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpMemberName %2 0 "foo" + %1 = OpTypeFloat 32 + %2 = OpTypeStruct %1 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd + )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpMemberName %_struct_1 0 " - "\"foo\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateWebGPU, OpSourceIsDisallowed) { +TEST_F(ValidateWebGPU, OpSourceIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpSource GLSL 450 + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSource GLSL 450 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpSource GLSL 450\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -// OpSourceContinued does not have a test case, because it requires being -// preceded by OpSource, which will cause a validation error. - -TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) { +TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpSourceExtension "bar" + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSource GLSL 450 + OpSourceContinued "I am a happy shader! Yay! ;" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpSourceExtension " - "\"bar\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateWebGPU, OpStringIsDisallowed) { +TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR -%1 = OpString "foo" + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSourceExtension "bar" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n %1 = OpString \"foo\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -// OpLine does not have a test case, because it requires being preceded by -// OpString, which will cause a validation error. - -TEST_F(ValidateWebGPU, OpNoLineDisallowed) { +TEST_F(ValidateWebGPU, OpStringIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpNoLine + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + %1 = OpString "foo" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpNoLine\n")); +TEST_F(ValidateWebGPU, OpLineIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + %1 = OpString "minimal.vert" + OpLine %1 1 1 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpNoLineIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpNoLine + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) { @@ -183,7 +236,6 @@ TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) { )"; CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); }