diff --git a/3rdparty/spirv-tools/DEPS b/3rdparty/spirv-tools/DEPS index 9a8e92265..f4557e971 100644 --- a/3rdparty/spirv-tools/DEPS +++ b/3rdparty/spirv-tools/DEPS @@ -1,174 +1,26 @@ use_relative_paths = True vars = { - 'chromium_git': 'https://chromium.googlesource.com', 'github': 'https://github.com', - 'build_revision': '037f38ae0fe5e11b4f7c33b750fd7a1e9634a606', - 'buildtools_revision': 'ab7b6a7b350dd15804c87c20ce78982811fdd76f', - 'clang_revision': 'abe5e4f9dc0f1df848c7a0efa05256253e77a7b7', - 'effcee_revision': '04b624799f5a9dbaf3fa1dbed2ba9dce2fc8dcf2', - 'googletest_revision': '98a0d007d7092b72eea0e501bb9ad17908a1a036', - 'testing_revision': '340252637e2e7c72c0901dcbeeacfff419e19b59', - 're2_revision': '6cf8ccd82dbaab2668e9b13596c68183c9ecd13f', - 'spirv_headers_revision': '8b911bd2ba37677037b38c9bd286c7c05701bcda', + 'effcee_revision': 'b83b58d177b797edd1f94c5f10837f2cc2863f0a', + 'googletest_revision': '2f42d769ad1b08742f7ccb5ad4dd357fc5ff248c', + 're2_revision': '848dfb7e1d7ba641d598cb66f81590f3999a555a', + 'spirv_headers_revision': 'de99d4d834aeb51dd9f099baa285bd44fd04bb3d', } deps = { - "build": - Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'), - - 'buildtools': - Var('chromium_git') + '/chromium/buildtools.git@' + - Var('buildtools_revision'), - - 'external/spirv-headers': - Var('github') + '/KhronosGroup/SPIRV-Headers.git@' + - Var('spirv_headers_revision'), + 'external/effcee': + Var('github') + '/google/effcee.git@' + Var('effcee_revision'), 'external/googletest': Var('github') + '/google/googletest.git@' + Var('googletest_revision'), - 'external/effcee': - Var('github') + '/google/effcee.git@' + Var('effcee_revision'), - 'external/re2': Var('github') + '/google/re2.git@' + Var('re2_revision'), - 'testing': - Var('chromium_git') + '/chromium/src/testing@' + - Var('testing_revision'), - - 'tools/clang': - Var('chromium_git') + '/chromium/src/tools/clang@' + Var('clang_revision') + 'external/spirv-headers': + Var('github') + '/KhronosGroup/SPIRV-Headers.git@' + + Var('spirv_headers_revision'), } -recursedeps = [ - # buildtools provides clang_format, libc++, and libc++api - 'buildtools', -] - -hooks = [ - { - 'name': 'gn_win', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=win32', - '--no_auth', - '--bucket', 'chromium-gn', - '-s', 'SPIRV-Tools/buildtools/win/gn.exe.sha1', - ], - }, - { - 'name': 'gn_mac', - 'pattern': '.', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=darwin', - '--no_auth', - '--bucket', 'chromium-gn', - '-s', 'SPIRV-Tools/buildtools/mac/gn.sha1', - ], - }, - { - 'name': 'gn_linux64', - 'pattern': '.', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=linux*', - '--no_auth', - '--bucket', 'chromium-gn', - '-s', 'SPIRV-Tools/buildtools/linux64/gn.sha1', - ], - }, - # Pull clang-format binaries using checked-in hashes. - { - 'name': 'clang_format_win', - 'pattern': '.', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=win32', - '--no_auth', - '--bucket', 'chromium-clang-format', - '-s', 'SPIRV-Tools/buildtools/win/clang-format.exe.sha1', - ], - }, - { - 'name': 'clang_format_mac', - 'pattern': '.', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=darwin', - '--no_auth', - '--bucket', 'chromium-clang-format', - '-s', 'SPIRV-Tools/buildtools/mac/clang-format.sha1', - ], - }, - { - 'name': 'clang_format_linux', - 'pattern': '.', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=linux*', - '--no_auth', - '--bucket', 'chromium-clang-format', - '-s', 'SPIRV-Tools/buildtools/linux64/clang-format.sha1', - ], - }, - { - # Pull clang - 'name': 'clang', - 'pattern': '.', - 'action': ['python', - 'SPIRV-Tools/tools/clang/scripts/update.py' - ], - }, - { - 'name': 'sysroot_arm', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_arm', - 'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=arm'], - }, - { - 'name': 'sysroot_arm64', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_arm64', - 'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=arm64'], - }, - { - 'name': 'sysroot_x86', - 'pattern': '.', - 'condition': 'checkout_linux and (checkout_x86 or checkout_x64)', - 'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=x86'], - }, - { - 'name': 'sysroot_mips', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_mips', - 'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=mips'], - }, - { - 'name': 'sysroot_x64', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_x64', - 'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=x64'], - }, - { - # Update the Windows toolchain if necessary. - 'name': 'win_toolchain', - 'pattern': '.', - 'condition': 'checkout_win', - 'action': ['python', 'SPIRV-Tools/build/vs_toolchain.py', 'update', '--force'], - }, - { - # Update the Mac toolchain if necessary. - 'name': 'mac_toolchain', - 'pattern': '.', - 'action': ['python', 'SPIRV-Tools/build/mac_toolchain.py'], - }, -] diff --git a/3rdparty/spirv-tools/README.md b/3rdparty/spirv-tools/README.md index 9737f7e78..8a5c85e11 100644 --- a/3rdparty/spirv-tools/README.md +++ b/3rdparty/spirv-tools/README.md @@ -270,6 +270,10 @@ mkdir build && cd build cmake [-G ] ``` +*Note*: +The script `utils/git-sync-deps` can be used to checkout and/or update the +contents of the repos under `external/` instead of manually maintaining them. + Once the build files have been generated, build using your preferred development environment. @@ -344,6 +348,13 @@ $ANDROID_NDK/ndk-build -C ../android_test \ NDK_APP_OUT=`pwd`/app ``` +### Updating DEPS +Occasionally the entries in DEPS will need to be updated. This is done on demand +when there is a request to do this, often due to downstream breakages. There is +a script `utils/roll_deps.sh` provided, which will generate a patch with the +updated DEPS values. This will still need to be tested in your checkout to +confirm that there are no integration issues that need to be resolved. + ## Library ### Usage diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 0947e22df..d5efa220f 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-47-g59983a60" +"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-60-gdf86bb44" diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index 9121a5396..a12e4f4fb 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -32,6 +32,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_useful_constructs.h + fuzzer_pass_obfuscate_constants.h fuzzer_pass_permute_blocks.h fuzzer_pass_split_blocks.h fuzzer_util.h @@ -46,8 +47,10 @@ if(SPIRV_BUILD_FUZZER) transformation_add_type_boolean.h transformation_add_type_float.h transformation_add_type_int.h + transformation_add_type_pointer.h transformation_move_block_down.h transformation_replace_boolean_constant_with_constant_binary.h + transformation_replace_constant_with_uniform.h transformation_split_block.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -58,6 +61,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_useful_constructs.cpp + fuzzer_pass_obfuscate_constants.cpp fuzzer_pass_permute_blocks.cpp fuzzer_pass_split_blocks.cpp fuzzer_util.cpp @@ -71,8 +75,10 @@ if(SPIRV_BUILD_FUZZER) transformation_add_type_boolean.cpp transformation_add_type_float.cpp transformation_add_type_int.cpp + transformation_add_type_pointer.cpp transformation_move_block_down.cpp transformation_replace_boolean_constant_with_constant_binary.cpp + transformation_replace_constant_with_uniform.cpp transformation_split_block.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc diff --git a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp index 37169415b..9cb6896a4 100644 --- a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp @@ -64,6 +64,11 @@ struct FactManager::ConstantUniformFacts { const protobufs::FactConstantUniform& constant_uniform_fact, uint32_t type_id) const; + // Checks that the width of a floating-point constant is supported, and that + // the constant is finite. + bool FloatingPointValueIsSuitable(const protobufs::FactConstantUniform& fact, + uint32_t width) const; + std::vector> facts_and_type_ids; }; @@ -169,22 +174,48 @@ FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown() return result; } +bool FactManager::ConstantUniformFacts::FloatingPointValueIsSuitable( + const protobufs::FactConstantUniform& fact, uint32_t width) const { + const uint32_t kFloatWidth = 32; + const uint32_t kDoubleWidth = 64; + if (width != kFloatWidth && width != kDoubleWidth) { + // Only 32- and 64-bit floating-point types are handled. + return false; + } + std::vector words = GetConstantWords(fact); + if (width == 32) { + float value; + memcpy(&value, words.data(), sizeof(float)); + if (!std::isfinite(value)) { + return false; + } + } else { + double value; + memcpy(&value, words.data(), sizeof(double)); + if (!std::isfinite(value)) { + return false; + } + } + return true; +} + bool FactManager::ConstantUniformFacts::AddFact( const protobufs::FactConstantUniform& fact, opt::IRContext* context) { - auto should_be_uniform_variable = context->get_def_use_mgr()->GetDef( - fact.uniform_buffer_element_descriptor().uniform_variable_id()); - if (!should_be_uniform_variable) { - return false; - } - if (SpvOpVariable != should_be_uniform_variable->opcode()) { - return false; - } - if (SpvStorageClassUniform != - should_be_uniform_variable->GetSingleWordInOperand(0)) { + // Try to find a unique instruction that declares a variable such that the + // variable is decorated with the descriptor set and binding associated with + // the constant uniform fact. + opt::Instruction* uniform_variable = FindUniformVariable( + fact.uniform_buffer_element_descriptor(), context, true); + + if (!uniform_variable) { return false; } + + assert(SpvOpVariable == uniform_variable->opcode()); + assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0)); + auto should_be_uniform_pointer_type = - context->get_type_mgr()->GetType(should_be_uniform_variable->type_id()); + context->get_type_mgr()->GetType(uniform_variable->type_id()); if (!should_be_uniform_pointer_type->AsPointer()) { return false; } @@ -193,7 +224,7 @@ bool FactManager::ConstantUniformFacts::AddFact( return false; } auto should_be_uniform_pointer_instruction = - context->get_def_use_mgr()->GetDef(should_be_uniform_variable->type_id()); + context->get_def_use_mgr()->GetDef(uniform_variable->type_id()); auto element_type = should_be_uniform_pointer_instruction->GetSingleWordInOperand(1); @@ -236,6 +267,12 @@ bool FactManager::ConstantUniformFacts::AddFact( auto width = final_element_type->AsFloat() ? final_element_type->AsFloat()->width() : final_element_type->AsInteger()->width(); + + if (final_element_type->AsFloat() && + !FloatingPointValueIsSuitable(fact, width)) { + return false; + } + auto required_words = (width + 32 - 1) / 32; if (static_cast(fact.constant_word().size()) != required_words) { return false; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index 25c24fc9d..42033e20f 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -21,6 +21,7 @@ #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" #include "source/fuzz/fuzzer_pass_add_useful_constructs.h" +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" #include "source/fuzz/fuzzer_pass_permute_blocks.h" #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" @@ -113,8 +114,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( FuzzerPassAddDeadBreaks(ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out) .Apply(); - - // TODO(afd) Various other passes will be added. + FuzzerPassObfuscateConstants(ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out) + .Apply(); // Finally, give the blocks in the module a good shake-up. FuzzerPassPermuteBlocks(ir_context.get(), &fact_manager, &fuzzer_context, diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp index b50dfab14..9252341ba 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -14,6 +14,8 @@ #include "source/fuzz/fuzzer_context.h" +#include + namespace spvtools { namespace fuzz { @@ -24,8 +26,19 @@ namespace { const uint32_t kDefaultChanceOfAddingDeadBreak = 20; const uint32_t kDefaultChanceOfMovingBlockDown = 25; +const uint32_t kDefaultChanceOfObfuscatingConstant = 20; const uint32_t kDefaultChanceOfSplittingBlock = 20; +// Default functions for controlling how deep to go during recursive +// generation/transformation. Keep them in alphabetical order. + +const std::function + kDefaultGoDeeperInConstantObfuscation = + [](uint32_t current_depth, RandomGenerator* random_generator) -> bool { + double chance = 1.0 / std::pow(3.0, static_cast(current_depth + 1)); + return random_generator->RandomDouble() < chance; +}; + } // namespace FuzzerContext::FuzzerContext(RandomGenerator* random_generator, @@ -34,7 +47,10 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, next_fresh_id_(min_fresh_id), chance_of_adding_dead_break_(kDefaultChanceOfAddingDeadBreak), chance_of_moving_block_down_(kDefaultChanceOfMovingBlockDown), - chance_of_splitting_block_(kDefaultChanceOfSplittingBlock) {} + chance_of_obfuscating_constant_(kDefaultChanceOfObfuscatingConstant), + chance_of_splitting_block_(kDefaultChanceOfSplittingBlock), + go_deeper_in_constant_obfuscation_( + kDefaultGoDeeperInConstantObfuscation) {} FuzzerContext::~FuzzerContext() = default; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h index 936ffbc90..2815bf7dd 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -15,6 +15,8 @@ #ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_ #define SOURCE_FUZZ_FUZZER_CONTEXT_H_ +#include + #include "source/fuzz/random_generator.h" #include "source/opt/function.h" @@ -43,8 +45,18 @@ class FuzzerContext { // Keep them in alphabetical order. uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; } uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; } + uint32_t GetChanceOfObfuscatingConstant() { + return chance_of_obfuscating_constant_; + } uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; } + // Probability distributions to control how deeply to recurse. + // Keep them in alphabetical order. + const std::function& + GoDeeperInConstantObfuscation() { + return go_deeper_in_constant_obfuscation_; + } + private: // The source of randomness. RandomGenerator* random_generator_; @@ -55,7 +67,13 @@ class FuzzerContext { // Keep them in alphabetical order. uint32_t chance_of_adding_dead_break_; uint32_t chance_of_moving_block_down_; + uint32_t chance_of_obfuscating_constant_; uint32_t chance_of_splitting_block_; + + // Functions to determine with what probability to go deeper when generating + // or mutating constructs recursively. + const std::function& + go_deeper_in_constant_obfuscation_; }; } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp index 335e3cac0..23606deba 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp @@ -19,6 +19,7 @@ #include "source/fuzz/transformation_add_type_boolean.h" #include "source/fuzz/transformation_add_type_float.h" #include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_pointer.h" namespace spvtools { namespace fuzz { @@ -176,6 +177,55 @@ void FuzzerPassAddUsefulConstructs::Apply() { for (unsigned int& datum : uint_data) { MaybeAddFloatConstant(32, {datum}); } + + // For every known-to-be-constant uniform, make sure we have instructions + // declaring: + // - a pointer type with uniform storage class, whose pointee type is the type + // of the element + // - a signed integer constant for each index required to access the element + // - a constant for the constant value itself + for (auto& fact_and_type_id : + GetFactManager()->GetConstantUniformFactsAndTypes()) { + uint32_t element_type_id = fact_and_type_id.second; + assert(element_type_id); + auto element_type = + GetIRContext()->get_type_mgr()->GetType(element_type_id); + assert(element_type && + "If the constant uniform fact is well-formed, the module must " + "already have a declaration of the type for the uniform element."); + opt::analysis::Pointer uniform_pointer(element_type, + SpvStorageClassUniform); + if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) { + auto add_pointer = transformation::MakeTransformationAddTypePointer( + GetFuzzerContext()->GetFreshId(), SpvStorageClassUniform, + element_type_id); + assert(transformation::IsApplicable(add_pointer, GetIRContext(), + *GetFactManager()) && + "Should be applicable by construction."); + transformation::Apply(add_pointer, GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation()->mutable_add_type_pointer() = + add_pointer; + } + std::vector words; + for (auto word : fact_and_type_id.first.constant_word()) { + words.push_back(word); + } + // We get the element type again as the type manager may have been + // invalidated since we last retrieved it. + element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id); + if (element_type->AsInteger()) { + MaybeAddIntConstant(element_type->AsInteger()->width(), + element_type->AsInteger()->IsSigned(), words); + } else { + assert(element_type->AsFloat() && + "Known uniform values must be integer or floating-point."); + MaybeAddFloatConstant(element_type->AsFloat()->width(), words); + } + for (auto index : + fact_and_type_id.first.uniform_buffer_element_descriptor().index()) { + MaybeAddIntConstant(32, true, {index}); + } + } } } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp new file mode 100644 index 000000000..36fcb8bd5 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -0,0 +1,471 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" + +#include + +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default; + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + const std::vector& greater_than_opcodes, + const std::vector& less_than_opcodes, uint32_t constant_id_1, + uint32_t constant_id_2, bool first_constant_is_larger) { + auto bool_constant_opcode = GetIRContext() + ->get_def_use_mgr() + ->GetDef(bool_constant_use.id_of_interest()) + ->opcode(); + assert((bool_constant_opcode == SpvOpConstantFalse || + bool_constant_opcode == SpvOpConstantTrue) && + "Precondition: this must be a usage of a boolean constant."); + + // Pick an opcode at random. First randomly decide whether to generate + // a 'greater than' or 'less than' kind of opcode, and then select a + // random opcode from the resulting subset. + SpvOp comparison_opcode; + if (GetFuzzerContext()->GetRandomGenerator()->RandomBool()) { + comparison_opcode = greater_than_opcodes + [GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(greater_than_opcodes.size()))]; + } else { + comparison_opcode = less_than_opcodes + [GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(less_than_opcodes.size()))]; + } + + // We now need to decide how to order constant_id_1 and constant_id_2 such + // that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the + // boolean constant. + const bool is_greater_than_opcode = + std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(), + comparison_opcode) != greater_than_opcodes.end(); + uint32_t lhs_id; + uint32_t rhs_id; + if ((bool_constant_opcode == SpvOpConstantTrue && + first_constant_is_larger == is_greater_than_opcode) || + (bool_constant_opcode == SpvOpConstantFalse && + first_constant_is_larger != is_greater_than_opcode)) { + lhs_id = constant_id_1; + rhs_id = constant_id_2; + } else { + lhs_id = constant_id_2; + rhs_id = constant_id_1; + } + + // We can now make a transformation that will replace |bool_constant_use| + // with an expression of the form (written using infix notation): + // |lhs_id| |comparison_opcode| |rhs_id| + auto transformation = transformation:: + MakeTransformationReplaceBooleanConstantWithConstantBinary( + bool_constant_use, lhs_id, rhs_id, comparison_opcode, + GetFuzzerContext()->GetFreshId()); + // The transformation should be applicable by construction. + assert(transformation::IsApplicable(transformation, GetIRContext(), + *GetFactManager())); + + // Applying this transformation yields a pointer to the new instruction that + // computes the result of the binary expression. + auto binary_operator_instruction = + transformation::Apply(transformation, GetIRContext(), GetFactManager()); + + // Add this transformation to the sequence of transformations that have been + // applied. + *GetTransformations() + ->add_transformation() + ->mutable_replace_boolean_constant_with_constant_binary() = + transformation; + + // Having made a binary expression, there may now be opportunities to further + // obfuscate the constants used as the LHS and RHS of the expression (e.g. by + // replacing them with loads from known uniforms). + // + // We thus consider operands 0 and 1 (LHS and RHS in turn). + for (uint32_t index : {0u, 1u}) { + // We randomly decide, based on the current depth of obfuscation, whether + // to further obfuscate this operand. + if (GetFuzzerContext()->GoDeeperInConstantObfuscation()( + depth, GetFuzzerContext()->GetRandomGenerator())) { + auto in_operand_use = transformation::MakeIdUseDescriptor( + binary_operator_instruction->GetSingleWordInOperand(index), + binary_operator_instruction->opcode(), index, + binary_operator_instruction->result_id(), 0); + ObfuscateConstant(depth + 1, in_operand_use); + } + } +} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t float_constant_id_1, uint32_t float_constant_id_2) { + auto float_constant_1 = GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(float_constant_id_1) + ->AsFloatConstant(); + auto float_constant_2 = GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(float_constant_id_2) + ->AsFloatConstant(); + assert(float_constant_1->words() != float_constant_2->words() && + "The constants should not be identical."); + assert(std::isfinite(float_constant_1->GetValueAsDouble()) && + "The constants must be finite numbers."); + assert(std::isfinite(float_constant_2->GetValueAsDouble()) && + "The constants must be finite numbers."); + bool first_constant_is_larger; + assert(float_constant_1->type()->AsFloat()->width() == + float_constant_2->type()->AsFloat()->width() && + "First and second floating-point constants must have the same width."); + if (float_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + float_constant_1->GetFloat() > float_constant_2->GetFloat(); + } else { + assert(float_constant_1->type()->AsFloat()->width() == 64 && + "Supported floating-point widths are 32 and 64."); + first_constant_is_larger = + float_constant_1->GetDouble() > float_constant_2->GetDouble(); + } + std::vector greater_than_opcodes{ + SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan, + SpvOpFUnordGreaterThanEqual}; + std::vector less_than_opcodes{ + SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan, + SpvOpFUnordGreaterThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + float_constant_id_1, float_constant_id_2, first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants:: + ObfuscateBoolConstantViaSignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) { + auto signed_int_constant_1 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(signed_int_constant_id_1) + ->AsIntConstant(); + auto signed_int_constant_2 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(signed_int_constant_id_2) + ->AsIntConstant(); + assert(signed_int_constant_1->words() != signed_int_constant_2->words() && + "The constants should not be identical."); + bool first_constant_is_larger; + assert(signed_int_constant_1->type()->AsInteger()->width() == + signed_int_constant_2->type()->AsInteger()->width() && + "First and second floating-point constants must have the same width."); + assert(signed_int_constant_1->type()->AsInteger()->IsSigned()); + assert(signed_int_constant_2->type()->AsInteger()->IsSigned()); + if (signed_int_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32(); + } else { + assert(signed_int_constant_1->type()->AsFloat()->width() == 64 && + "Supported integer widths are 32 and 64."); + first_constant_is_larger = + signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64(); + } + std::vector greater_than_opcodes{SpvOpSGreaterThan, + SpvOpSGreaterThanEqual}; + std::vector less_than_opcodes{SpvOpSLessThan, SpvOpSLessThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + signed_int_constant_id_1, signed_int_constant_id_2, + first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants:: + ObfuscateBoolConstantViaUnsignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t unsigned_int_constant_id_1, + uint32_t unsigned_int_constant_id_2) { + auto unsigned_int_constant_1 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(unsigned_int_constant_id_1) + ->AsIntConstant(); + auto unsigned_int_constant_2 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(unsigned_int_constant_id_2) + ->AsIntConstant(); + assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() && + "The constants should not be identical."); + bool first_constant_is_larger; + assert(unsigned_int_constant_1->type()->AsInteger()->width() == + unsigned_int_constant_2->type()->AsInteger()->width() && + "First and second floating-point constants must have the same width."); + assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned()); + assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned()); + if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32(); + } else { + assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 && + "Supported integer widths are 32 and 64."); + first_constant_is_larger = + unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64(); + } + std::vector greater_than_opcodes{SpvOpUGreaterThan, + SpvOpUGreaterThanEqual}; + std::vector less_than_opcodes{SpvOpULessThan, SpvOpULessThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + unsigned_int_constant_id_1, unsigned_int_constant_id_2, + first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstant( + uint32_t depth, const protobufs::IdUseDescriptor& constant_use) { + // We want to replace the boolean constant use with a binary expression over + // scalar constants, but only if we can then potentially replace the constants + // with uniforms of the same value. + + auto available_types_with_uniforms = + GetFactManager()->GetTypesForWhichUniformValuesAreKnown(); + if (available_types_with_uniforms.empty()) { + // Do not try to obfuscate if we do not have access to any uniform + // elements with known values. + return; + } + auto chosen_type_id = available_types_with_uniforms + [GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(available_types_with_uniforms.size()))]; + auto available_constants = + GetFactManager()->GetConstantsAvailableFromUniformsForType( + GetIRContext(), chosen_type_id); + if (available_constants.size() == 1) { + // TODO(afd): for now we only obfuscate a boolean if there are at least + // two constants available from uniforms, so that we can do a + // comparison between them. It would be good to be able to do the + // obfuscation even if there is only one such constant, if there is + // also another regular constant available. + return; + } + + // We know we have at least two known-to-be-constant uniforms of the chosen + // type. Pick one of them at random. + auto constant_index_1 = + GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(available_constants.size())); + uint32_t constant_index_2; + + // Now choose another one distinct from the first one. + do { + constant_index_2 = GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(available_constants.size())); + } while (constant_index_1 == constant_index_2); + + auto constant_id_1 = available_constants[constant_index_1]; + auto constant_id_2 = available_constants[constant_index_2]; + + assert(constant_id_1 != 0 && constant_id_2 != 0 && + "We should not find an available constant with an id of 0."); + + // Now perform the obfuscation, according to whether the type of the constants + // is float, signed int, or unsigned int. + auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id); + if (chosen_type->AsFloat()) { + ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use, + constant_id_1, constant_id_2); + } else { + assert(chosen_type->AsInteger() && + "We should only have uniform facts about ints and floats."); + if (chosen_type->AsInteger()->IsSigned()) { + ObfuscateBoolConstantViaSignedIntConstantPair( + depth, constant_use, constant_id_1, constant_id_2); + } else { + ObfuscateBoolConstantViaUnsignedIntConstantPair( + depth, constant_use, constant_id_1, constant_id_2); + } + } +} + +void FuzzerPassObfuscateConstants::ObfuscateScalarConstant( + uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider + // additional ways to obfuscate scalar constants. + + // Check whether we know that any uniforms are guaranteed to be equal to the + // scalar constant associated with |constant_use|. + auto uniform_descriptors = GetFactManager()->GetUniformDescriptorsForConstant( + GetIRContext(), constant_use.id_of_interest()); + if (uniform_descriptors.empty()) { + // No relevant uniforms, so do not obfuscate. + return; + } + + // Choose a random available uniform known to be equal to the constant. + protobufs::UniformBufferElementDescriptor uniform_descriptor = + uniform_descriptors + [GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(uniform_descriptors.size()))]; + // Create, apply and record a transformation to replace the constant use with + // the result of a load from the chosen uniform. + auto transformation = + transformation::MakeTransformationReplaceConstantWithUniform( + constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId()); + // Transformation should be applicable by construction. + assert(transformation::IsApplicable(transformation, GetIRContext(), + *GetFactManager())); + transformation::Apply(transformation, GetIRContext(), GetFactManager()); + *GetTransformations() + ->add_transformation() + ->mutable_replace_constant_with_uniform() = transformation; +} + +void FuzzerPassObfuscateConstants::ObfuscateConstant( + uint32_t depth, const protobufs::IdUseDescriptor& constant_use) { + switch (GetIRContext() + ->get_def_use_mgr() + ->GetDef(constant_use.id_of_interest()) + ->opcode()) { + case SpvOpConstantTrue: + case SpvOpConstantFalse: + ObfuscateBoolConstant(depth, constant_use); + break; + case SpvOpConstant: + ObfuscateScalarConstant(depth, constant_use); + break; + default: + assert(false && "The opcode should be one of the above."); + break; + } +} + +void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse( + const opt::Instruction& inst, uint32_t in_operand_index, + uint32_t base_instruction_result_id, + const std::map& skipped_opcode_count, + std::vector* constant_uses) { + if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) { + // The operand is not an id, so it cannot be a constant id. + return; + } + auto operand_id = inst.GetSingleWordInOperand(in_operand_index); + auto operand_definition = + GetIRContext()->get_def_use_mgr()->GetDef(operand_id); + switch (operand_definition->opcode()) { + case SpvOpConstantFalse: + case SpvOpConstantTrue: + case SpvOpConstant: { + // The operand is a constant id, so make an id use descriptor and record + // it. + protobufs::IdUseDescriptor id_use_descriptor; + id_use_descriptor.set_id_of_interest(operand_id); + id_use_descriptor.set_target_instruction_opcode(inst.opcode()); + id_use_descriptor.set_in_operand_index(in_operand_index); + id_use_descriptor.set_base_instruction_result_id( + base_instruction_result_id); + id_use_descriptor.set_num_opcodes_to_ignore( + skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end() + ? 0 + : skipped_opcode_count.at(inst.opcode())); + constant_uses->push_back(id_use_descriptor); + } break; + default: + break; + } +} + +void FuzzerPassObfuscateConstants::Apply() { + // First, gather up all the constant uses available in the module, by going + // through each block in each function. + std::vector constant_uses; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // For each constant use we encounter we are going to make an id use + // descriptor. An id use is described with respect to a base instruction; + // if there are instructions at the start of the block without result ids, + // the base instruction will have to be the block's label. + uint32_t base_instruction_result_id = block.id(); + + // An id use descriptor also records how many instructions of a particular + // opcode need to be skipped in order to find the instruction of interest + // from the base instruction. We maintain a mapping that records a skip + // count for each relevant opcode. + std::map skipped_opcode_count; + + // Go through each instruction in the block. + for (auto& inst : block) { + if (inst.HasResultId()) { + // The instruction has a result id, so can be used as the base + // instruction from now on, until another instruction with a result id + // is encountered. + base_instruction_result_id = inst.result_id(); + // Opcode skip counts were with respect to the previous base + // instruction and are now irrelevant. + skipped_opcode_count.clear(); + } + + // Consider each operand of the instruction, and add a constant id use + // for the operand if relevant. + for (uint32_t in_operand_index = 0; + in_operand_index < inst.NumInOperands(); in_operand_index++) { + MaybeAddConstantIdUse(inst, in_operand_index, + base_instruction_result_id, + skipped_opcode_count, &constant_uses); + } + + if (!inst.HasResultId()) { + // The instruction has no result id, so in order to identify future id + // uses for instructions with this opcode from the existing base + // instruction, we need to increase the skip count for this opcode. + skipped_opcode_count[inst.opcode()] = + skipped_opcode_count.find(inst.opcode()) == + skipped_opcode_count.end() + ? 1 + : skipped_opcode_count[inst.opcode()] + 1; + } + } + } + } + + // Go through the constant uses in a random order by repeatedly pulling out a + // constant use at a random index. + while (!constant_uses.empty()) { + auto index = GetFuzzerContext()->GetRandomGenerator()->RandomUint32( + static_cast(constant_uses.size())); + auto constant_use = std::move(constant_uses[index]); + constant_uses.erase(constant_uses.begin() + index); + // Decide probabilistically whether to skip or obfuscate this constant use. + if (GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() > + GetFuzzerContext()->GetChanceOfObfuscatingConstant()) { + continue; + } + ObfuscateConstant(0, constant_use); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h new file mode 100644 index 000000000..03477a564 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -0,0 +1,107 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_ +#define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for turning uses of constants into more complex forms. +// Examples include replacing 'true' with '42 < 52', and replacing '42' with +// 'a.b.c' if 'a.b.c' is known to hold the value '42'. +class FuzzerPassObfuscateConstants : public FuzzerPass { + public: + FuzzerPassObfuscateConstants( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassObfuscateConstants() override; + + void Apply() override; + + private: + // Applies 0 or more transformations to potentially obfuscate the constant + // use represented by |constant_use|. The |depth| parameter controls how + // deeply obfuscation can recurse. + void ObfuscateConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // This method will try to turn |constant_use|, required to be a use of a + // boolean constant, into a binary expression on scalar constants, which may + // themselves be recursively obfuscated. + void ObfuscateBoolConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // This method will try to turn |constant_use|, required to be a use of a + // scalar constant, into the value loaded from a uniform known to have the + // same value as the constant (if one exists). + void ObfuscateScalarConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // Applies a transformation to replace the boolean constant usage represented + // by |bool_constant_use| with a binary expression involving + // |float_constant_id_1| and |float_constant_id_2|, which must not be equal + // to one another. Possibly further obfuscates the uses of these float + // constants. The |depth| parameter controls how deeply obfuscation can + // recurse. + void ObfuscateBoolConstantViaFloatConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t float_constant_id_1, uint32_t float_constant_id_2); + + // Similar to the above, but for signed int constants. + void ObfuscateBoolConstantViaSignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2); + + // Similar to the above, but for unsigned int constants. + void ObfuscateBoolConstantViaUnsignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t unsigned_int_constant_id_1, uint32_t unsigned_int_constant_id_2); + + // A helper method to capture the common parts of the above methods. + // The method is used to obfuscate the boolean constant usage represented by + // |bool_constant_use| by replacing it with '|constant_id_1| OP + // |constant_id_2|', where 'OP' is chosen from either |greater_than_opcodes| + // or |less_than_opcodes|. + // + // The two constant ids must not represent the same value, and thus + // |greater_than_opcodes| may include 'greater than or equal' opcodes + // (similar for |less_than_opcodes|). + void ObfuscateBoolConstantViaConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + const std::vector& greater_than_opcodes, + const std::vector& less_than_opcodes, uint32_t constant_id_1, + uint32_t constant_id_2, bool first_constant_is_larger); + + // A helper method to determine whether input operand |in_operand_index| of + // |inst| is the id of a constant, and add an id use descriptor to + // |candidate_constant_uses| if so. The other parameters are used for id use + // descriptor construction. + void MaybeAddConstantIdUse( + const opt::Instruction& inst, uint32_t in_operand_index, + uint32_t base_instruction_result_id, + const std::map& skipped_opcode_count, + std::vector* constant_uses); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // #define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_ diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index 4bf82521f..aecd4c7a9 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -64,8 +64,9 @@ message UniformBufferElementDescriptor { // is contained, and (b) a series of indices that need to be followed to get // to the element (via fields and array/vector indices). // - // Example: suppose %42 is the id of a uniform variable, and that the uniform - // variable has the following type (using GLSL-like syntax): + // Example: suppose there is a uniform variable with descriptor set 7 and + // binding 9, and that the uniform variable has the following type (using + // GLSL-like syntax): // // struct S { // float f; @@ -74,16 +75,17 @@ message UniformBufferElementDescriptor { // }; // // Then: - // - 42[0] describes the 'f' field. - // - 42[1,1] describes the y component of the 'g' field. - // - 42[2,7,3] describes the w component of element 7 of the 'h' field + // - (7, 9, [0]) describes the 'f' field. + // - (7, 9, [1,1]) describes the y component of the 'g' field. + // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field - // The result id of a uniform variable. - uint32 uniform_variable_id = 1; + // The descriptor set and binding associated with a uniform variable. + uint32 descriptor_set = 1; + uint32 binding = 2; // An ordered sequence of indices through composite structures in the // uniform buffer. - repeated uint32 index = 2; + repeated uint32 index = 3; } @@ -133,6 +135,8 @@ message Transformation { TransformationAddTypeInt add_type_int = 7; TransformationAddDeadBreak add_dead_break = 8; TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9; + TransformationAddTypePointer add_type_pointer = 10; + TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; // Add additional option using the next available number. } } @@ -222,6 +226,22 @@ message TransformationAddTypeInt { } +message TransformationAddTypePointer { + + // Adds OpTypePointer to the module, with the given storage class and base + // type + + // Id to be used for the type + uint32 fresh_id = 1; + + // Pointer storage class + uint32 storage_class = 2; + + // Id of the base type for the pointer + uint32 base_type_id = 3; + +} + message TransformationMoveBlockDown { // A transformation that moves a basic block to be one position lower in @@ -229,6 +249,24 @@ message TransformationMoveBlockDown { // The id of the block to move down. uint32 block_id = 1; +} + +message TransformationReplaceConstantWithUniform { + + // Replaces a use of a constant id with the the result of a load from an + // element of uniform buffer known to hold the same value as the constant + + // A descriptor for the id we would like to replace + IdUseDescriptor id_use_descriptor = 1; + + // Uniform descriptor to identify which uniform value to choose + UniformBufferElementDescriptor uniform_descriptor = 2; + + // Id that will store the result of an access chain + uint32 fresh_id_for_access_chain = 3; + + // Id that will store the result of a load + uint32 fresh_id_for_load = 4; } diff --git a/3rdparty/spirv-tools/source/fuzz/replayer.cpp b/3rdparty/spirv-tools/source/fuzz/replayer.cpp index ccea91f98..9c9967549 100644 --- a/3rdparty/spirv-tools/source/fuzz/replayer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/replayer.cpp @@ -24,8 +24,10 @@ #include "source/fuzz/transformation_add_type_boolean.h" #include "source/fuzz/transformation_add_type_float.h" #include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_pointer.h" #include "source/fuzz/transformation_move_block_down.h" #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" #include "source/fuzz/transformation_split_block.h" #include "source/opt/build_module.h" #include "source/util/make_unique.h" @@ -58,6 +60,9 @@ bool IsApplicable(const protobufs::Transformation& transformation, case protobufs::Transformation::TransformationCase::kAddTypeInt: return transformation::IsApplicable(transformation.add_type_int(), context, fact_manager); + case protobufs::Transformation::TransformationCase::kAddTypePointer: + return transformation::IsApplicable(transformation.add_type_pointer(), + context, fact_manager); case protobufs::Transformation::TransformationCase::kMoveBlockDown: return transformation::IsApplicable(transformation.move_block_down(), context, fact_manager); @@ -66,6 +71,11 @@ bool IsApplicable(const protobufs::Transformation& transformation, return transformation::IsApplicable( transformation.replace_boolean_constant_with_constant_binary(), context, fact_manager); + case protobufs::Transformation::TransformationCase:: + kReplaceConstantWithUniform: + return transformation::IsApplicable( + transformation.replace_constant_with_uniform(), context, + fact_manager); case protobufs::Transformation::TransformationCase::kSplitBlock: return transformation::IsApplicable(transformation.split_block(), context, fact_manager); @@ -107,6 +117,10 @@ void Apply(const protobufs::Transformation& transformation, transformation::Apply(transformation.add_type_int(), context, fact_manager); break; + case protobufs::Transformation::TransformationCase::kAddTypePointer: + transformation::Apply(transformation.add_type_pointer(), context, + fact_manager); + break; case protobufs::Transformation::TransformationCase::kMoveBlockDown: transformation::Apply(transformation.move_block_down(), context, fact_manager); @@ -117,6 +131,11 @@ void Apply(const protobufs::Transformation& transformation, transformation.replace_boolean_constant_with_constant_binary(), context, fact_manager); break; + case protobufs::Transformation::TransformationCase:: + kReplaceConstantWithUniform: + transformation::Apply(transformation.replace_constant_with_uniform(), + context, fact_manager); + break; case protobufs::Transformation::TransformationCase::kSplitBlock: transformation::Apply(transformation.split_block(), context, fact_manager); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp new file mode 100644 index 000000000..9098cba89 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_pointer.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace transformation { + +using opt::IRContext; + +bool IsApplicable(const protobufs::TransformationAddTypePointer& message, + IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(context, message.fresh_id())) { + return false; + } + // The base type must be known. + return context->get_type_mgr()->GetType(message.base_type_id()) != nullptr; +} + +void Apply(const protobufs::TransformationAddTypePointer& message, + IRContext* context, spvtools::fuzz::FactManager* /*unused*/) { + // Add the pointer type. + opt::Instruction::OperandList in_operands = { + {SPV_OPERAND_TYPE_STORAGE_CLASS, {message.storage_class()}}, + {SPV_OPERAND_TYPE_ID, {message.base_type_id()}}}; + context->module()->AddType(MakeUnique( + context, SpvOpTypePointer, 0, message.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(context, message.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone); +} + +protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer( + uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) { + protobufs::TransformationAddTypePointer result; + result.set_fresh_id(fresh_id); + result.set_storage_class(storage_class); + result.set_base_type_id(base_type_id); + return result; +} + +} // namespace transformation +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.h new file mode 100644 index 000000000..2cc4d30a2 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.h @@ -0,0 +1,44 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace transformation { + +// - |message.fresh_id| must not be used by the module +// - |message.base_type_id| must be the result id of an OpType[...] instruction +bool IsApplicable(const protobufs::TransformationAddTypePointer& message, + opt::IRContext* context, const FactManager& fact_manager); + +// Adds an OpTypePointer instruction with the given storage class and base type +// to the module. +void Apply(const protobufs::TransformationAddTypePointer& message, + opt::IRContext* context, FactManager* fact_manager); + +// Helper factory to create a transformation message. +protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer( + uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id); + +} // namespace transformation +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp new file mode 100644 index 000000000..cc6e1374a --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -0,0 +1,230 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_constant_with_uniform.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +namespace spvtools { +namespace fuzz { +namespace transformation { + +namespace { + +std::unique_ptr MakeAccessChainInstruction( + const protobufs::TransformationReplaceConstantWithUniform& message, + spvtools::opt::IRContext* context, uint32_t constant_type_id) { + // The input operands for the access chain. + opt::Instruction::OperandList operands_for_access_chain; + + opt::Instruction* uniform_variable = + FindUniformVariable(message.uniform_descriptor(), context, false); + + // The first input operand is the id of the uniform variable. + operands_for_access_chain.push_back( + {SPV_OPERAND_TYPE_ID, {uniform_variable->result_id()}}); + + // The other input operands are the ids of the constants used to index into + // the uniform. The uniform buffer descriptor specifies a series of literals; + // for each we find the id of the instruction that defines it, and add these + // instruction ids as operands. + opt::analysis::Integer int_type(32, true); + auto registered_int_type = + context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = context->get_type_mgr()->GetId(&int_type); + for (auto index : message.uniform_descriptor().index()) { + opt::analysis::IntConstant int_constant(registered_int_type, {index}); + auto constant_id = context->get_constant_mgr()->FindDeclaredConstant( + &int_constant, int_type_id); + operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}}); + } + + // The type id for the access chain is a uniform pointer with base type + // matching the given constant id type. + auto type_and_pointer_type = context->get_type_mgr()->GetTypeAndPointerType( + constant_type_id, SpvStorageClassUniform); + assert(type_and_pointer_type.first != nullptr); + assert(type_and_pointer_type.second != nullptr); + auto pointer_to_uniform_constant_type_id = + context->get_type_mgr()->GetId(type_and_pointer_type.second.get()); + + return MakeUnique( + context, SpvOpAccessChain, pointer_to_uniform_constant_type_id, + message.fresh_id_for_access_chain(), operands_for_access_chain); +} + +std::unique_ptr MakeLoadInstruction( + const protobufs::TransformationReplaceConstantWithUniform& message, + spvtools::opt::IRContext* context, uint32_t constant_type_id) { + opt::Instruction::OperandList operands_for_load = { + {SPV_OPERAND_TYPE_ID, {message.fresh_id_for_access_chain()}}}; + return MakeUnique(context, SpvOpLoad, constant_type_id, + message.fresh_id_for_load(), + operands_for_load); +} + +} // namespace + +bool IsApplicable( + const protobufs::TransformationReplaceConstantWithUniform& message, + spvtools::opt::IRContext* context, + const spvtools::fuzz::FactManager& fact_manager) { + // The following is really an invariant of the transformation rather than + // merely a requirement of the precondition. We check it here since we cannot + // check it in the message constructor. + assert(message.fresh_id_for_access_chain() != message.fresh_id_for_load() && + "Fresh ids for access chain and load result cannot be the same."); + + // The ids for the access chain and load instructions must both be fresh. + if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_access_chain())) { + return false; + } + if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_load())) { + return false; + } + + // The id specified in the id use descriptor must be that of a declared scalar + // constant. + auto declared_constant = context->get_constant_mgr()->FindDeclaredConstant( + message.id_use_descriptor().id_of_interest()); + if (!declared_constant) { + return false; + } + if (!declared_constant->AsScalarConstant()) { + return false; + } + + // The fact manager needs to believe that the uniform data element described + // by the uniform buffer element descriptor will hold a scalar value. + auto constant_id_associated_with_uniform = + fact_manager.GetConstantFromUniformDescriptor( + context, message.uniform_descriptor()); + if (!constant_id_associated_with_uniform) { + return false; + } + auto constant_associated_with_uniform = + context->get_constant_mgr()->FindDeclaredConstant( + constant_id_associated_with_uniform); + assert(constant_associated_with_uniform && + "The constant should be present in the module."); + if (!constant_associated_with_uniform->AsScalarConstant()) { + return false; + } + + // The types and values of the scalar value held in the id specified by the id + // use descriptor and in the uniform data element specified by the uniform + // buffer element descriptor need to match on both type and value. + if (!declared_constant->type()->IsSame( + constant_associated_with_uniform->type())) { + return false; + } + if (declared_constant->AsScalarConstant()->words() != + constant_associated_with_uniform->AsScalarConstant()->words()) { + return false; + } + + // The id use descriptor must identify some instruction with respect to the + // module. + auto instruction_using_constant = + transformation::FindInstruction(message.id_use_descriptor(), context); + if (!instruction_using_constant) { + return false; + } + + // The module needs to have a uniform pointer type suitable for indexing into + // the uniform variable, i.e. matching the type of the constant we wish to + // replace with a uniform. + opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(), + SpvStorageClassUniform); + if (!context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) { + return false; + } + + // In order to index into the uniform, the module has got to contain the int32 + // type, plus an OpConstant for each of the indices of interest. + opt::analysis::Integer int_type(32, true); + if (!context->get_type_mgr()->GetId(&int_type)) { + return false; + } + auto registered_int_type = + context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = context->get_type_mgr()->GetId(&int_type); + for (auto index : message.uniform_descriptor().index()) { + opt::analysis::IntConstant int_constant(registered_int_type, {index}); + if (!context->get_constant_mgr()->FindDeclaredConstant(&int_constant, + int_type_id)) { + return false; + } + } + + return true; +} + +void Apply(const protobufs::TransformationReplaceConstantWithUniform& message, + spvtools::opt::IRContext* context, + spvtools::fuzz::FactManager* /*unused*/) { + // Get the instruction that contains the id use we wish to replace. + auto instruction_containing_constant_use = + transformation::FindInstruction(message.id_use_descriptor(), context); + assert(instruction_containing_constant_use && + "Precondition requires that the id use can be found."); + assert(instruction_containing_constant_use->GetSingleWordInOperand( + message.id_use_descriptor().in_operand_index()) == + message.id_use_descriptor().id_of_interest() && + "Does not appear to be a usage of the desired id."); + + // The id of the type for the constant whose use we wish to replace. + auto constant_type_id = + context->get_def_use_mgr() + ->GetDef(message.id_use_descriptor().id_of_interest()) + ->type_id(); + + // Add an access chain instruction to target the uniform element. + instruction_containing_constant_use->InsertBefore( + MakeAccessChainInstruction(message, context, constant_type_id)); + + // Add a load from this access chain. + instruction_containing_constant_use->InsertBefore( + MakeLoadInstruction(message, context, constant_type_id)); + + // Adjust the instruction containing the usage of the constant so that this + // usage refers instead to the result of the load. + instruction_containing_constant_use->SetInOperand( + message.id_use_descriptor().in_operand_index(), + {message.fresh_id_for_load()}); + + // Update the module id bound to reflect the new instructions. + fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_load()); + fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_access_chain()); + + context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::TransformationReplaceConstantWithUniform +MakeTransformationReplaceConstantWithUniform( + protobufs::IdUseDescriptor id_use, + protobufs::UniformBufferElementDescriptor uniform_descriptor, + uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) { + protobufs::TransformationReplaceConstantWithUniform result; + *result.mutable_id_use_descriptor() = std::move(id_use); + *result.mutable_uniform_descriptor() = std::move(uniform_descriptor); + result.set_fresh_id_for_access_chain(fresh_id_for_access_chain); + result.set_fresh_id_for_load(fresh_id_for_load); + return result; +} + +} // namespace transformation +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h new file mode 100644 index 000000000..0112e8078 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h @@ -0,0 +1,73 @@ +#include + +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace transformation { + +// - |fresh_id_for_access_chain| and |fresh_id_for_load| must be distinct fresh +// ids. +// - |uniform_descriptor| specifies a result id and a list of integer literal +// indices. +// As an example, suppose |uniform_descriptor| is (18, [0, 1, 0]) +// It is required that: +// - the result id (18 in our example) is the id of some uniform variable +// - the module contains an integer constant instruction corresponding to +// each of the literal indices; in our example there must thus be +// OpConstant instructions %A and %B say for each of 0 and 1 +// - it is legitimate to index into the uniform variable using the +// sequence of indices; in our example this means indexing into %18 using +// the sequence %A %B %A +// - the module contains a uniform pointer type corresponding to the type +// of the uniform data element obtained by following these indices +// - |id_use_descriptor| identifies the use of some id %C. It is required that: +// - this use does indeed exist in the module +// - %C is an OpConstant +// - According to the fact manager, the uniform data element specified by +// |uniform_descriptor| holds a value with the same type and value as %C +bool IsApplicable( + const protobufs::TransformationReplaceConstantWithUniform& message, + opt::IRContext* context, const FactManager& fact_manager); + +// - Introduces two new instructions: +// - An access chain targeting the uniform data element specified by +// |uniform_descriptor|, with result id |fresh_id_for_access_chain| +// - A load from this access chain, with id |fresh_id_for_load| +// - Replaces the id use specified by |id_use_descriptor| with +// |fresh_id_for_load| +void Apply(const protobufs::TransformationReplaceConstantWithUniform& message, + opt::IRContext* context, FactManager* fact_manager); + +// Helper factory to create a transformation message. +protobufs::TransformationReplaceConstantWithUniform +MakeTransformationReplaceConstantWithUniform( + protobufs::IdUseDescriptor id_use, + protobufs::UniformBufferElementDescriptor uniform_descriptor, + uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load); + +} // namespace transformation +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp b/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp index cd840d3b6..8c758e421 100644 --- a/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp +++ b/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp @@ -14,13 +14,17 @@ #include "source/fuzz/uniform_buffer_element_descriptor.h" +#include + namespace spvtools { namespace fuzz { protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( - uint32_t uniform_variable_id, std::vector&& indices) { + uint32_t descriptor_set, uint32_t binding, + std::vector&& indices) { protobufs::UniformBufferElementDescriptor result; - result.set_uniform_variable_id(uniform_variable_id); + result.set_descriptor_set(descriptor_set); + result.set_binding(binding); for (auto index : indices) { result.add_index(index); } @@ -30,10 +34,85 @@ protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( bool UniformBufferElementDescriptorEquals::operator()( const protobufs::UniformBufferElementDescriptor* first, const protobufs::UniformBufferElementDescriptor* second) const { - return first->uniform_variable_id() == second->uniform_variable_id() && + return first->descriptor_set() == second->descriptor_set() && + first->binding() == second->binding() && + first->index().size() == second->index().size() && std::equal(first->index().begin(), first->index().end(), second->index().begin()); } +opt::Instruction* FindUniformVariable( + const protobufs::UniformBufferElementDescriptor& + uniform_buffer_element_descriptor, + opt::IRContext* context, bool check_unique) { + opt::Instruction* result = nullptr; + + for (auto& inst : context->types_values()) { + // Consider all global variables with uniform storage class. + if (inst.opcode() != SpvOpVariable) { + continue; + } + if (inst.GetSingleWordInOperand(0) != SpvStorageClassUniform) { + continue; + } + + // Determine whether the variable is decorated with a descriptor set + // matching that in |uniform_buffer_element|. + bool descriptor_set_matches = false; + context->get_decoration_mgr()->ForEachDecoration( + inst.result_id(), SpvDecorationDescriptorSet, + [&descriptor_set_matches, &uniform_buffer_element_descriptor]( + const opt::Instruction& decoration_inst) { + const uint32_t kDescriptorSetOperandIndex = 2; + if (decoration_inst.GetSingleWordInOperand( + kDescriptorSetOperandIndex) == + uniform_buffer_element_descriptor.descriptor_set()) { + descriptor_set_matches = true; + } + }); + if (!descriptor_set_matches) { + // Descriptor set does not match. + continue; + } + + // Determine whether the variable is decorated with a binding matching that + // in |uniform_buffer_element|. + bool binding_matches = false; + context->get_decoration_mgr()->ForEachDecoration( + inst.result_id(), SpvDecorationBinding, + [&binding_matches, &uniform_buffer_element_descriptor]( + const opt::Instruction& decoration_inst) { + const uint32_t kBindingOperandIndex = 2; + if (decoration_inst.GetSingleWordInOperand(kBindingOperandIndex) == + uniform_buffer_element_descriptor.binding()) { + binding_matches = true; + } + }); + if (!binding_matches) { + // Binding does not match. + continue; + } + + // This instruction is a uniform variable with the right descriptor set and + // binding. + if (!check_unique) { + // If we aren't checking uniqueness, return it. + return &inst; + } + + if (result) { + // More than one uniform variable is decorated with the given descriptor + // set and binding. This means the fact is ambiguous. + return nullptr; + } + result = &inst; + } + + // We get here either if no match was found, or if |check_unique| holds and + // exactly one match was found. + assert(result == nullptr || check_unique); + return result; +} + } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h b/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h index b2e596231..23a16f0df 100644 --- a/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h +++ b/3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h @@ -19,6 +19,8 @@ #include #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" namespace spvtools { namespace fuzz { @@ -26,7 +28,7 @@ namespace fuzz { // Factory method to create a uniform buffer element descriptor message from an // id and list of indices. protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( - uint32_t uniform_variable_id, std::vector&& indices); + uint32_t descriptor_set, uint32_t binding, std::vector&& indices); // Equality function for uniform buffer element descriptors. struct UniformBufferElementDescriptorEquals { @@ -35,6 +37,16 @@ struct UniformBufferElementDescriptorEquals { const protobufs::UniformBufferElementDescriptor* second) const; }; +// Returns a pointer to an OpVariable in |context| that is decorated with the +// descriptor set and binding associated with |uniform_buffer_element|. Returns +// nullptr if no such variable exists. If multiple such variables exist, a +// pointer to an arbitrary one of the associated instructions is returned if +// |check_unique| is false, and nullptr is returned if |check_unique| is true. +opt::Instruction* FindUniformVariable( + const protobufs::UniformBufferElementDescriptor& + uniform_buffer_element_descriptor, + opt::IRContext* context, bool check_unique); + } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp index 4063eb5a6..a4bef6c9f 100644 --- a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -868,6 +868,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_AMD_gpu_shader_half_float_fetch", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", diff --git a/3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.cpp index 7762fba4d..86889a66e 100644 --- a/3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.cpp @@ -579,6 +579,7 @@ void CommonUniformElimPass::InitExtensions() { "SPV_AMD_gpu_shader_half_float_fetch", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", diff --git a/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp b/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp index 2258e6101..6eb130e35 100644 --- a/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp @@ -343,6 +343,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_AMD_gpu_shader_half_float_fetch", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", diff --git a/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp index 3e2eb7ef4..cc1b83746 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp @@ -246,6 +246,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_AMD_gpu_shader_half_float_fetch", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", diff --git a/3rdparty/spirv-tools/source/opt/local_ssa_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_ssa_elim_pass.cpp index df327a69c..299bbe068 100644 --- a/3rdparty/spirv-tools/source/opt/local_ssa_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_ssa_elim_pass.cpp @@ -95,6 +95,7 @@ void LocalMultiStoreElimPass::InitExtensions() { "SPV_AMD_gpu_shader_half_float_fetch", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", diff --git a/3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp b/3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp index 31c0abdba..ef9f62035 100644 --- a/3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp +++ b/3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp @@ -25,6 +25,12 @@ namespace spvtools { namespace opt { Pass::Status UpgradeMemoryModel::Process() { + // TODO: This pass needs changes to support cooperative matrices. + if (context()->get_feature_mgr()->HasCapability( + SpvCapabilityCooperativeMatrixNV)) { + return Pass::Status::SuccessWithoutChange; + } + // Only update Logical GLSL450 to Logical VulkanKHR. Instruction* memory_model = get_module()->GetMemoryModel(); if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical || @@ -110,6 +116,12 @@ void UpgradeMemoryModel::UpgradeInstructions() { } }); } + + UpgradeMemoryAndImages(); + UpgradeAtomics(); +} + +void UpgradeMemoryModel::UpgradeMemoryAndImages() { for (auto& func : *get_module()) { func.ForEachInst([this](Instruction* inst) { bool is_coherent = false; @@ -239,6 +251,50 @@ void UpgradeMemoryModel::UpgradeInstructions() { } } +void UpgradeMemoryModel::UpgradeAtomics() { + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + if (spvOpcodeIsAtomicOp(inst->opcode())) { + bool unused_coherent = false; + bool is_volatile = false; + SpvScope unused_scope = SpvScopeQueueFamilyKHR; + std::tie(unused_coherent, is_volatile, unused_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0)); + + UpgradeSemantics(inst, 2u, is_volatile); + if (inst->opcode() == SpvOpAtomicCompareExchange || + inst->opcode() == SpvOpAtomicCompareExchangeWeak) { + UpgradeSemantics(inst, 3u, is_volatile); + } + } + }); + } +} + +void UpgradeMemoryModel::UpgradeSemantics(Instruction* inst, + uint32_t in_operand, + bool is_volatile) { + if (!is_volatile) return; + + uint32_t semantics_id = inst->GetSingleWordInOperand(in_operand); + const analysis::Constant* constant = + context()->get_constant_mgr()->FindDeclaredConstant(semantics_id); + const analysis::Integer* type = constant->type()->AsInteger(); + assert(type && type->width() == 32); + uint32_t value = 0; + if (type->IsSigned()) { + value = static_cast(constant->GetS32()); + } else { + value = constant->GetU32(); + } + + value |= SpvMemorySemanticsVolatileMask; + auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value}); + auto new_semantics = + context()->get_constant_mgr()->GetDefiningInstruction(new_constant); + inst->SetInOperand(in_operand, {new_semantics->result_id()}); +} + std::tuple UpgradeMemoryModel::GetInstructionAttributes( uint32_t id) { // |id| is a pointer used in a memory/image instruction. Need to determine if diff --git a/3rdparty/spirv-tools/source/opt/upgrade_memory_model.h b/3rdparty/spirv-tools/source/opt/upgrade_memory_model.h index 0dcd4fd0f..f75304ed0 100644 --- a/3rdparty/spirv-tools/source/opt/upgrade_memory_model.h +++ b/3rdparty/spirv-tools/source/opt/upgrade_memory_model.h @@ -57,12 +57,19 @@ class UpgradeMemoryModel : public Pass { // capability and extension. void UpgradeMemoryModelInstruction(); - // Upgrades memory, image and barrier instructions. + // Upgrades memory, image and atomic instructions. // Memory and image instructions convert coherent and volatile decorations - // into flags on the instruction. Barriers in tessellation shaders get the - // output storage semantic if appropriate. + // into flags on the instruction. + // Atomic memory semantics convert volatile decoration into flags on the + // instruction. void UpgradeInstructions(); + // Upgrades memory and image operands for instructions that have them. + void UpgradeMemoryAndImages(); + + // Adds the volatile memory semantic if necessary. + void UpgradeAtomics(); + // Returns whether |id| is coherent and/or volatile. std::tuple GetInstructionAttributes(uint32_t id); @@ -95,6 +102,11 @@ class UpgradeMemoryModel : public Pass { bool is_volatile, OperationType operation_type, InstructionType inst_type); + // Modifies the semantics at |in_operand| of |inst| to include the volatile + // bit if |is_volatile| is true. + void UpgradeSemantics(Instruction* inst, uint32_t in_operand, + bool is_volatile); + // Returns the result id for a constant for |scope|. uint32_t GetScopeConstant(SpvScope scope); diff --git a/3rdparty/spirv-tools/source/spirv_target_env.cpp b/3rdparty/spirv-tools/source/spirv_target_env.cpp index 09a12197d..f14d44552 100644 --- a/3rdparty/spirv-tools/source/spirv_target_env.cpp +++ b/3rdparty/spirv-tools/source/spirv_target_env.cpp @@ -103,80 +103,45 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { return SPV_SPIRV_VERSION_WORD(0, 0); } +static const std::pair spvTargetEnvNameMap[] = { + {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {"vulkan1.0", SPV_ENV_VULKAN_1_0}, + {"vulkan1.1", SPV_ENV_VULKAN_1_1}, + {"spv1.0", SPV_ENV_UNIVERSAL_1_0}, + {"spv1.1", SPV_ENV_UNIVERSAL_1_1}, + {"spv1.2", SPV_ENV_UNIVERSAL_1_2}, + {"spv1.3", SPV_ENV_UNIVERSAL_1_3}, + {"spv1.4", SPV_ENV_UNIVERSAL_1_4}, + {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2}, + {"opencl1.2", SPV_ENV_OPENCL_1_2}, + {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0}, + {"opencl2.0", SPV_ENV_OPENCL_2_0}, + {"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1}, + {"opencl2.1", SPV_ENV_OPENCL_2_1}, + {"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2}, + {"opencl2.2", SPV_ENV_OPENCL_2_2}, + {"opengl4.0", SPV_ENV_OPENGL_4_0}, + {"opengl4.1", SPV_ENV_OPENGL_4_1}, + {"opengl4.2", SPV_ENV_OPENGL_4_2}, + {"opengl4.3", SPV_ENV_OPENGL_4_3}, + {"opengl4.5", SPV_ENV_OPENGL_4_5}, + {"webgpu0", SPV_ENV_WEBGPU_0}, +}; + bool spvParseTargetEnv(const char* s, spv_target_env* env) { auto match = [s](const char* b) { return s && (0 == strncmp(s, b, strlen(b))); }; - if (match("vulkan1.1spv1.4")) { - if (env) *env = SPV_ENV_VULKAN_1_1_SPIRV_1_4; - return true; - } else if (match("vulkan1.0")) { - if (env) *env = SPV_ENV_VULKAN_1_0; - return true; - } else if (match("vulkan1.1")) { - if (env) *env = SPV_ENV_VULKAN_1_1; - return true; - } else if (match("spv1.0")) { - if (env) *env = SPV_ENV_UNIVERSAL_1_0; - return true; - } else if (match("spv1.1")) { - if (env) *env = SPV_ENV_UNIVERSAL_1_1; - return true; - } else if (match("spv1.2")) { - if (env) *env = SPV_ENV_UNIVERSAL_1_2; - return true; - } else if (match("spv1.3")) { - if (env) *env = SPV_ENV_UNIVERSAL_1_3; - return true; - } else if (match("spv1.4")) { - if (env) *env = SPV_ENV_UNIVERSAL_1_4; - return true; - } else if (match("opencl1.2embedded")) { - if (env) *env = SPV_ENV_OPENCL_EMBEDDED_1_2; - return true; - } else if (match("opencl1.2")) { - if (env) *env = SPV_ENV_OPENCL_1_2; - return true; - } else if (match("opencl2.0embedded")) { - if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_0; - return true; - } else if (match("opencl2.0")) { - if (env) *env = SPV_ENV_OPENCL_2_0; - return true; - } else if (match("opencl2.1embedded")) { - if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_1; - return true; - } else if (match("opencl2.1")) { - if (env) *env = SPV_ENV_OPENCL_2_1; - return true; - } else if (match("opencl2.2embedded")) { - if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_2; - return true; - } else if (match("opencl2.2")) { - if (env) *env = SPV_ENV_OPENCL_2_2; - return true; - } else if (match("opengl4.0")) { - if (env) *env = SPV_ENV_OPENGL_4_0; - return true; - } else if (match("opengl4.1")) { - if (env) *env = SPV_ENV_OPENGL_4_1; - return true; - } else if (match("opengl4.2")) { - if (env) *env = SPV_ENV_OPENGL_4_2; - return true; - } else if (match("opengl4.3")) { - if (env) *env = SPV_ENV_OPENGL_4_3; - return true; - } else if (match("opengl4.5")) { - if (env) *env = SPV_ENV_OPENGL_4_5; - return true; - } else if (match("webgpu0")) { - if (env) *env = SPV_ENV_WEBGPU_0; - return true; - } else { - if (env) *env = SPV_ENV_UNIVERSAL_1_0; - return false; + for (auto& name_env : spvTargetEnvNameMap) { + if (match(name_env.first)) { + if (env) { + *env = name_env.second; + } + return true; + } } + if (env) *env = SPV_ENV_UNIVERSAL_1_0; + return false; } bool spvIsVulkanEnv(spv_target_env env) { @@ -310,3 +275,29 @@ std::string spvLogStringForEnv(spv_target_env env) { } return "Unknown"; } + +std::string spvTargetEnvList(const int pad, const int wrap) { + std::string ret; + size_t max_line_len = wrap - pad; // The first line isn't padded + std::string line; + std::string sep = ""; + + for (auto& name_env : spvTargetEnvNameMap) { + std::string word = sep + name_env.first; + if (line.length() + word.length() > max_line_len) { + // Adding one word wouldn't fit, commit the line in progress and + // start a new one. + ret += line + "\n"; + line.assign(pad, ' '); + // The first line is done. The max length now comprises the + // padding. + max_line_len = wrap; + } + line += word; + sep = "|"; + } + + ret += line; + + return ret; +} diff --git a/3rdparty/spirv-tools/source/spirv_target_env.h b/3rdparty/spirv-tools/source/spirv_target_env.h index c19d16921..3e33a3567 100644 --- a/3rdparty/spirv-tools/source/spirv_target_env.h +++ b/3rdparty/spirv-tools/source/spirv_target_env.h @@ -38,4 +38,12 @@ uint32_t spvVersionForTargetEnv(spv_target_env env); // environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc. std::string spvLogStringForEnv(spv_target_env env); +// Returns a formatted list of all SPIR-V target environment names that +// can be parsed by spvParseTargetEnv. +// |pad| is the number of space characters that the begining of each line +// except the first one will be padded with. +// |wrap| is the max length of lines the user desires. Word-wrapping will +// occur to satisfy this limit. +std::string spvTargetEnvList(const int pad, const int wrap); + #endif // SOURCE_SPIRV_TARGET_ENV_H_ diff --git a/3rdparty/spirv-tools/source/val/validate_atomics.cpp b/3rdparty/spirv-tools/source/val/validate_atomics.cpp index 38c7053b9..e9e496056 100644 --- a/3rdparty/spirv-tools/source/val/validate_atomics.cpp +++ b/3rdparty/spirv-tools/source/val/validate_atomics.cpp @@ -175,13 +175,37 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { return error; } - if (auto error = ValidateMemorySemantics(_, inst, operand_index++)) + const auto equal_semantics_index = operand_index++; + if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index)) return error; if (opcode == SpvOpAtomicCompareExchange || opcode == SpvOpAtomicCompareExchangeWeak) { - if (auto error = ValidateMemorySemantics(_, inst, operand_index++)) + const auto unequal_semantics_index = operand_index++; + if (auto error = + ValidateMemorySemantics(_, inst, unequal_semantics_index)) return error; + + // Volatile bits must match for equal and unequal semantics. Previous + // checks guarantee they are 32-bit constants, but we need to recheck + // whether they are evaluatable constants. + bool is_int32 = false; + bool is_equal_const = false; + bool is_unequal_const = false; + uint32_t equal_value = 0; + uint32_t unequal_value = 0; + std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst( + inst->GetOperandAs(equal_semantics_index)); + std::tie(is_int32, is_unequal_const, unequal_value) = + _.EvalInt32IfConst( + inst->GetOperandAs(unequal_semantics_index)); + if (is_equal_const && is_unequal_const && + ((equal_value & SpvMemorySemanticsVolatileMask) ^ + (unequal_value & SpvMemorySemanticsVolatileMask))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Volatile mask setting must match for Equal and Unequal " + "memory semantics"; + } } if (opcode == SpvOpAtomicStore) { diff --git a/3rdparty/spirv-tools/source/val/validate_decorations.cpp b/3rdparty/spirv-tools/source/val/validate_decorations.cpp index a6a7ce626..51746006a 100644 --- a/3rdparty/spirv-tools/source/val/validate_decorations.cpp +++ b/3rdparty/spirv-tools/source/val/validate_decorations.cpp @@ -1393,6 +1393,81 @@ spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate, << spvOpcodeString(inst.opcode()); } +// Returns SPV_SUCCESS if validation rules are satisfied for the Component +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckComponentDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + + uint32_t type_id; + if (decoration.struct_member_index() == Decoration::kInvalidMember) { + // The target must be a memory object declaration. + const auto opcode = inst.opcode(); + if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration must be a memory object " + "declaration (a variable or a function parameter)"; + } + + // Only valid for the Input and Output Storage Classes. + const auto storage_class = opcode == SpvOpVariable + ? inst.GetOperandAs(2) + : SpvStorageClassMax; + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassMax) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration is invalid: must point to a " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class " + << storage_class; + } + + type_id = inst.type_id(); + if (vstate.IsPointerType(type_id)) { + const auto pointer = vstate.FindDef(type_id); + type_id = pointer->GetOperandAs(2); + } + } else { + if (inst.opcode() != SpvOpTypeStruct) { + return vstate.diag(SPV_ERROR_INVALID_DATA, &inst) + << "Attempted to get underlying data type via member index for " + "non-struct type."; + } + type_id = inst.word(decoration.struct_member_index() + 2); + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + if (!vstate.IsIntScalarOrVectorType(type_id) && + !vstate.IsFloatScalarOrVectorType(type_id)) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Component decoration specified for type " + << vstate.getIdName(type_id) << " that is not a scalar or vector"; + } + + // For 16-, and 32-bit types, it is invalid if this sequence of components + // gets larger than 3. + const auto bit_width = vstate.GetBitWidth(type_id); + if (bit_width == 16 || bit_width == 32) { + assert(decoration.params().size() == 1 && + "Grammar ensures Component has one parameter"); + + const auto component = decoration.params()[0]; + const auto last_component = component + vstate.GetDimension(type_id) - 1; + if (last_component > 3) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Sequence of components starting with " << component + << " and ending with " << last_component + << " gets larger than 3"; + } + } + } + + return SPV_SUCCESS; +} + #define PASS_OR_BAIL_AT_LINE(X, LINE) \ { \ spv_result_t e##LINE = (X); \ @@ -1421,6 +1496,9 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { for (const auto& decoration : decorations) { switch (decoration.dec_type()) { + case SpvDecorationComponent: + PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration)); + break; case SpvDecorationFPRoundingMode: if (is_shader) PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst)); diff --git a/3rdparty/spirv-tools/source/val/validate_memory.cpp b/3rdparty/spirv-tools/source/val/validate_memory.cpp index af6b5c289..29fdd5dc5 100644 --- a/3rdparty/spirv-tools/source/val/validate_memory.cpp +++ b/3rdparty/spirv-tools/source/val/validate_memory.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 @@ -21,6 +19,7 @@ #include "source/opcode.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" +#include "source/val/validate.h" #include "source/val/validate_scopes.h" #include "source/val/validation_state.h" @@ -809,6 +808,25 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { << "OpStore Pointer '" << _.getIdName(pointer_id) << "' storage class is read-only"; } + + if (spvIsVulkanEnv(_.context()->target_env) && + storage_class == SpvStorageClassUniform) { + auto base_ptr = _.TracePointer(pointer); + if (base_ptr->opcode() == SpvOpVariable) { + // If it's not a variable a different check should catch the problem. + auto base_type = _.FindDef(base_ptr->GetOperandAs(0)); + // Get the pointed-to type. + base_type = _.FindDef(base_type->GetOperandAs(2u)); + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = _.FindDef(base_type->GetOperandAs(1u)); + } + if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In the Vulkan environment, cannot store to Uniform Blocks"; + } + } + } } const auto object_index = 1; diff --git a/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp b/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp index 31154f5d4..0088cddd7 100644 --- a/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp +++ b/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp @@ -39,11 +39,20 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, } if (!is_const_int32) { - if (_.HasCapability(SpvCapabilityShader)) { + if (_.HasCapability(SpvCapabilityShader) && + !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Memory Semantics ids must be OpConstant when Shader " "capability is present"; } + + if (_.HasCapability(SpvCapabilityShader) && + _.HasCapability(SpvCapabilityCooperativeMatrixNV) && + !spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present"; + } return SPV_SUCCESS; } @@ -127,6 +136,21 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, << "VulkanMemoryModelKHR"; } + if (value & SpvMemorySemanticsVolatileMask) { + if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics Volatile requires capability " + "VulkanMemoryModelKHR"; + } + + if (!spvOpcodeIsAtomicOp(inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics Volatile can only be used with atomic " + "instructions"; + } + } + if (value & SpvMemorySemanticsUniformMemoryMask && !_.HasCapability(SpvCapabilityShader)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) diff --git a/3rdparty/spirv-tools/source/val/validation_state.cpp b/3rdparty/spirv-tools/source/val/validation_state.cpp index 394f72867..c5673eddd 100644 --- a/3rdparty/spirv-tools/source/val/validation_state.cpp +++ b/3rdparty/spirv-tools/source/val/validation_state.cpp @@ -1213,5 +1213,18 @@ bool ValidationState_t::LogicallyMatch(const Instruction* lhs, return false; } +const Instruction* ValidationState_t::TracePointer( + const Instruction* inst) const { + auto base_ptr = inst; + while (base_ptr->opcode() == SpvOpAccessChain || + base_ptr->opcode() == SpvOpInBoundsAccessChain || + base_ptr->opcode() == SpvOpPtrAccessChain || + base_ptr->opcode() == SpvOpInBoundsPtrAccessChain || + base_ptr->opcode() == SpvOpCopyObject) { + base_ptr = FindDef(base_ptr->GetOperandAs(2u)); + } + return base_ptr; +} + } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/val/validation_state.h b/3rdparty/spirv-tools/source/val/validation_state.h index c34c1ec95..a0914eca8 100644 --- a/3rdparty/spirv-tools/source/val/validation_state.h +++ b/3rdparty/spirv-tools/source/val/validation_state.h @@ -680,6 +680,15 @@ class ValidationState_t { bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs, bool check_decorations); + // Traces |inst| to find a single base pointer. Returns the base pointer. + // Will trace through the following instructions: + // * OpAccessChain + // * OpInBoundsAccessChain + // * OpPtrAccessChain + // * OpInBoundsPtrAccessChain + // * OpCopyObject + const Instruction* TracePointer(const Instruction* inst) const; + private: ValidationState_t(const ValidationState_t&); diff --git a/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt index 2e4bca576..7b47b5530 100644 --- a/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt @@ -20,15 +20,19 @@ if (${SPIRV_BUILD_FUZZER}) fuzzer_replayer_test.cpp fact_manager_test.cpp fuzz_test_util.cpp + fuzzer_pass_add_useful_constructs_test.cpp transformation_add_constant_boolean_test.cpp transformation_add_constant_scalar_test.cpp transformation_add_dead_break_test.cpp transformation_add_type_boolean_test.cpp transformation_add_type_float_test.cpp transformation_add_type_int_test.cpp + transformation_add_type_pointer_test.cpp transformation_move_block_down_test.cpp transformation_replace_boolean_constant_with_constant_binary_test.cpp - transformation_split_block_test.cpp) + transformation_replace_constant_with_uniform_test.cpp + transformation_split_block_test.cpp + uniform_buffer_element_descriptor_test.cpp) add_spvtools_unittest(TARGET fuzz SRCS ${SOURCES} diff --git a/3rdparty/spirv-tools/test/fuzz/fact_manager_test.cpp b/3rdparty/spirv-tools/test/fuzz/fact_manager_test.cpp index 0209a608c..738f8c997 100644 --- a/3rdparty/spirv-tools/test/fuzz/fact_manager_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fact_manager_test.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include "source/fuzz/fact_manager.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" #include "test/fuzz/fuzz_test_util.h" @@ -56,6 +58,44 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) { OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %200 DescriptorSet 0 + OpDecorate %200 Binding 1 + OpDecorate %300 DescriptorSet 0 + OpDecorate %300 Binding 2 + OpDecorate %400 DescriptorSet 0 + OpDecorate %400 Binding 3 + OpDecorate %500 DescriptorSet 0 + OpDecorate %500 Binding 4 + OpDecorate %600 DescriptorSet 0 + OpDecorate %600 Binding 5 + OpDecorate %700 DescriptorSet 0 + OpDecorate %700 Binding 6 + OpDecorate %800 DescriptorSet 1 + OpDecorate %800 Binding 0 + OpDecorate %900 DescriptorSet 1 + OpDecorate %900 Binding 1 + OpDecorate %1000 DescriptorSet 1 + OpDecorate %1000 Binding 2 + OpDecorate %1100 DescriptorSet 1 + OpDecorate %1100 Binding 3 + OpDecorate %1200 DescriptorSet 1 + OpDecorate %1200 Binding 4 + OpDecorate %1300 DescriptorSet 1 + OpDecorate %1300 Binding 5 + OpDecorate %1400 DescriptorSet 1 + OpDecorate %1400 Binding 6 + OpDecorate %1500 DescriptorSet 2 + OpDecorate %1500 Binding 0 + OpDecorate %1600 DescriptorSet 2 + OpDecorate %1600 Binding 1 + OpDecorate %1700 DescriptorSet 2 + OpDecorate %1700 Binding 2 + OpDecorate %1800 DescriptorSet 2 + OpDecorate %1800 Binding 3 + OpDecorate %1900 DescriptorSet 2 + OpDecorate %1900 Binding 4 %2 = OpTypeVoid %3 = OpTypeFunction %2 %10 = OpTypeInt 32 0 @@ -229,94 +269,98 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) { type_uint32_id) .empty()); + // In the comments that follow we write v[...][...] to refer to uniform + // variable v indexed with some given indices, when in practice v is + // identified via a (descriptor set, binding) pair. + // 100[2][3] == int(1) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1}, - MakeUniformBufferElementDescriptor(100, {2, 3}))); + MakeUniformBufferElementDescriptor(0, 0, {2, 3}))); // 200[1][2][3] == int(1) ASSERT_TRUE( AddFactHelper(&fact_manager, context.get(), {1}, - MakeUniformBufferElementDescriptor(200, {1, 2, 3}))); + MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3}))); // 300[1][0][2][3] == int(1) ASSERT_TRUE( AddFactHelper(&fact_manager, context.get(), {1}, - MakeUniformBufferElementDescriptor(300, {1, 0, 2, 3}))); + MakeUniformBufferElementDescriptor(0, 2, {1, 0, 2, 3}))); // 400[2][3] = int32_min ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]}, - MakeUniformBufferElementDescriptor(400, {2, 3}))); + MakeUniformBufferElementDescriptor(0, 3, {2, 3}))); // 500[1][2][3] = int32_min ASSERT_TRUE( AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]}, - MakeUniformBufferElementDescriptor(500, {1, 2, 3}))); + MakeUniformBufferElementDescriptor(0, 4, {1, 2, 3}))); // 600[1][2][3] = int64_max ASSERT_TRUE(AddFactHelper( &fact_manager, context.get(), {buffer_int64_max[0], buffer_int64_max[1]}, - MakeUniformBufferElementDescriptor(600, {1, 2, 3}))); + MakeUniformBufferElementDescriptor(0, 5, {1, 2, 3}))); // 700[1][1] = int64_max ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_int64_max[0], buffer_int64_max[1]}, - MakeUniformBufferElementDescriptor(700, {1, 1}))); + MakeUniformBufferElementDescriptor(0, 6, {1, 1}))); // 800[2][3] = uint(1) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1}, - MakeUniformBufferElementDescriptor(800, {2, 3}))); + MakeUniformBufferElementDescriptor(1, 0, {2, 3}))); // 900[1][2][3] = uint(1) ASSERT_TRUE( AddFactHelper(&fact_manager, context.get(), {1}, - MakeUniformBufferElementDescriptor(900, {1, 2, 3}))); + MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3}))); // 1000[1][0][2][3] = uint(1) ASSERT_TRUE( AddFactHelper(&fact_manager, context.get(), {1}, - MakeUniformBufferElementDescriptor(1000, {1, 0, 2, 3}))); + MakeUniformBufferElementDescriptor(1, 2, {1, 0, 2, 3}))); // 1100[0] = uint64(1) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_uint64_1[0], buffer_uint64_1[1]}, - MakeUniformBufferElementDescriptor(1100, {0}))); + MakeUniformBufferElementDescriptor(1, 3, {0}))); // 1200[0][0] = uint64_max ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_uint64_max[0], buffer_uint64_max[1]}, - MakeUniformBufferElementDescriptor(1200, {0, 0}))); + MakeUniformBufferElementDescriptor(1, 4, {0, 0}))); // 1300[1][0] = uint64_max ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_uint64_max[0], buffer_uint64_max[1]}, - MakeUniformBufferElementDescriptor(1300, {1, 0}))); + MakeUniformBufferElementDescriptor(1, 5, {1, 0}))); // 1400[6] = float(10.0) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]}, - MakeUniformBufferElementDescriptor(1400, {6}))); + MakeUniformBufferElementDescriptor(1, 6, {6}))); // 1500[7] = float(10.0) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]}, - MakeUniformBufferElementDescriptor(1500, {7}))); + MakeUniformBufferElementDescriptor(2, 0, {7}))); // 1600[9][9] = float(10.0) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]}, - MakeUniformBufferElementDescriptor(1600, {9, 9}))); + MakeUniformBufferElementDescriptor(2, 1, {9, 9}))); // 1700[9][9][1] = double(10.0) ASSERT_TRUE(AddFactHelper( &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]}, - MakeUniformBufferElementDescriptor(1700, {9, 9, 1}))); + MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1}))); // 1800[9][9][2] = double(10.0) ASSERT_TRUE(AddFactHelper( &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]}, - MakeUniformBufferElementDescriptor(1800, {9, 9, 2}))); + MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2}))); // 1900[0][0][0][0][0] = double(20.0) ASSERT_TRUE(AddFactHelper( &fact_manager, context.get(), {buffer_double_20[0], buffer_double_20[1]}, - MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0}))); + MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0}))); opt::Instruction::OperandList operands = { {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}}; @@ -427,12 +471,12 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) { context.get(), double_constant_ids[0]); ASSERT_EQ(2, descriptors_for_double_10.size()); { - auto temp = MakeUniformBufferElementDescriptor(1700, {9, 9, 1}); + auto temp = MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1}); ASSERT_TRUE(UniformBufferElementDescriptorEquals()( &temp, &descriptors_for_double_10[0])); } { - auto temp = MakeUniformBufferElementDescriptor(1800, {9, 9, 2}); + auto temp = MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2}); ASSERT_TRUE(UniformBufferElementDescriptorEquals()( &temp, &descriptors_for_double_10[1])); } @@ -441,17 +485,17 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) { context.get(), double_constant_ids[1]); ASSERT_EQ(1, descriptors_for_double_20.size()); { - auto temp = MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0}); + auto temp = MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0}); ASSERT_TRUE(UniformBufferElementDescriptorEquals()( &temp, &descriptors_for_double_20[0])); } auto constant_1_id = fact_manager.GetConstantFromUniformDescriptor( - context.get(), MakeUniformBufferElementDescriptor(1800, {9, 9, 2})); + context.get(), MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2})); ASSERT_TRUE(constant_1_id); auto constant_2_id = fact_manager.GetConstantFromUniformDescriptor( - context.get(), MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0})); + context.get(), MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0})); ASSERT_TRUE(constant_2_id); ASSERT_EQ(double_constant_ids[0], constant_1_id); @@ -503,9 +547,9 @@ TEST(FactManagerTest, TwoConstantsWithSameValue) { FactManager fact_manager; auto uniform_buffer_element_descriptor = - MakeUniformBufferElementDescriptor(12, {0}); + MakeUniformBufferElementDescriptor(0, 0, {0}); - // 12[0] = int(1) + // (0, 0, [0]) = int(1) ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1}, uniform_buffer_element_descriptor)); auto constants = @@ -529,6 +573,171 @@ TEST(FactManagerTest, TwoConstantsWithSameValue) { } } +TEST(FactManagerTest, NonFiniteFactsAreNotValid) { + std::string shader = R"( + OpCapability Shader + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "f" + OpMemberName %7 1 "d" + OpName %9 "" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 8 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %10 = OpTypeFloat 64 + %7 = OpTypeStruct %6 %10 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %4 = OpFunction %2 None %3 + %5 = 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; + auto uniform_buffer_element_descriptor_f = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + auto uniform_buffer_element_descriptor_d = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + if (std::numeric_limits::has_infinity) { + // f == +inf + float positive_infinity_float = std::numeric_limits::infinity(); + uint32_t words[1]; + memcpy(words, &positive_infinity_float, sizeof(float)); + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]}, + uniform_buffer_element_descriptor_f)); + // f == -inf + float negative_infinity_float = std::numeric_limits::infinity(); + memcpy(words, &negative_infinity_float, sizeof(float)); + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]}, + uniform_buffer_element_descriptor_f)); + } + + if (std::numeric_limits::has_quiet_NaN) { + // f == NaN + float quiet_nan_float = std::numeric_limits::quiet_NaN(); + uint32_t words[1]; + memcpy(words, &quiet_nan_float, sizeof(float)); + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]}, + uniform_buffer_element_descriptor_f)); + } + + if (std::numeric_limits::has_infinity) { + // d == +inf + double positive_infinity_double = std::numeric_limits::infinity(); + uint32_t words[2]; + memcpy(words, &positive_infinity_double, sizeof(double)); + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), + {words[0], words[1]}, + uniform_buffer_element_descriptor_d)); + // d == -inf + double negative_infinity_double = -std::numeric_limits::infinity(); + memcpy(words, &negative_infinity_double, sizeof(double)); + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), + {words[0], words[1]}, + uniform_buffer_element_descriptor_d)); + } + + if (std::numeric_limits::has_quiet_NaN) { + // d == NaN + double quiet_nan_double = std::numeric_limits::quiet_NaN(); + uint32_t words[2]; + memcpy(words, &quiet_nan_double, sizeof(double)); + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), + {words[0], words[1]}, + uniform_buffer_element_descriptor_d)); + } +} + +TEST(FactManagerTest, AmbiguousFact) { + // This test came from the following GLSL: + // + // #version 310 es + // + // precision highp float; + // + // layout(set = 0, binding = 0) uniform buf { + // float f; + // }; + // + // layout(set = 0, binding = 0) uniform buf2 { + // float g; + // }; + // + // void main() { + // + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "f" + OpName %9 "" + OpName %10 "buf2" + OpMemberName %10 0 "g" + OpName %12 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = 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; + auto uniform_buffer_element_descriptor = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + // The fact cannot be added because it is ambiguous: there are two uniforms + // with descriptor set 0 and binding 0. + ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {1}, + uniform_buffer_element_descriptor)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp b/3rdparty/spirv-tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp new file mode 100644 index 000000000..89f006e07 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp @@ -0,0 +1,393 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_useful_constructs.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +bool AddFactHelper( + FactManager* fact_manager, opt::IRContext* context, uint32_t word, + const protobufs::UniformBufferElementDescriptor& descriptor) { + protobufs::FactConstantUniform constant_uniform_fact; + constant_uniform_fact.add_constant_word(word); + *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() = + descriptor; + protobufs::Fact fact; + *fact.mutable_constant_uniform_fact() = constant_uniform_fact; + return fact_manager->AddFact(fact, context); +} + +TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) { + // The SPIR-V came from the following empty GLSL shader: + // + // #version 450 + // + // void main() + // { + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = 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; + FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager, + &fuzzer_context, &transformation_sequence); + pass.Apply(); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %100 = OpTypeBool + %101 = OpTypeInt 32 1 + %102 = OpTypeInt 32 0 + %103 = OpTypeFloat 32 + %104 = OpConstantTrue %100 + %105 = OpConstantFalse %100 + %106 = OpConstant %101 0 + %107 = OpConstant %101 1 + %108 = OpConstant %102 0 + %109 = OpConstant %102 1 + %110 = OpConstant %103 0 + %111 = OpConstant %103 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after, context.get())); +} + +TEST(FuzzerPassAddUsefulConstructsTest, + CheckTypesIndicesAndConstantsAddedForUniformFacts) { + // The SPIR-V came from the following GLSL shader: + // + // #version 450 + // + // struct S { + // int x; + // float y; + // int z; + // int w; + // }; + // + // uniform buf { + // S s; + // uint w[10]; + // }; + // + // void main() { + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "S" + OpMemberName %8 0 "x" + OpMemberName %8 1 "y" + OpMemberName %8 2 "z" + OpMemberName %8 3 "w" + OpName %12 "buf" + OpMemberName %12 0 "s" + OpMemberName %12 1 "w" + OpName %14 "" + OpMemberDecorate %8 0 Offset 0 + OpMemberDecorate %8 1 Offset 4 + OpMemberDecorate %8 2 Offset 8 + OpMemberDecorate %8 3 Offset 12 + OpDecorate %11 ArrayStride 16 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 1 Offset 16 + OpDecorate %12 Block + OpDecorate %14 DescriptorSet 0 + OpDecorate %14 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 %6 %6 + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 10 + %11 = OpTypeArray %9 %10 + %12 = OpTypeStruct %8 %11 + %13 = OpTypePointer Uniform %12 + %14 = OpVariable %13 Uniform + %4 = OpFunction %2 None %3 + %5 = 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; + FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + protobufs::TransformationSequence transformation_sequence; + + // Add some uniform facts. + + // buf.s.x == 200 + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200, + MakeUniformBufferElementDescriptor(0, 0, {0, 0}))); + + // buf.s.y == 0.5 + const float float_value = 0.5; + uint32_t float_value_as_uint; + memcpy(&float_value_as_uint, &float_value, sizeof(float_value)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint, + MakeUniformBufferElementDescriptor(0, 0, {0, 1}))); + + // buf.s.z == 300 + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300, + MakeUniformBufferElementDescriptor(0, 0, {0, 2}))); + + // buf.s.w == 400 + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400, + MakeUniformBufferElementDescriptor(0, 0, {0, 3}))); + + // buf.w[6] = 22 + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22, + MakeUniformBufferElementDescriptor(0, 0, {1, 6}))); + + // buf.w[8] = 23 + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23, + MakeUniformBufferElementDescriptor(0, 0, {1, 8}))); + + // Assert some things about the module that are not true prior to adding the + // pass + + { + // No uniform int pointer + opt::analysis::Integer temp_type_signed_int(32, true); + opt::analysis::Integer* registered_type_signed_int = + context->get_type_mgr() + ->GetRegisteredType(&temp_type_signed_int) + ->AsInteger(); + opt::analysis::Pointer type_pointer_uniform_signed_int( + registered_type_signed_int, SpvStorageClassUniform); + ASSERT_EQ(0, + context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int)); + + // No uniform uint pointer + opt::analysis::Integer temp_type_unsigned_int(32, false); + opt::analysis::Integer* registered_type_unsigned_int = + context->get_type_mgr() + ->GetRegisteredType(&temp_type_unsigned_int) + ->AsInteger(); + opt::analysis::Pointer type_pointer_uniform_unsigned_int( + registered_type_unsigned_int, SpvStorageClassUniform); + ASSERT_EQ( + 0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int)); + + // No uniform float pointer + opt::analysis::Float temp_type_float(32); + opt::analysis::Float* registered_type_float = + context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat(); + opt::analysis::Pointer type_pointer_uniform_float(registered_type_float, + SpvStorageClassUniform); + ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float)); + + // No int constants 200, 300 nor 400 + opt::analysis::IntConstant int_constant_200(registered_type_signed_int, + {200}); + opt::analysis::IntConstant int_constant_300(registered_type_signed_int, + {300}); + opt::analysis::IntConstant int_constant_400(registered_type_signed_int, + {400}); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_200)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_300)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_400)); + + // No float constant 0.5 + opt::analysis::FloatConstant float_constant_zero_point_five( + registered_type_float, {float_value_as_uint}); + ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant( + &float_constant_zero_point_five)); + + // No uint constant 22 + opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int, + {22}); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&uint_constant_22)); + + // No uint constant 23 + opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int, + {23}); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&uint_constant_23)); + + // No int constants 0, 1, 2, 3, 6, 8 + opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0}); + opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1}); + opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2}); + opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3}); + opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6}); + opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8}); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_0)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_1)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_2)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_3)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_6)); + ASSERT_EQ(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_8)); + } + + FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager, + &fuzzer_context, &transformation_sequence); + pass.Apply(); + ASSERT_TRUE(IsValid(env, context.get())); + + // Now assert some things about the module that should be true following the + // pass. + + // We reconstruct all necessary types and constants to guard against the type + // and constant managers for the module having been invalidated. + + { + // Uniform int pointer now present + opt::analysis::Integer temp_type_signed_int(32, true); + opt::analysis::Integer* registered_type_signed_int = + context->get_type_mgr() + ->GetRegisteredType(&temp_type_signed_int) + ->AsInteger(); + opt::analysis::Pointer type_pointer_uniform_signed_int( + registered_type_signed_int, SpvStorageClassUniform); + ASSERT_NE(0, + context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int)); + + // Uniform uint pointer now present + opt::analysis::Integer temp_type_unsigned_int(32, false); + opt::analysis::Integer* registered_type_unsigned_int = + context->get_type_mgr() + ->GetRegisteredType(&temp_type_unsigned_int) + ->AsInteger(); + opt::analysis::Pointer type_pointer_uniform_unsigned_int( + registered_type_unsigned_int, SpvStorageClassUniform); + ASSERT_NE( + 0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int)); + + // Uniform float pointer now present + opt::analysis::Float temp_type_float(32); + opt::analysis::Float* registered_type_float = + context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat(); + opt::analysis::Pointer type_pointer_uniform_float(registered_type_float, + SpvStorageClassUniform); + ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float)); + + // int constants 200, 300, 400 now present + opt::analysis::IntConstant int_constant_200(registered_type_signed_int, + {200}); + opt::analysis::IntConstant int_constant_300(registered_type_signed_int, + {300}); + opt::analysis::IntConstant int_constant_400(registered_type_signed_int, + {400}); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_200)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_300)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_400)); + + // float constant 0.5 now present + opt::analysis::FloatConstant float_constant_zero_point_five( + registered_type_float, {float_value_as_uint}); + ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant( + &float_constant_zero_point_five)); + + // uint constant 22 now present + opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int, + {22}); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&uint_constant_22)); + + // uint constant 23 now present + opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int, + {23}); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&uint_constant_23)); + + // int constants 0, 1, 2, 3, 6, 8 now present + opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0}); + opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1}); + opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2}); + opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3}); + opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6}); + opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8}); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_0)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_1)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_2)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_3)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_6)); + ASSERT_NE(nullptr, + context->get_constant_mgr()->FindConstant(&int_constant_8)); + } +} + +} // namespace +} // 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 3adf76621..8b3775b55 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp @@ -14,6 +14,7 @@ #include "source/fuzz/fuzzer.h" #include "source/fuzz/replayer.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { @@ -25,8 +26,9 @@ namespace { // the binary produced after each fuzzer run is valid, and that replaying // the transformations that were applied during fuzzing leads to an // identical binary. -void RunFuzzerAndReplayer(const std::string& shader, uint32_t initial_seed, - uint32_t num_runs) { +void RunFuzzerAndReplayer(const std::string& shader, + const protobufs::FactSequence& initial_facts, + uint32_t initial_seed, uint32_t num_runs) { const auto env = SPV_ENV_UNIVERSAL_1_3; std::vector binary_in; @@ -35,23 +37,27 @@ void RunFuzzerAndReplayer(const std::string& shader, uint32_t initial_seed, ASSERT_TRUE(t.Validate(binary_in)); for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) { - protobufs::FactSequence initial_facts; std::vector fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; spvtools::FuzzerOptions fuzzer_options; spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed); Fuzzer fuzzer(env); - fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out, - &fuzzer_transformation_sequence_out, fuzzer_options); + auto fuzzer_result_status = + fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out, + &fuzzer_transformation_sequence_out, fuzzer_options); + ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status); ASSERT_TRUE(t.Validate(fuzzer_binary_out)); std::vector replayer_binary_out; protobufs::TransformationSequence replayer_transformation_sequence_out; Replayer replayer(env); - replayer.Run(binary_in, initial_facts, fuzzer_transformation_sequence_out, - &replayer_binary_out, &replayer_transformation_sequence_out); + auto replayer_result_status = replayer.Run( + binary_in, initial_facts, fuzzer_transformation_sequence_out, + &replayer_binary_out, &replayer_transformation_sequence_out); + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, + replayer_result_status); // After replaying the transformations applied by the fuzzer, exactly those // transformations should have been applied, and the binary resulting from @@ -233,7 +239,7 @@ TEST(FuzzerReplayerTest, Miscellaneous1) { // Do 10 fuzzer runs, starting from an initial seed of 0 (seed value chosen // arbitrarily). - RunFuzzerAndReplayer(shader, 0, 10); + RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 10); } TEST(FuzzerReplayerTest, Miscellaneous2) { @@ -478,7 +484,492 @@ TEST(FuzzerReplayerTest, Miscellaneous2) { // Do 10 fuzzer runs, starting from an initial seed of 10 (seed value chosen // arbitrarily). - RunFuzzerAndReplayer(shader, 10, 10); + RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 10); +} + +TEST(FuzzerReplayerTest, Miscellaneous3) { + // The SPIR-V came from this GLSL, which was then optimized using spirv-opt + // with the -O argument: + // + // #version 310 es + // + // precision highp float; + // + // layout(location = 0) out vec4 _GLF_color; + // + // layout(set = 0, binding = 0) uniform buf0 { + // vec2 resolution; + // }; + // void main(void) + // { + // float A[50]; + // for( + // int i = 0; + // i < 200; + // i ++ + // ) + // { + // if(i >= int(resolution.x)) + // { + // break; + // } + // if((4 * (i / 4)) == i) + // { + // A[i / 4] = float(i); + // } + // } + // for( + // int i = 0; + // i < 50; + // i ++ + // ) + // { + // if(i < int(gl_FragCoord.x)) + // { + // break; + // } + // if(i > 0) + // { + // A[i] += A[i - 1]; + // } + // } + // if(int(gl_FragCoord.x) < 20) + // { + // _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 40) + // { + // _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 60) + // { + // _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y, + // 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 80) + // { + // _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y, + // 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 100) + // { + // _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y, + // 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 120) + // { + // _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y, + // 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 140) + // { + // _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y, + // 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 160) + // { + // _GLF_color = vec4(A[35] / resolution.x, A[39] / + // resolution.y, 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 180) + // { + // _GLF_color = vec4(A[40] / resolution.x, A[44] / + // resolution.y, 1.0, 1.0); + // } + // else + // if(int(gl_FragCoord.x) < 180) + // { + // _GLF_color = vec4(A[45] / resolution.x, A[49] / + // resolution.y, 1.0, 1.0); + // } + // else + // { + // discard; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %68 %100 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %22 "buf0" + OpMemberName %22 0 "resolution" + OpName %24 "" + OpName %46 "A" + OpName %68 "gl_FragCoord" + OpName %100 "_GLF_color" + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %68 BuiltIn FragCoord + OpDecorate %83 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %100 Location 0 + OpDecorate %302 RelaxedPrecision + OpDecorate %304 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpConstant %6 200 + %17 = OpTypeBool + %20 = OpTypeFloat 32 + %21 = OpTypeVector %20 2 + %22 = OpTypeStruct %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypeInt 32 0 + %26 = OpConstant %25 0 + %27 = OpTypePointer Uniform %20 + %35 = OpConstant %6 4 + %43 = OpConstant %25 50 + %44 = OpTypeArray %20 %43 + %45 = OpTypePointer Function %44 + %51 = OpTypePointer Function %20 + %54 = OpConstant %6 1 + %63 = OpConstant %6 50 + %66 = OpTypeVector %20 4 + %67 = OpTypePointer Input %66 + %68 = OpVariable %67 Input + %69 = OpTypePointer Input %20 + %95 = OpConstant %6 20 + %99 = OpTypePointer Output %66 + %100 = OpVariable %99 Output + %108 = OpConstant %25 1 + %112 = OpConstant %20 1 + %118 = OpConstant %6 40 + %122 = OpConstant %6 5 + %128 = OpConstant %6 9 + %139 = OpConstant %6 60 + %143 = OpConstant %6 10 + %149 = OpConstant %6 14 + %160 = OpConstant %6 80 + %164 = OpConstant %6 15 + %170 = OpConstant %6 19 + %181 = OpConstant %6 100 + %190 = OpConstant %6 24 + %201 = OpConstant %6 120 + %205 = OpConstant %6 25 + %211 = OpConstant %6 29 + %222 = OpConstant %6 140 + %226 = OpConstant %6 30 + %232 = OpConstant %6 34 + %243 = OpConstant %6 160 + %247 = OpConstant %6 35 + %253 = OpConstant %6 39 + %264 = OpConstant %6 180 + %273 = OpConstant %6 44 + %287 = OpConstant %6 45 + %293 = OpConstant %6 49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %46 = OpVariable %45 Function + OpBranch %10 + %10 = OpLabel + %302 = OpPhi %6 %9 %5 %55 %42 + %18 = OpSLessThan %17 %302 %16 + OpLoopMerge %12 %42 None + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %28 = OpAccessChain %27 %24 %9 %26 + %29 = OpLoad %20 %28 + %30 = OpConvertFToS %6 %29 + %31 = OpSGreaterThanEqual %17 %302 %30 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + OpBranch %12 + %33 = OpLabel + %37 = OpSDiv %6 %302 %35 + %38 = OpIMul %6 %35 %37 + %40 = OpIEqual %17 %38 %302 + OpSelectionMerge %42 None + OpBranchConditional %40 %41 %42 + %41 = OpLabel + %50 = OpConvertSToF %20 %302 + %52 = OpAccessChain %51 %46 %37 + OpStore %52 %50 + OpBranch %42 + %42 = OpLabel + %55 = OpIAdd %6 %302 %54 + OpBranch %10 + %12 = OpLabel + OpBranch %57 + %57 = OpLabel + %304 = OpPhi %6 %9 %12 %91 %80 + %64 = OpSLessThan %17 %304 %63 + OpLoopMerge %59 %80 None + OpBranchConditional %64 %58 %59 + %58 = OpLabel + %70 = OpAccessChain %69 %68 %26 + %71 = OpLoad %20 %70 + %72 = OpConvertFToS %6 %71 + %73 = OpSLessThan %17 %304 %72 + OpSelectionMerge %75 None + OpBranchConditional %73 %74 %75 + %74 = OpLabel + OpBranch %59 + %75 = OpLabel + %78 = OpSGreaterThan %17 %304 %9 + OpSelectionMerge %80 None + OpBranchConditional %78 %79 %80 + %79 = OpLabel + %83 = OpISub %6 %304 %54 + %84 = OpAccessChain %51 %46 %83 + %85 = OpLoad %20 %84 + %86 = OpAccessChain %51 %46 %304 + %87 = OpLoad %20 %86 + %88 = OpFAdd %20 %87 %85 + OpStore %86 %88 + OpBranch %80 + %80 = OpLabel + %91 = OpIAdd %6 %304 %54 + OpBranch %57 + %59 = OpLabel + %92 = OpAccessChain %69 %68 %26 + %93 = OpLoad %20 %92 + %94 = OpConvertFToS %6 %93 + %96 = OpSLessThan %17 %94 %95 + OpSelectionMerge %98 None + OpBranchConditional %96 %97 %114 + %97 = OpLabel + %101 = OpAccessChain %51 %46 %9 + %102 = OpLoad %20 %101 + %103 = OpAccessChain %27 %24 %9 %26 + %104 = OpLoad %20 %103 + %105 = OpFDiv %20 %102 %104 + %106 = OpAccessChain %51 %46 %35 + %107 = OpLoad %20 %106 + %109 = OpAccessChain %27 %24 %9 %108 + %110 = OpLoad %20 %109 + %111 = OpFDiv %20 %107 %110 + %113 = OpCompositeConstruct %66 %105 %111 %112 %112 + OpStore %100 %113 + OpBranch %98 + %114 = OpLabel + %119 = OpSLessThan %17 %94 %118 + OpSelectionMerge %121 None + OpBranchConditional %119 %120 %135 + %120 = OpLabel + %123 = OpAccessChain %51 %46 %122 + %124 = OpLoad %20 %123 + %125 = OpAccessChain %27 %24 %9 %26 + %126 = OpLoad %20 %125 + %127 = OpFDiv %20 %124 %126 + %129 = OpAccessChain %51 %46 %128 + %130 = OpLoad %20 %129 + %131 = OpAccessChain %27 %24 %9 %108 + %132 = OpLoad %20 %131 + %133 = OpFDiv %20 %130 %132 + %134 = OpCompositeConstruct %66 %127 %133 %112 %112 + OpStore %100 %134 + OpBranch %121 + %135 = OpLabel + %140 = OpSLessThan %17 %94 %139 + OpSelectionMerge %142 None + OpBranchConditional %140 %141 %156 + %141 = OpLabel + %144 = OpAccessChain %51 %46 %143 + %145 = OpLoad %20 %144 + %146 = OpAccessChain %27 %24 %9 %26 + %147 = OpLoad %20 %146 + %148 = OpFDiv %20 %145 %147 + %150 = OpAccessChain %51 %46 %149 + %151 = OpLoad %20 %150 + %152 = OpAccessChain %27 %24 %9 %108 + %153 = OpLoad %20 %152 + %154 = OpFDiv %20 %151 %153 + %155 = OpCompositeConstruct %66 %148 %154 %112 %112 + OpStore %100 %155 + OpBranch %142 + %156 = OpLabel + %161 = OpSLessThan %17 %94 %160 + OpSelectionMerge %163 None + OpBranchConditional %161 %162 %177 + %162 = OpLabel + %165 = OpAccessChain %51 %46 %164 + %166 = OpLoad %20 %165 + %167 = OpAccessChain %27 %24 %9 %26 + %168 = OpLoad %20 %167 + %169 = OpFDiv %20 %166 %168 + %171 = OpAccessChain %51 %46 %170 + %172 = OpLoad %20 %171 + %173 = OpAccessChain %27 %24 %9 %108 + %174 = OpLoad %20 %173 + %175 = OpFDiv %20 %172 %174 + %176 = OpCompositeConstruct %66 %169 %175 %112 %112 + OpStore %100 %176 + OpBranch %163 + %177 = OpLabel + %182 = OpSLessThan %17 %94 %181 + OpSelectionMerge %184 None + OpBranchConditional %182 %183 %197 + %183 = OpLabel + %185 = OpAccessChain %51 %46 %95 + %186 = OpLoad %20 %185 + %187 = OpAccessChain %27 %24 %9 %26 + %188 = OpLoad %20 %187 + %189 = OpFDiv %20 %186 %188 + %191 = OpAccessChain %51 %46 %190 + %192 = OpLoad %20 %191 + %193 = OpAccessChain %27 %24 %9 %108 + %194 = OpLoad %20 %193 + %195 = OpFDiv %20 %192 %194 + %196 = OpCompositeConstruct %66 %189 %195 %112 %112 + OpStore %100 %196 + OpBranch %184 + %197 = OpLabel + %202 = OpSLessThan %17 %94 %201 + OpSelectionMerge %204 None + OpBranchConditional %202 %203 %218 + %203 = OpLabel + %206 = OpAccessChain %51 %46 %205 + %207 = OpLoad %20 %206 + %208 = OpAccessChain %27 %24 %9 %26 + %209 = OpLoad %20 %208 + %210 = OpFDiv %20 %207 %209 + %212 = OpAccessChain %51 %46 %211 + %213 = OpLoad %20 %212 + %214 = OpAccessChain %27 %24 %9 %108 + %215 = OpLoad %20 %214 + %216 = OpFDiv %20 %213 %215 + %217 = OpCompositeConstruct %66 %210 %216 %112 %112 + OpStore %100 %217 + OpBranch %204 + %218 = OpLabel + %223 = OpSLessThan %17 %94 %222 + OpSelectionMerge %225 None + OpBranchConditional %223 %224 %239 + %224 = OpLabel + %227 = OpAccessChain %51 %46 %226 + %228 = OpLoad %20 %227 + %229 = OpAccessChain %27 %24 %9 %26 + %230 = OpLoad %20 %229 + %231 = OpFDiv %20 %228 %230 + %233 = OpAccessChain %51 %46 %232 + %234 = OpLoad %20 %233 + %235 = OpAccessChain %27 %24 %9 %108 + %236 = OpLoad %20 %235 + %237 = OpFDiv %20 %234 %236 + %238 = OpCompositeConstruct %66 %231 %237 %112 %112 + OpStore %100 %238 + OpBranch %225 + %239 = OpLabel + %244 = OpSLessThan %17 %94 %243 + OpSelectionMerge %246 None + OpBranchConditional %244 %245 %260 + %245 = OpLabel + %248 = OpAccessChain %51 %46 %247 + %249 = OpLoad %20 %248 + %250 = OpAccessChain %27 %24 %9 %26 + %251 = OpLoad %20 %250 + %252 = OpFDiv %20 %249 %251 + %254 = OpAccessChain %51 %46 %253 + %255 = OpLoad %20 %254 + %256 = OpAccessChain %27 %24 %9 %108 + %257 = OpLoad %20 %256 + %258 = OpFDiv %20 %255 %257 + %259 = OpCompositeConstruct %66 %252 %258 %112 %112 + OpStore %100 %259 + OpBranch %246 + %260 = OpLabel + %265 = OpSLessThan %17 %94 %264 + OpSelectionMerge %267 None + OpBranchConditional %265 %266 %280 + %266 = OpLabel + %268 = OpAccessChain %51 %46 %118 + %269 = OpLoad %20 %268 + %270 = OpAccessChain %27 %24 %9 %26 + %271 = OpLoad %20 %270 + %272 = OpFDiv %20 %269 %271 + %274 = OpAccessChain %51 %46 %273 + %275 = OpLoad %20 %274 + %276 = OpAccessChain %27 %24 %9 %108 + %277 = OpLoad %20 %276 + %278 = OpFDiv %20 %275 %277 + %279 = OpCompositeConstruct %66 %272 %278 %112 %112 + OpStore %100 %279 + OpBranch %267 + %280 = OpLabel + OpSelectionMerge %285 None + OpBranchConditional %265 %285 %300 + %285 = OpLabel + %288 = OpAccessChain %51 %46 %287 + %289 = OpLoad %20 %288 + %290 = OpAccessChain %27 %24 %9 %26 + %291 = OpLoad %20 %290 + %292 = OpFDiv %20 %289 %291 + %294 = OpAccessChain %51 %46 %293 + %295 = OpLoad %20 %294 + %296 = OpAccessChain %27 %24 %9 %108 + %297 = OpLoad %20 %296 + %298 = OpFDiv %20 %295 %297 + %299 = OpCompositeConstruct %66 %292 %298 %112 %112 + OpStore %100 %299 + OpBranch %267 + %300 = OpLabel + OpKill + %267 = OpLabel + OpBranch %246 + %246 = OpLabel + OpBranch %225 + %225 = OpLabel + OpBranch %204 + %204 = OpLabel + OpBranch %184 + %184 = OpLabel + OpBranch %163 + %163 = OpLabel + OpBranch %142 + %142 = OpLabel + OpBranch %121 + %121 = OpLabel + OpBranch %98 + %98 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Add the facts "resolution.x == 250" and "resolution.y == 100". + protobufs::FactSequence facts; + { + protobufs::FactConstantUniform resolution_x_eq_250; + *resolution_x_eq_250.mutable_uniform_buffer_element_descriptor() = + MakeUniformBufferElementDescriptor(0, 0, {0, 0}); + *resolution_x_eq_250.mutable_constant_word()->Add() = 250; + protobufs::Fact temp; + *temp.mutable_constant_uniform_fact() = resolution_x_eq_250; + *facts.mutable_fact()->Add() = temp; + } + { + protobufs::FactConstantUniform resolution_y_eq_100; + *resolution_y_eq_100.mutable_uniform_buffer_element_descriptor() = + MakeUniformBufferElementDescriptor(0, 0, {0, 1}); + *resolution_y_eq_100.mutable_constant_word()->Add() = 100; + protobufs::Fact temp; + *temp.mutable_constant_uniform_fact() = resolution_y_eq_100; + *facts.mutable_fact()->Add() = temp; + } + + // Do 10 fuzzer runs, starting from an initial seed of 94 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(shader, facts, 94, 10); } } // namespace diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp new file mode 100644 index 000000000..184b69984 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_pointer.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypePointerTest, BasicTest) { + // The SPIR-V was obtained from this GLSL: + // + // #version 450 + // + // int x; + // float y; + // vec2 z; + // + // struct T { + // int a, b; + // }; + // + // struct S { + // T t; + // int u; + // }; + // + // void main() { + // S myS = S(T(1, 2), 3); + // myS.u = x; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %7 "T" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %8 "S" + OpMemberName %8 0 "t" + OpMemberName %8 1 "u" + OpName %10 "myS" + OpName %17 "x" + OpName %23 "y" + OpName %26 "z" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %11 = OpConstant %6 1 + %12 = OpConstant %6 2 + %13 = OpConstantComposite %7 %11 %12 + %14 = OpConstant %6 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpTypePointer Private %6 + %17 = OpVariable %16 Private + %19 = OpTypePointer Function %6 + %21 = OpTypeFloat 32 + %22 = OpTypePointer Private %21 + %23 = OpVariable %22 Private + %24 = OpTypeVector %21 2 + %25 = OpTypePointer Private %24 + %26 = OpVariable %25 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %15 + %18 = OpLoad %6 %17 + %20 = OpAccessChain %19 %10 %11 + OpStore %20 %18 + 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; + + auto bad_type_id_does_not_exist = + transformation::MakeTransformationAddTypePointer( + 100, SpvStorageClassFunction, 101); + auto bad_type_id_is_not_type = + transformation::MakeTransformationAddTypePointer( + 100, SpvStorageClassFunction, 23); + auto bad_result_id_is_not_fresh = + transformation::MakeTransformationAddTypePointer( + 17, SpvStorageClassFunction, 21); + + auto good_new_private_pointer_to_t = + transformation::MakeTransformationAddTypePointer( + 101, SpvStorageClassPrivate, 7); + auto good_new_uniform_pointer_to_t = + transformation::MakeTransformationAddTypePointer( + 102, SpvStorageClassUniform, 7); + auto good_another_function_pointer_to_s = + transformation::MakeTransformationAddTypePointer( + 103, SpvStorageClassFunction, 8); + auto good_new_uniform_pointer_to_s = + transformation::MakeTransformationAddTypePointer( + 104, SpvStorageClassUniform, 8); + auto good_another_private_pointer_to_float = + transformation::MakeTransformationAddTypePointer( + 105, SpvStorageClassPrivate, 21); + auto good_new_private_pointer_to_private_pointer_to_float = + transformation::MakeTransformationAddTypePointer( + 106, SpvStorageClassPrivate, 105); + auto good_new_uniform_pointer_to_vec2 = + transformation::MakeTransformationAddTypePointer( + 107, SpvStorageClassUniform, 24); + auto good_new_private_pointer_to_uniform_pointer_to_vec2 = + transformation::MakeTransformationAddTypePointer( + 108, SpvStorageClassPrivate, 107); + + ASSERT_FALSE(transformation::IsApplicable(bad_type_id_does_not_exist, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_not_type, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(bad_result_id_is_not_fresh, + context.get(), fact_manager)); + + for (auto& transformation : + {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t, + good_another_function_pointer_to_s, good_new_uniform_pointer_to_s, + good_another_private_pointer_to_float, + good_new_private_pointer_to_private_pointer_to_float, + good_new_uniform_pointer_to_vec2, + good_new_private_pointer_to_uniform_pointer_to_vec2}) { + ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(), + fact_manager)); + transformation::Apply(transformation, context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %7 "T" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %8 "S" + OpMemberName %8 0 "t" + OpMemberName %8 1 "u" + OpName %10 "myS" + OpName %17 "x" + OpName %23 "y" + OpName %26 "z" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %11 = OpConstant %6 1 + %12 = OpConstant %6 2 + %13 = OpConstantComposite %7 %11 %12 + %14 = OpConstant %6 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpTypePointer Private %6 + %17 = OpVariable %16 Private + %19 = OpTypePointer Function %6 + %21 = OpTypeFloat 32 + %22 = OpTypePointer Private %21 + %23 = OpVariable %22 Private + %24 = OpTypeVector %21 2 + %25 = OpTypePointer Private %24 + %26 = OpVariable %25 Private + %101 = OpTypePointer Private %7 + %102 = OpTypePointer Uniform %7 + %103 = OpTypePointer Function %8 + %104 = OpTypePointer Uniform %8 + %105 = OpTypePointer Private %21 + %106 = OpTypePointer Private %105 + %107 = OpTypePointer Uniform %24 + %108 = OpTypePointer Private %107 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %15 + %18 = OpLoad %6 %17 + %20 = OpAccessChain %19 %10 %11 + OpStore %20 %18 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp new file mode 100644 index 000000000..5a101732d --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp @@ -0,0 +1,1486 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +bool AddFactHelper( + FactManager* fact_manager, opt::IRContext* context, uint32_t word, + const protobufs::UniformBufferElementDescriptor& descriptor) { + protobufs::FactConstantUniform constant_uniform_fact; + constant_uniform_fact.add_constant_word(word); + *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() = + descriptor; + protobufs::Fact fact; + *fact.mutable_constant_uniform_fact() = constant_uniform_fact; + return fact_manager->AddFact(fact, context); +} + +TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int a; + // int b; + // int c; + // }; + // + // void main() + // { + // int x; + // x = 1; + // x = x + 2; + // x = 3 + x; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpIAdd %6 %10 %11 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %15 = OpIAdd %6 %14 %13 + OpStore %8 %15 + 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; + protobufs::UniformBufferElementDescriptor blockname_a = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_b = + MakeUniformBufferElementDescriptor(0, 0, {1}); + protobufs::UniformBufferElementDescriptor blockname_c = + MakeUniformBufferElementDescriptor(0, 0, {2}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_a)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_b)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_c)); + + // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively. + protobufs::IdUseDescriptor use_of_9_in_store = + transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + protobufs::IdUseDescriptor use_of_11_in_add = + transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0); + protobufs::IdUseDescriptor use_of_14_in_add = + transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0); + + // These transformations work: they match the facts. + auto transformation_use_of_9_in_store = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_a, 100, 101); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_9_in_store, + context.get(), fact_manager)); + auto transformation_use_of_11_in_add = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_11_in_add, blockname_b, 102, 103); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_11_in_add, + context.get(), fact_manager)); + auto transformation_use_of_14_in_add = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_14_in_add, blockname_c, 104, 105); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_14_in_add, + context.get(), fact_manager)); + + // The transformations are not applicable if we change which uniforms are + // applied to which constants. + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_b, 101, 102), + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_11_in_add, blockname_c, 101, 102), + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_14_in_add, blockname_a, 101, 102), + context.get(), fact_manager)); + + // The following transformations do not apply because the uniform descriptors + // are not sensible. + protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor1 = + MakeUniformBufferElementDescriptor(1, 2, {0}); + protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor2 = + MakeUniformBufferElementDescriptor(0, 0, {5}); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102), + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102), + context.get(), fact_manager)); + + // The following transformation does not apply because the id descriptor is + // not sensible. + protobufs::IdUseDescriptor nonsense_id_use_descriptor = + transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + nonsense_id_use_descriptor, blockname_a, 101, 102), + context.get(), fact_manager)); + + // The following transformations do not apply because the ids are not fresh. + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_11_in_add, blockname_b, 15, 103), + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_11_in_add, blockname_b, 102, 15), + context.get(), fact_manager)); + + // Apply the use of 9 in a store. + transformation::Apply(transformation_use_of_9_in_store, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string after_replacing_use_of_9_in_store = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpAccessChain %51 %18 %50 + %101 = OpLoad %6 %100 + OpStore %8 %101 + %10 = OpLoad %6 %8 + %12 = OpIAdd %6 %10 %11 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %15 = OpIAdd %6 %14 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get())); + + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_11_in_add, + context.get(), fact_manager)); + // Apply the use of 11 in an add. + transformation::Apply(transformation_use_of_11_in_add, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string after_replacing_use_of_11_in_add = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpAccessChain %51 %18 %50 + %101 = OpLoad %6 %100 + OpStore %8 %101 + %10 = OpLoad %6 %8 + %102 = OpAccessChain %51 %18 %9 + %103 = OpLoad %6 %102 + %12 = OpIAdd %6 %10 %103 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %15 = OpIAdd %6 %14 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get())); + + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_14_in_add, + context.get(), fact_manager)); + // Apply the use of 15 in an add. + transformation::Apply(transformation_use_of_14_in_add, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string after_replacing_use_of_14_in_add = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpAccessChain %51 %18 %50 + %101 = OpLoad %6 %100 + OpStore %8 %101 + %10 = OpLoad %6 %8 + %102 = OpAccessChain %51 %18 %9 + %103 = OpLoad %6 %102 + %12 = OpIAdd %6 %10 %103 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %104 = OpAccessChain %51 %18 %11 + %105 = OpLoad %6 %104 + %15 = OpIAdd %6 %105 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_replacing_use_of_14_in_add, context.get())); +} + +TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { + // This test came from the following GLSL: + // + // #version 450 + // + // struct U { + // int x; // == 4 + // }; + // + // struct T { + // int x; // == 3 + // U y; + // }; + // + // struct S { + // T x; + // int y; // == 2 + // }; + // + // uniform blockname { + // int x; // == 1 + // S y; + // }; + // + // void foo(int a) { } + // + // void main() + // { + // int x; + // x = 1; + // x = x + 2; + // x = 3 + x; + // foo(4); + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "a" + OpName %12 "x" + OpName %21 "param" + OpName %23 "U" + OpMemberName %23 0 "x" + OpName %24 "T" + OpMemberName %24 0 "x" + OpMemberName %24 1 "y" + OpName %25 "S" + OpMemberName %25 0 "x" + OpMemberName %25 1 "y" + OpName %26 "blockname" + OpMemberName %26 0 "x" + OpMemberName %26 1 "y" + OpName %28 "" + OpMemberDecorate %23 0 Offset 0 + OpMemberDecorate %24 0 Offset 0 + OpMemberDecorate %24 1 Offset 16 + OpMemberDecorate %25 0 Offset 0 + OpMemberDecorate %25 1 Offset 32 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 16 + OpDecorate %26 Block + OpDecorate %28 DescriptorSet 0 + OpDecorate %28 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpConstant %6 0 + %13 = OpConstant %6 1 + %15 = OpConstant %6 2 + %17 = OpConstant %6 3 + %20 = OpConstant %6 4 + %23 = OpTypeStruct %6 + %24 = OpTypeStruct %6 %23 + %25 = OpTypeStruct %24 %6 + %26 = OpTypeStruct %6 %25 + %27 = OpTypePointer Uniform %26 + %51 = OpTypePointer Uniform %6 + %28 = OpVariable %27 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %7 Function + %21 = OpVariable %7 Function + OpStore %12 %13 + %14 = OpLoad %6 %12 + %16 = OpIAdd %6 %14 %15 + OpStore %12 %16 + %18 = OpLoad %6 %12 + %19 = OpIAdd %6 %17 %18 + OpStore %12 %19 + OpStore %21 %20 + %22 = OpFunctionCall %2 %10 %21 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = 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; + protobufs::UniformBufferElementDescriptor blockname_1 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_2 = + MakeUniformBufferElementDescriptor(0, 0, {1, 1}); + protobufs::UniformBufferElementDescriptor blockname_3 = + MakeUniformBufferElementDescriptor(0, 0, {1, 0, 0}); + protobufs::UniformBufferElementDescriptor blockname_4 = + MakeUniformBufferElementDescriptor(0, 0, {1, 0, 1, 0}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_1)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_2)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_3)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, blockname_4)); + + // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively. + protobufs::IdUseDescriptor use_of_13_in_store = + transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0); + protobufs::IdUseDescriptor use_of_15_in_add = + transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0); + protobufs::IdUseDescriptor use_of_17_in_add = + transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0); + protobufs::IdUseDescriptor use_of_20_in_store = + transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1); + + // These transformations work: they match the facts. + auto transformation_use_of_13_in_store = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_13_in_store, blockname_1, 100, 101); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_13_in_store, + context.get(), fact_manager)); + auto transformation_use_of_15_in_add = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_15_in_add, blockname_2, 102, 103); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add, + context.get(), fact_manager)); + auto transformation_use_of_17_in_add = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_17_in_add, blockname_3, 104, 105); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add, + context.get(), fact_manager)); + auto transformation_use_of_20_in_store = + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_20_in_store, blockname_4, 106, 107); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store, + context.get(), fact_manager)); + + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_13_in_store, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store, + context.get(), fact_manager)); + + transformation::Apply(transformation_use_of_13_in_store, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store, + context.get(), fact_manager)); + + transformation::Apply(transformation_use_of_15_in_add, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store, + context.get(), fact_manager)); + + transformation::Apply(transformation_use_of_17_in_add, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_17_in_add, + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store, + context.get(), fact_manager)); + + transformation::Apply(transformation_use_of_20_in_store, context.get(), + &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_17_in_add, + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_20_in_store, + context.get(), fact_manager)); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "a" + OpName %12 "x" + OpName %21 "param" + OpName %23 "U" + OpMemberName %23 0 "x" + OpName %24 "T" + OpMemberName %24 0 "x" + OpMemberName %24 1 "y" + OpName %25 "S" + OpMemberName %25 0 "x" + OpMemberName %25 1 "y" + OpName %26 "blockname" + OpMemberName %26 0 "x" + OpMemberName %26 1 "y" + OpName %28 "" + OpMemberDecorate %23 0 Offset 0 + OpMemberDecorate %24 0 Offset 0 + OpMemberDecorate %24 1 Offset 16 + OpMemberDecorate %25 0 Offset 0 + OpMemberDecorate %25 1 Offset 32 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 16 + OpDecorate %26 Block + OpDecorate %28 DescriptorSet 0 + OpDecorate %28 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpConstant %6 0 + %13 = OpConstant %6 1 + %15 = OpConstant %6 2 + %17 = OpConstant %6 3 + %20 = OpConstant %6 4 + %23 = OpTypeStruct %6 + %24 = OpTypeStruct %6 %23 + %25 = OpTypeStruct %24 %6 + %26 = OpTypeStruct %6 %25 + %27 = OpTypePointer Uniform %26 + %51 = OpTypePointer Uniform %6 + %28 = OpVariable %27 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %7 Function + %21 = OpVariable %7 Function + %100 = OpAccessChain %51 %28 %50 + %101 = OpLoad %6 %100 + OpStore %12 %101 + %14 = OpLoad %6 %12 + %102 = OpAccessChain %51 %28 %13 %13 + %103 = OpLoad %6 %102 + %16 = OpIAdd %6 %14 %103 + OpStore %12 %16 + %18 = OpLoad %6 %12 + %104 = OpAccessChain %51 %28 %13 %50 %50 + %105 = OpLoad %6 %104 + %19 = OpIAdd %6 %105 %18 + OpStore %12 %19 + %106 = OpAccessChain %51 %28 %13 %50 %13 %50 + %107 = OpLoad %6 %106 + OpStore %21 %107 + %22 = OpFunctionCall %2 %10 %21 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after, context.get())); +} + +TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int x; // == 0 + // }; + // + // void main() + // { + // int a; + // a = 0; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "blockname" + OpMemberName %10 0 "x" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + 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; + protobufs::UniformBufferElementDescriptor blockname_0 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_0)); + + // The constant id is 9 for 0. + protobufs::IdUseDescriptor use_of_9_in_store = + transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + + // This transformation is not available because no uniform pointer to integer + // type is present: + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_0, 100, 101), + context.get(), fact_manager)); +} + +TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int x; // == 0 + // int y; // == 9 + // }; + // + // void main() + // { + // int a; + // a = 9; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "blockname" + OpMemberName %10 0 "x" + OpMemberName %10 1 "y" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpMemberDecorate %10 1 Offset 4 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 9 + %10 = OpTypeStruct %6 %6 + %11 = OpTypePointer Uniform %10 + %50 = OpTypePointer Uniform %6 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + 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; + protobufs::UniformBufferElementDescriptor blockname_0 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_9 = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9)); + + // The constant id is 9 for 9. + protobufs::IdUseDescriptor use_of_9_in_store = + transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + + // This transformation is not available because no constant is present for the + // index 1 required to index into the uniform buffer: + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_9, 100, 101), + context.get(), fact_manager)); +} + +TEST(TransformationReplaceConstantWithUniformTest, + NoIntTypePresentToEnableIndexing) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // float f; // == 9 + // }; + // + // void main() + // { + // float a; + // a = 3.0; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "blockname" + OpMemberName %10 0 "f" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3 + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + 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; + protobufs::UniformBufferElementDescriptor blockname_3 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + uint32_t float_data[1]; + float temp = 3.0; + memcpy(&float_data[0], &temp, sizeof(float)); + ASSERT_TRUE( + AddFactHelper(&fact_manager, context.get(), float_data[0], blockname_3)); + + // The constant id is 9 for 3.0. + protobufs::IdUseDescriptor use_of_9_in_store = + transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + + // This transformation is not available because no integer type is present to + // allow a constant index to be expressed: + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_3, 100, 101), + context.get(), fact_manager)); +} + +TEST(TransformationReplaceConstantWithUniformTest, + UniformFactsDoNotMatchConstants) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int x; // == 9 + // int y; // == 10 + // }; + // + // void main() + // { + // int a; + // int b; + // a = 9; + // b = 10; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "blockname" + OpMemberName %12 0 "x" + OpMemberName %12 1 "y" + OpName %14 "" + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 1 Offset 4 + OpDecorate %12 Block + OpDecorate %14 DescriptorSet 0 + OpDecorate %14 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 9 + %11 = OpConstant %6 10 + %50 = OpConstant %6 0 + %51 = OpConstant %6 1 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Uniform %12 + %52 = OpTypePointer Uniform %6 + %14 = OpVariable %13 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + 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; + protobufs::UniformBufferElementDescriptor blockname_9 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_10 = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 10, blockname_10)); + + // The constant ids for 9 and 10 are 9 and 11 respectively + protobufs::IdUseDescriptor use_of_9_in_store = + transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0); + protobufs::IdUseDescriptor use_of_11_in_store = + transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1); + + // These are right: + ASSERT_TRUE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_9, 100, 101), + context.get(), fact_manager)); + ASSERT_TRUE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_11_in_store, blockname_10, 102, 103), + context.get(), fact_manager)); + + // These are wrong because the constants do not match the facts about + // uniforms. + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_11_in_store, blockname_9, 100, 101), + context.get(), fact_manager)); + ASSERT_FALSE(transformation::IsApplicable( + transformation::MakeTransformationReplaceConstantWithUniform( + use_of_9_in_store, blockname_10, 102, 103), + context.get(), fact_manager)); +} + +TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { + // The following GLSL was the basis for this test: + + // #version 450 + // + // struct T { + // float a[5]; // [1.0, 1.5, 1.75, 1.875, 1.9375] + // ivec4 b; // (1, 2, 3, 4) + // vec3 c; // (2.0, 2.5, 2.75) + // uint d; // 42u + // bool e; // Not used in test + // }; + // + // uniform block { + // T f; + // int g; // 22 + // uvec2 h; // (100u, 200u) + // }; + // + // void main() + // { + // T myT; + // + // myT.a[0] = 1.9375; + // myT.a[1] = 1.875; + // myT.a[2] = 1.75; + // myT.a[3] = 1.5; + // myT.a[4] = 1.0; + // + // myT.b.x = 4; + // myT.b.y = 3; + // myT.b.z = 2; + // myT.b.w = 1; + // + // myT.b.r = 22; + // + // myT.c[0] = 2.75; + // myT.c[0] = 2.5; + // myT.c[0] = 2.0; + // + // myT.d = 42u; + // myT.d = 100u; + // myT.d = 200u; + // + // myT.e = true; // No attempt to replace 'true' by a uniform value + // + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %14 "T" + OpMemberName %14 0 "a" + OpMemberName %14 1 "b" + OpMemberName %14 2 "c" + OpMemberName %14 3 "d" + OpMemberName %14 4 "e" + OpName %16 "myT" + OpName %61 "T" + OpMemberName %61 0 "a" + OpMemberName %61 1 "b" + OpMemberName %61 2 "c" + OpMemberName %61 3 "d" + OpMemberName %61 4 "e" + OpName %63 "block" + OpMemberName %63 0 "f" + OpMemberName %63 1 "g" + OpMemberName %63 2 "h" + OpName %65 "" + OpDecorate %60 ArrayStride 16 + OpMemberDecorate %61 0 Offset 0 + OpMemberDecorate %61 1 Offset 80 + OpMemberDecorate %61 2 Offset 96 + OpMemberDecorate %61 3 Offset 108 + OpMemberDecorate %61 4 Offset 112 + OpMemberDecorate %63 0 Offset 0 + OpMemberDecorate %63 1 Offset 128 + OpMemberDecorate %63 2 Offset 136 + OpDecorate %63 Block + OpDecorate %65 DescriptorSet 0 + OpDecorate %65 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 5 + %9 = OpTypeArray %6 %8 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 4 + %12 = OpTypeVector %6 3 + %13 = OpTypeBool + %14 = OpTypeStruct %9 %11 %12 %7 %13 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 0 + %18 = OpConstant %6 1.9375 + %19 = OpTypePointer Function %6 + %21 = OpConstant %10 1 + %22 = OpConstant %6 1.875 + %24 = OpConstant %10 2 + %25 = OpConstant %6 1.75 + %27 = OpConstant %10 3 + %28 = OpConstant %6 1.5 + %30 = OpConstant %10 4 + %31 = OpConstant %6 1 + %33 = OpConstant %7 0 + %34 = OpTypePointer Function %10 + %36 = OpConstant %7 1 + %38 = OpConstant %7 2 + %40 = OpConstant %7 3 + %42 = OpConstant %10 22 + %44 = OpConstant %6 2.75 + %46 = OpConstant %6 2.5 + %48 = OpConstant %6 2 + %50 = OpConstant %7 42 + %51 = OpTypePointer Function %7 + %53 = OpConstant %7 100 + %55 = OpConstant %7 200 + %57 = OpConstantTrue %13 + %58 = OpTypePointer Function %13 + %60 = OpTypeArray %6 %8 + %61 = OpTypeStruct %60 %11 %12 %7 %7 + %62 = OpTypeVector %7 2 + %63 = OpTypeStruct %61 %10 %62 + %64 = OpTypePointer Uniform %63 + %100 = OpTypePointer Uniform %10 + %101 = OpTypePointer Uniform %7 + %102 = OpTypePointer Uniform %6 + %103 = OpTypePointer Uniform %13 + %65 = OpVariable %64 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %16 = OpVariable %15 Function + %20 = OpAccessChain %19 %16 %17 %17 + OpStore %20 %18 + %23 = OpAccessChain %19 %16 %17 %21 + OpStore %23 %22 + %26 = OpAccessChain %19 %16 %17 %24 + OpStore %26 %25 + %29 = OpAccessChain %19 %16 %17 %27 + OpStore %29 %28 + %32 = OpAccessChain %19 %16 %17 %30 + OpStore %32 %31 + %35 = OpAccessChain %34 %16 %21 %33 + OpStore %35 %30 + %37 = OpAccessChain %34 %16 %21 %36 + OpStore %37 %27 + %39 = OpAccessChain %34 %16 %21 %38 + OpStore %39 %24 + %41 = OpAccessChain %34 %16 %21 %40 + OpStore %41 %21 + %43 = OpAccessChain %34 %16 %21 %33 + OpStore %43 %42 + %45 = OpAccessChain %19 %16 %24 %33 + OpStore %45 %44 + %47 = OpAccessChain %19 %16 %24 %33 + OpStore %47 %46 + %49 = OpAccessChain %19 %16 %24 %33 + OpStore %49 %48 + %52 = OpAccessChain %51 %16 %27 + OpStore %52 %50 + %54 = OpAccessChain %51 %16 %27 + OpStore %54 %53 + %56 = OpAccessChain %51 %16 %27 + OpStore %56 %55 + %59 = OpAccessChain %58 %16 %30 + OpStore %59 %57 + 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; + + const float float_array_values[5] = {1.0, 1.5, 1.75, 1.875, 1.9375}; + uint32_t float_array_data[5]; + memcpy(&float_array_data, &float_array_values, sizeof(float_array_values)); + + const float float_vector_values[3] = {2.0, 2.5, 2.75}; + uint32_t float_vector_data[3]; + memcpy(&float_vector_data, &float_vector_values, sizeof(float_vector_values)); + + protobufs::UniformBufferElementDescriptor uniform_f_a_0 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 0}); + protobufs::UniformBufferElementDescriptor uniform_f_a_1 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 1}); + protobufs::UniformBufferElementDescriptor uniform_f_a_2 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 2}); + protobufs::UniformBufferElementDescriptor uniform_f_a_3 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 3}); + protobufs::UniformBufferElementDescriptor uniform_f_a_4 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 4}); + + protobufs::UniformBufferElementDescriptor uniform_f_b_x = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 0}); + protobufs::UniformBufferElementDescriptor uniform_f_b_y = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 1}); + protobufs::UniformBufferElementDescriptor uniform_f_b_z = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 2}); + protobufs::UniformBufferElementDescriptor uniform_f_b_w = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 3}); + + protobufs::UniformBufferElementDescriptor uniform_f_c_x = + MakeUniformBufferElementDescriptor(0, 0, {0, 2, 0}); + protobufs::UniformBufferElementDescriptor uniform_f_c_y = + MakeUniformBufferElementDescriptor(0, 0, {0, 2, 1}); + protobufs::UniformBufferElementDescriptor uniform_f_c_z = + MakeUniformBufferElementDescriptor(0, 0, {0, 2, 2}); + + protobufs::UniformBufferElementDescriptor uniform_f_d = + MakeUniformBufferElementDescriptor(0, 0, {0, 3}); + + protobufs::UniformBufferElementDescriptor uniform_g = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + protobufs::UniformBufferElementDescriptor uniform_h_x = + MakeUniformBufferElementDescriptor(0, 0, {2, 0}); + protobufs::UniformBufferElementDescriptor uniform_h_y = + MakeUniformBufferElementDescriptor(0, 0, {2, 1}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[0], + uniform_f_a_0)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[1], + uniform_f_a_1)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[2], + uniform_f_a_2)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[3], + uniform_f_a_3)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[4], + uniform_f_a_4)); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, uniform_f_b_x)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, uniform_f_b_y)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, uniform_f_b_z)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, uniform_f_b_w)); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[0], + uniform_f_c_x)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[1], + uniform_f_c_y)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[2], + uniform_f_c_z)); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 42, uniform_f_d)); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22, uniform_g)); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 100, uniform_h_x)); + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200, uniform_h_y)); + + std::vector + transformations; + + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0), + uniform_f_a_4, 200, 201)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0), + uniform_f_a_3, 202, 203)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0), + uniform_f_a_2, 204, 205)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0), + uniform_f_a_1, 206, 207)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0), + uniform_f_a_0, 208, 209)); + + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0), + uniform_f_b_w, 210, 211)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0), + uniform_f_b_z, 212, 213)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0), + uniform_f_b_y, 214, 215)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0), + uniform_f_b_x, 216, 217)); + + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0), + uniform_f_c_z, 220, 221)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0), + uniform_f_c_y, 222, 223)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0), + uniform_f_c_x, 224, 225)); + + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0), + uniform_f_d, 226, 227)); + + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0), + uniform_h_x, 228, 229)); + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0), + uniform_h_y, 230, 231)); + + transformations.emplace_back( + transformation::MakeTransformationReplaceConstantWithUniform( + transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), + uniform_g, 218, 219)); + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(), + fact_manager)); + transformation::Apply(transformation, context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %14 "T" + OpMemberName %14 0 "a" + OpMemberName %14 1 "b" + OpMemberName %14 2 "c" + OpMemberName %14 3 "d" + OpMemberName %14 4 "e" + OpName %16 "myT" + OpName %61 "T" + OpMemberName %61 0 "a" + OpMemberName %61 1 "b" + OpMemberName %61 2 "c" + OpMemberName %61 3 "d" + OpMemberName %61 4 "e" + OpName %63 "block" + OpMemberName %63 0 "f" + OpMemberName %63 1 "g" + OpMemberName %63 2 "h" + OpName %65 "" + OpDecorate %60 ArrayStride 16 + OpMemberDecorate %61 0 Offset 0 + OpMemberDecorate %61 1 Offset 80 + OpMemberDecorate %61 2 Offset 96 + OpMemberDecorate %61 3 Offset 108 + OpMemberDecorate %61 4 Offset 112 + OpMemberDecorate %63 0 Offset 0 + OpMemberDecorate %63 1 Offset 128 + OpMemberDecorate %63 2 Offset 136 + OpDecorate %63 Block + OpDecorate %65 DescriptorSet 0 + OpDecorate %65 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 5 + %9 = OpTypeArray %6 %8 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 4 + %12 = OpTypeVector %6 3 + %13 = OpTypeBool + %14 = OpTypeStruct %9 %11 %12 %7 %13 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 0 + %18 = OpConstant %6 1.9375 + %19 = OpTypePointer Function %6 + %21 = OpConstant %10 1 + %22 = OpConstant %6 1.875 + %24 = OpConstant %10 2 + %25 = OpConstant %6 1.75 + %27 = OpConstant %10 3 + %28 = OpConstant %6 1.5 + %30 = OpConstant %10 4 + %31 = OpConstant %6 1 + %33 = OpConstant %7 0 + %34 = OpTypePointer Function %10 + %36 = OpConstant %7 1 + %38 = OpConstant %7 2 + %40 = OpConstant %7 3 + %42 = OpConstant %10 22 + %44 = OpConstant %6 2.75 + %46 = OpConstant %6 2.5 + %48 = OpConstant %6 2 + %50 = OpConstant %7 42 + %51 = OpTypePointer Function %7 + %53 = OpConstant %7 100 + %55 = OpConstant %7 200 + %57 = OpConstantTrue %13 + %58 = OpTypePointer Function %13 + %60 = OpTypeArray %6 %8 + %61 = OpTypeStruct %60 %11 %12 %7 %7 + %62 = OpTypeVector %7 2 + %63 = OpTypeStruct %61 %10 %62 + %64 = OpTypePointer Uniform %63 + %100 = OpTypePointer Uniform %10 + %101 = OpTypePointer Uniform %7 + %102 = OpTypePointer Uniform %6 + %103 = OpTypePointer Uniform %13 + %65 = OpVariable %64 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %16 = OpVariable %15 Function + %20 = OpAccessChain %19 %16 %17 %17 + %200 = OpAccessChain %102 %65 %17 %17 %30 + %201 = OpLoad %6 %200 + OpStore %20 %201 + %23 = OpAccessChain %19 %16 %17 %21 + %202 = OpAccessChain %102 %65 %17 %17 %27 + %203 = OpLoad %6 %202 + OpStore %23 %203 + %26 = OpAccessChain %19 %16 %17 %24 + %204 = OpAccessChain %102 %65 %17 %17 %24 + %205 = OpLoad %6 %204 + OpStore %26 %205 + %29 = OpAccessChain %19 %16 %17 %27 + %206 = OpAccessChain %102 %65 %17 %17 %21 + %207 = OpLoad %6 %206 + OpStore %29 %207 + %32 = OpAccessChain %19 %16 %17 %30 + %208 = OpAccessChain %102 %65 %17 %17 %17 + %209 = OpLoad %6 %208 + OpStore %32 %209 + %35 = OpAccessChain %34 %16 %21 %33 + %210 = OpAccessChain %100 %65 %17 %21 %27 + %211 = OpLoad %10 %210 + OpStore %35 %211 + %37 = OpAccessChain %34 %16 %21 %36 + %212 = OpAccessChain %100 %65 %17 %21 %24 + %213 = OpLoad %10 %212 + OpStore %37 %213 + %39 = OpAccessChain %34 %16 %21 %38 + %214 = OpAccessChain %100 %65 %17 %21 %21 + %215 = OpLoad %10 %214 + OpStore %39 %215 + %41 = OpAccessChain %34 %16 %21 %40 + %216 = OpAccessChain %100 %65 %17 %21 %17 + %217 = OpLoad %10 %216 + OpStore %41 %217 + %43 = OpAccessChain %34 %16 %21 %33 + %218 = OpAccessChain %100 %65 %21 + %219 = OpLoad %10 %218 + OpStore %43 %219 + %45 = OpAccessChain %19 %16 %24 %33 + %220 = OpAccessChain %102 %65 %17 %24 %24 + %221 = OpLoad %6 %220 + OpStore %45 %221 + %47 = OpAccessChain %19 %16 %24 %33 + %222 = OpAccessChain %102 %65 %17 %24 %21 + %223 = OpLoad %6 %222 + OpStore %47 %223 + %49 = OpAccessChain %19 %16 %24 %33 + %224 = OpAccessChain %102 %65 %17 %24 %17 + %225 = OpLoad %6 %224 + OpStore %49 %225 + %52 = OpAccessChain %51 %16 %27 + %226 = OpAccessChain %101 %65 %17 %27 + %227 = OpLoad %7 %226 + OpStore %52 %227 + %54 = OpAccessChain %51 %16 %27 + %228 = OpAccessChain %101 %65 %24 %17 + %229 = OpLoad %7 %228 + OpStore %54 %229 + %56 = OpAccessChain %51 %16 %27 + %230 = OpAccessChain %101 %65 %24 %21 + %231 = OpLoad %7 %230 + OpStore %56 %231 + %59 = OpAccessChain %58 %16 %30 + OpStore %59 %57 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp b/3rdparty/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp new file mode 100644 index 000000000..6c6d52a04 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(UniformBufferElementDescriptorTest, TestEquality) { + // Test that equality works as expected for various buffer element + // descriptors. + + protobufs::UniformBufferElementDescriptor descriptor1 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor2 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor3 = + MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor4 = + MakeUniformBufferElementDescriptor(1, 0, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor5 = + MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor6 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2, 4}); + protobufs::UniformBufferElementDescriptor descriptor7 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2}); + + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor1)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor2)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor3)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor4)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor5)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor6)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor7)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor3)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor4)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor5)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor6)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor7)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor1)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/upgrade_memory_model_test.cpp b/3rdparty/spirv-tools/test/opt/upgrade_memory_model_test.cpp index 8de7e0216..b012383a8 100644 --- a/3rdparty/spirv-tools/test/opt/upgrade_memory_model_test.cpp +++ b/3rdparty/spirv-tools/test/opt/upgrade_memory_model_test.cpp @@ -2037,4 +2037,203 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoad) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768 +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %ssbo_var %device %relaxed +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadPreviousFlags) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834 +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%acquire_ssbo = OpConstant %int 66 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %ssbo_var %device %acquire_ssbo +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicStore) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32768 +; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpAtomicStore %ssbo_var %device %relaxed %int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicStorePreviousFlags) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32836 +; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%device = OpConstant %int 1 +%release_ssbo = OpConstant %int 68 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpAtomicStore %ssbo_var %device %release_ssbo %int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchange) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768 +; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile]] [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicCompareExchange %int %ssbo_var %device %relaxed %relaxed %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchangePreviousFlags) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile_acq_rel:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32840 +; CHECK: [[volatile_acq:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834 +; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile_acq_rel]] [[volatile_acq]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%device = OpConstant %int 1 +%acq_ssbo = OpConstant %int 66 +%acq_rel_ssbo = OpConstant %int 72 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicCompareExchange %int %ssbo_var %device %acq_rel_ssbo %acq_ssbo %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadMemberDecoration) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate {{.*}} {{.*}} Volatile +; CHECK: [[relaxed:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 0 +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768 +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[relaxed]] +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 1 Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%struct = OpTypeStruct %int %int +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%gep0 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0 +%ld0 = OpAtomicLoad %int %gep0 %device %relaxed +%gep1 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_1 +%ld1 = OpAtomicLoad %int %gep1 %device %relaxed +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + } // namespace diff --git a/3rdparty/spirv-tools/test/val/val_atomics_test.cpp b/3rdparty/spirv-tools/test/val/val_atomics_test.cpp index 58b2846c2..f130e02ed 100644 --- a/3rdparty/spirv-tools/test/val/val_atomics_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_atomics_test.cpp @@ -1998,6 +1998,153 @@ TEST_F(ValidateAtomics, CompareExchangeWeakV14Bad) { "AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier")); } +TEST_F(ValidateAtomics, CompareExchangeVolatileMatch) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpConstant %int 32768 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %volatile %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, CompareExchangeVolatileMismatch) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpConstant %int 32768 +%non_volatile = OpConstant %int 0 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %non_volatile %volatile %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Volatile mask setting must match for Equal and " + "Unequal memory semantics")); +} + +TEST_F(ValidateAtomics, CompareExchangeVolatileMismatchCooperativeMatrix) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpCapability CooperativeMatrixNV +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpSpecConstant %int 32768 +%non_volatile = OpSpecConstant %int 32768 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %non_volatile %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + // This is ok because we cannot evaluate the spec constant defaults. + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, VolatileRequiresVulkanMemoryModel) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpConstant %int 32768 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %wg_var %workgroup %volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Volatile requires capability " + "VulkanMemoryModelKHR")); +} + +TEST_F(ValidateAtomics, CooperativeMatrixSemanticsMustBeConstant) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%undef = OpUndef %int +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %wg_var %workgroup %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_barriers_test.cpp b/3rdparty/spirv-tools/test/val/val_barriers_test.cpp index cf81e42a9..b28e51a85 100644 --- a/3rdparty/spirv-tools/test/val/val_barriers_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_barriers_test.cpp @@ -1361,6 +1361,115 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } +TEST_F(ValidateBarriers, VolatileMemoryBarrier) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpConstant %int 32768 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpMemoryBarrier %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Volatile can only be used with " + "atomic instructions")); +} + +TEST_F(ValidateBarriers, VolatileControlBarrier) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpConstant %int 32768 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Volatile can only be used with " + "atomic instructions")); +} + +TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability CooperativeMatrixNV +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpSpecConstant %int 32768 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability CooperativeMatrixNV +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpUndef %int +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_capability_test.cpp b/3rdparty/spirv-tools/test/val/val_capability_test.cpp index 4fb2a7cbd..5a6e75127 100644 --- a/3rdparty/spirv-tools/test/val/val_capability_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_capability_test.cpp @@ -1168,8 +1168,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Component 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Component 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" diff --git a/3rdparty/spirv-tools/test/val/val_decoration_test.cpp b/3rdparty/spirv-tools/test/val/val_decoration_test.cpp index c454bed54..5ea1d87ad 100644 --- a/3rdparty/spirv-tools/test/val/val_decoration_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_decoration_test.cpp @@ -6400,6 +6400,314 @@ OpDecorate %1 BufferBlock "requires SPIR-V version 1.3 or earlier")); } +// Component + +TEST_F(ValidateDecorations, ComponentDecorationBadTarget) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %t Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%t = OpTypeVector %float 2 +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of Component decoration must be " + "a memory object declaration")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %v Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%t = OpTypeVector %float 2 +%ptr_private = OpTypePointer Private %t +%v = OpVariable %ptr_private Private +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of Component decoration is invalid: must " + "point to a Storage Class of Input(1) or Output(3)")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBadTypeVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpCapability Matrix +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %v Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%vtype = OpTypeVector %float 4 +%t = OpTypeMatrix %vtype 4 +%ptr_input = OpTypePointer Input %t +%v = OpVariable %ptr_input Input +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component decoration specified for type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector")); +} + +std::string ShaderWithComponentDecoration(const std::string& type, + const std::string& decoration) { + return R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpDecorate %entryPointOutput Location 0 +OpDecorate %entryPointOutput )" + + decoration + R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%vtype = )" + type + R"( +%float_0 = OpConstant %float 0 +%_ptr_Output_vtype = OpTypePointer Output %vtype +%entryPointOutput = OpVariable %_ptr_Output_vtype Output +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 0"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 2"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 3"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 4"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 4 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 3", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 0"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 1 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 3"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 3 " + "and ending with 6 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBlockGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %12 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %9 Location 0 +OpMemberDecorate %block 0 Location 2 +OpMemberDecorate %block 0 Component 1 +OpDecorate %block Block +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%8 = OpTypePointer Output %vec3 +%9 = OpVariable %8 Output +%block = OpTypeStruct %vec3 +%11 = OpTypePointer Input %block +%12 = OpVariable %11 Input +%int = OpTypeInt 32 1 +%14 = OpConstant %int 0 +%15 = OpTypePointer Input %vec3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %vec3 %16 +OpStore %9 %17 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBlockBadVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %12 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %9 Location 0 +OpMemberDecorate %block 0 Location 2 +OpMemberDecorate %block 0 Component 2 +OpDecorate %block Block +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%8 = OpTypePointer Output %vec3 +%9 = OpVariable %8 Output +%block = OpTypeStruct %vec3 +%11 = OpTypePointer Input %block +%12 = OpVariable %11 Input +%int = OpTypeInt 32 1 +%14 = OpConstant %int 0 +%15 = OpTypePointer Input %vec3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %vec3 %16 +OpStore %9 %17 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 2 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + + OpDecorate %param_f Component 0 + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 + %struct_b = OpTypeStruct %float + +%extra_fn = OpTypeFunction %void %float + + %helper = OpFunction %void None %extra_fn + %param_f = OpFunctionParameter %float +%helper_label = OpLabel + OpReturn + OpFunctionEnd + + %main = OpFunction %void None %void_fn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_memory_test.cpp b/3rdparty/spirv-tools/test/val/val_memory_test.cpp index f10582209..c28a11d56 100644 --- a/3rdparty/spirv-tools/test/val/val_memory_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_memory_test.cpp @@ -3566,6 +3566,272 @@ OpMemoryModel Logical GLSL450 "the Result Type")); } +TEST_F(ValidateMemory, StoreToUniformBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, StoreToUniformBlockVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +// This test requires that the struct is not id 2. +TEST_F(ValidateMemory, StoreToUniformBlockVulkan2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gid_var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %3 Block +OpMemberDecorate %3 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %gid_var BuiltIn GlobalInvocationId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int3 = OpTypeVector %int 3 +%int4 = OpTypeVector %int 4 +%3 = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %3 +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%ptr_input_int3 = OpTypePointer Input %int3 +%gid_var = OpVariable %ptr_input_int3 Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +TEST_F(ValidateMemory, StoreToUniformBufferBlockVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct BufferBlock +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%array_struct = OpTypeArray %struct %int_1 +%ptr_uniform_array = OpTypePointer Uniform %array_struct +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_array Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0 +%gep2 = OpCopyObject %ptr_uniform_int %gep1 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +// This test requires that the struct is not id 2. +TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gid_var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %gid_var BuiltIn GlobalInvocationId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int3 = OpTypeVector %int 3 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%array_struct = OpTypeArray %struct %int_1 +%ptr_uniform_array = OpTypePointer Uniform %array_struct +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_array Uniform +%ptr_input_int3 = OpTypePointer Input %int3 +%gid_var = OpVariable %ptr_input_int3 Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0 +%gep2 = OpCopyObject %ptr_uniform_int %gep1 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +TEST_F(ValidateMemory, StoreToUniformBlockVulkanRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%array_struct = OpTypeRuntimeArray %struct +%ptr_uniform_array = OpTypePointer Uniform %array_struct +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_array Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 %int_0 +%gep2 = OpInBoundsAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/tools/as/as.cpp b/3rdparty/spirv-tools/tools/as/as.cpp index 1568579c0..acef5b46d 100644 --- a/3rdparty/spirv-tools/tools/as/as.cpp +++ b/3rdparty/spirv-tools/tools/as/as.cpp @@ -21,6 +21,7 @@ #include "tools/io.h" void print_usage(char* argv0) { + std::string target_env_list = spvTargetEnvList(19, 80); printf( R"(%s - Create a SPIR-V binary module from SPIR-V assembly text @@ -41,11 +42,10 @@ Options: Numeric IDs in the binary will have the same values as in the source. Non-numeric IDs are allocated by filling in the gaps, starting with 1 and going up. - --target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4} - Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1, - SPIR-V 1.2, SPIR-V 1.3, or SPIR-V 1.4 + --target-env {%s} + Use specified environment. )", - argv0, argv0); + argv0, argv0, target_env_list.c_str()); } static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4; diff --git a/3rdparty/spirv-tools/tools/fuzz/fuzz.cpp b/3rdparty/spirv-tools/tools/fuzz/fuzz.cpp index 089dc4e03..9943dff61 100644 --- a/3rdparty/spirv-tools/tools/fuzz/fuzz.cpp +++ b/3rdparty/spirv-tools/tools/fuzz/fuzz.cpp @@ -213,7 +213,7 @@ bool Replay(const spv_target_env& target_env, bool Fuzz(const spv_target_env& target_env, const spvtools::FuzzerOptions& fuzzer_options, - const std::vector& binary_in, + const std::vector& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, std::vector* binary_out, spvtools::fuzz::protobufs::TransformationSequence* diff --git a/3rdparty/spirv-tools/tools/link/linker.cpp b/3rdparty/spirv-tools/tools/link/linker.cpp index 3508b1351..82d430ebb 100644 --- a/3rdparty/spirv-tools/tools/link/linker.cpp +++ b/3rdparty/spirv-tools/tools/link/linker.cpp @@ -23,6 +23,7 @@ #include "tools/io.h" void print_usage(char* argv0) { + std::string target_env_list = spvTargetEnvList(27, 95); printf( R"(%s - Link SPIR-V binary files together. @@ -39,11 +40,10 @@ Options: --allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved. --verify-ids Verify that IDs in the resulting modules are truly unique. --version Display linker version information - --target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4|opencl2.1|opencl2.2} - Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3, - SPIR-V1.4, OpenCL 2.1, OpenCL 2.2 validation rules. + --target-env {%s} + Use validation rules from the specified environment. )", - argv0, argv0); + argv0, argv0, target_env_list.c_str()); } int main(int argc, char** argv) { diff --git a/3rdparty/spirv-tools/tools/opt/opt.cpp b/3rdparty/spirv-tools/tools/opt/opt.cpp index 585952b62..af527e33e 100644 --- a/3rdparty/spirv-tools/tools/opt/opt.cpp +++ b/3rdparty/spirv-tools/tools/opt/opt.cpp @@ -92,6 +92,7 @@ std::string GetWebGPUToVulkanPasses() { } void PrintUsage(const char* program) { + std::string target_env_list = spvTargetEnvList(16, 80); // NOTE: Please maintain flags in lexicographical order. printf( R"(%s - Optimize a SPIR-V binary file. @@ -436,9 +437,9 @@ Options (in lexicographical order):)", printf(R"( --target-env= Set the target environment. Without this flag the target - enviroment defaults to spv1.3. - must be one of vulkan1.0, vulkan1.1, opencl2.2, spv1.0, - spv1.1, spv1.2, spv1.3, or webgpu0.)"); + enviroment defaults to spv1.3. must be one of + {%s})", + target_env_list.c_str()); printf(R"( --time-report Print the resource utilization of each pass (e.g., CPU time, diff --git a/3rdparty/spirv-tools/tools/val/val.cpp b/3rdparty/spirv-tools/tools/val/val.cpp index a61f4d1f3..6a8542d4a 100644 --- a/3rdparty/spirv-tools/tools/val/val.cpp +++ b/3rdparty/spirv-tools/tools/val/val.cpp @@ -25,6 +25,7 @@ #include "tools/util/cli_consumer.h" void print_usage(char* argv0) { + std::string target_env_list = spvTargetEnvList(36, 105); printf( R"(%s - Validate a SPIR-V binary file. @@ -65,13 +66,10 @@ Options: --before-hlsl-legalization Allows code patterns that are intended to be fixed by spirv-opt's legalization passes. --version Display validator version information. - --target-env {vulkan1.0|vulkan1.1|vulkan1.1spv1.4|opencl2.2|spv1.0|spv1.1| - spv1.2|spv1.3|spv1.4|webgpu0} - Use Vulkan 1.0, Vulkan 1.1, Vulkan 1.1 with SPIR-V 1.4, - OpenCL 2.2, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3, - SPIR-V 1.4, or WIP WebGPU validation rules. + --target-env {%s} + Use validation rules from the specified environment. )", - argv0, argv0); + argv0, argv0, target_env_list.c_str()); } int main(int argc, char** argv) { diff --git a/3rdparty/spirv-tools/utils/git-sync-deps b/3rdparty/spirv-tools/utils/git-sync-deps new file mode 100755 index 000000000..057564132 --- /dev/null +++ b/3rdparty/spirv-tools/utils/git-sync-deps @@ -0,0 +1,282 @@ +#!/usr/bin/env python +# Copyright 2014 Google Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Parse a DEPS file and git checkout all of the dependencies. + +Args: + An optional list of deps_os values. + +Environment Variables: + GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of + ['git', 'git.exe', 'git.bat'] in your default path. + + GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset, + will use the file ../DEPS relative to this script's directory. + + GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages. + +Git Config: + To disable syncing of a single repository: + cd path/to/repository + git config sync-deps.disable true + + To re-enable sync: + cd path/to/repository + git config --unset sync-deps.disable +""" + + +import os +import re +import subprocess +import sys +import threading +from builtins import bytes + + +def git_executable(): + """Find the git executable. + + Returns: + A string suitable for passing to subprocess functions, or None. + """ + envgit = os.environ.get('GIT_EXECUTABLE') + searchlist = ['git', 'git.exe', 'git.bat'] + if envgit: + searchlist.insert(0, envgit) + with open(os.devnull, 'w') as devnull: + for git in searchlist: + try: + subprocess.call([git, '--version'], stdout=devnull) + except (OSError,): + continue + return git + return None + + +DEFAULT_DEPS_PATH = os.path.normpath( + os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS')) + + +def usage(deps_file_path = None): + sys.stderr.write( + 'Usage: run to grab dependencies, with optional platform support:\n') + sys.stderr.write(' %s %s' % (sys.executable, __file__)) + if deps_file_path: + parsed_deps = parse_file_to_dict(deps_file_path) + if 'deps_os' in parsed_deps: + for deps_os in parsed_deps['deps_os']: + sys.stderr.write(' [%s]' % deps_os) + sys.stderr.write('\n\n') + sys.stderr.write(__doc__) + + +def git_repository_sync_is_disabled(git, directory): + try: + disable = subprocess.check_output( + [git, 'config', 'sync-deps.disable'], cwd=directory) + return disable.lower().strip() in ['true', '1', 'yes', 'on'] + except subprocess.CalledProcessError: + return False + + +def is_git_toplevel(git, directory): + """Return true iff the directory is the top level of a Git repository. + + Args: + git (string) the git executable + + directory (string) the path into which the repository + is expected to be checked out. + """ + try: + toplevel = subprocess.check_output( + [git, 'rev-parse', '--show-toplevel'], cwd=directory).strip() + return os.path.realpath(bytes(directory, 'utf8')) == os.path.realpath(toplevel) + except subprocess.CalledProcessError: + return False + + +def status(directory, checkoutable): + def truncate(s, length): + return s if len(s) <= length else s[:(length - 3)] + '...' + dlen = 36 + directory = truncate(directory, dlen) + checkoutable = truncate(checkoutable, 40) + sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable)) + + +def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): + """Checkout (and clone if needed) a Git repository. + + Args: + git (string) the git executable + + repo (string) the location of the repository, suitable + for passing to `git clone`. + + checkoutable (string) a tag, branch, or commit, suitable for + passing to `git checkout` + + directory (string) the path into which the repository + should be checked out. + + verbose (boolean) + + Raises an exception if any calls to git fail. + """ + if not os.path.isdir(directory): + subprocess.check_call( + [git, 'clone', '--quiet', repo, directory]) + + if not is_git_toplevel(git, directory): + # if the directory exists, but isn't a git repo, you will modify + # the parent repostory, which isn't what you want. + sys.stdout.write('%s\n IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory) + return + + # Check to see if this repo is disabled. Quick return. + if git_repository_sync_is_disabled(git, directory): + sys.stdout.write('%s\n SYNC IS DISABLED.\n' % directory) + return + + with open(os.devnull, 'w') as devnull: + # If this fails, we will fetch before trying again. Don't spam user + # with error infomation. + if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], + cwd=directory, stderr=devnull): + # if this succeeds, skip slow `git fetch`. + if verbose: + status(directory, checkoutable) # Success. + return + + # If the repo has changed, always force use of the correct repo. + # If origin already points to repo, this is a quick no-op. + subprocess.check_call( + [git, 'remote', 'set-url', 'origin', repo], cwd=directory) + + subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory) + + subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory) + + if verbose: + status(directory, checkoutable) # Success. + + +def parse_file_to_dict(path): + dictionary = {} + contents = open(path).read() + # Need to convert Var() to vars[], so that the DEPS is actually Python. Var() + # comes from Autoroller using gclient which has a slightly different DEPS + # format. + contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents) + exec(contents, dictionary) + return dictionary + + +def git_sync_deps(deps_file_path, command_line_os_requests, verbose): + """Grab dependencies, with optional platform support. + + Args: + deps_file_path (string) Path to the DEPS file. + + command_line_os_requests (list of strings) Can be empty list. + List of strings that should each be a key in the deps_os + dictionary in the DEPS file. + + Raises git Exceptions. + """ + git = git_executable() + assert git + + deps_file_directory = os.path.dirname(deps_file_path) + deps_file = parse_file_to_dict(deps_file_path) + dependencies = deps_file['deps'].copy() + os_specific_dependencies = deps_file.get('deps_os', dict()) + if 'all' in command_line_os_requests: + for value in list(os_specific_dependencies.values()): + dependencies.update(value) + else: + for os_name in command_line_os_requests: + # Add OS-specific dependencies + if os_name in os_specific_dependencies: + dependencies.update(os_specific_dependencies[os_name]) + for directory in dependencies: + for other_dir in dependencies: + if directory.startswith(other_dir + '/'): + raise Exception('%r is parent of %r' % (other_dir, directory)) + list_of_arg_lists = [] + for directory in sorted(dependencies): + if '@' in dependencies[directory]: + repo, checkoutable = dependencies[directory].split('@', 1) + else: + raise Exception("please specify commit or tag") + + relative_directory = os.path.join(deps_file_directory, directory) + + list_of_arg_lists.append( + (git, repo, checkoutable, relative_directory, verbose)) + + multithread(git_checkout_to_directory, list_of_arg_lists) + + for directory in deps_file.get('recursedeps', []): + recursive_path = os.path.join(deps_file_directory, directory, 'DEPS') + git_sync_deps(recursive_path, command_line_os_requests, verbose) + + +def multithread(function, list_of_arg_lists): + # for args in list_of_arg_lists: + # function(*args) + # return + threads = [] + for args in list_of_arg_lists: + thread = threading.Thread(None, function, None, args) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + + +def main(argv): + deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH) + verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False)) + + if '--help' in argv or '-h' in argv: + usage(deps_file_path) + return 1 + + git_sync_deps(deps_file_path, argv, verbose) + # subprocess.check_call( + # [sys.executable, + # os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')]) + return 0 + + +if __name__ == '__main__': + exit(main(sys.argv[1:])) diff --git a/3rdparty/spirv-tools/utils/roll_deps.sh b/3rdparty/spirv-tools/utils/roll_deps.sh new file mode 100755 index 000000000..622afc9ed --- /dev/null +++ b/3rdparty/spirv-tools/utils/roll_deps.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Copyright (c) 2019 Google 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. + +# Attempts to roll all entries in DEPS to origin/master and creates a +# commit. +# +# Depends on roll-dep from depot_path being in PATH. + +# This script assumes it's parent directory is the repo root. +repo_path=$(dirname "$0")/.. + +effcee_dir="external/effcee/" +googletest_dir="external/googletest/" +re2_dir="external/re2/" +spirv_headers_dir="external/spirv-headers/" + +cd "$repo_path" + +roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"