1 // Copyright (c) 2016 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_ 16 #define LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_ 17 18 #include <iostream> 19 #include <string> 20 #include <tuple> 21 #include <vector> 22 23 #include <gtest/gtest.h> 24 25 #include "opt/build_module.h" 26 #include "opt/make_unique.h" 27 #include "opt/pass_manager.h" 28 #include "opt/passes.h" 29 #include "spirv-tools/libspirv.hpp" 30 31 namespace spvtools { 32 33 // Template class for testing passes. It contains some handy utility methods for 34 // running passes and checking results. 35 // 36 // To write value-Parameterized tests: 37 // using ValueParamTest = PassTest<::testing::TestWithParam<std::string>>; 38 // To use as normal fixture: 39 // using FixtureTest = PassTest<::testing::Test>; 40 template <typename TestT> 41 class PassTest : public TestT { 42 public: PassTest()43 PassTest() 44 : consumer_(nullptr), 45 tools_(SPV_ENV_UNIVERSAL_1_1), 46 manager_(new opt::PassManager()), 47 assemble_options_(SpirvTools::kDefaultAssembleOption), 48 disassemble_options_(SpirvTools::kDefaultDisassembleOption) {} 49 50 // Runs the given |pass| on the binary assembled from the |original|. 51 // Returns a tuple of the optimized binary and the boolean value returned 52 // from pass Process() function. OptimizeToBinary(opt::Pass * pass,const std::string & original,bool skip_nop)53 std::tuple<std::vector<uint32_t>, opt::Pass::Status> OptimizeToBinary( 54 opt::Pass* pass, const std::string& original, bool skip_nop) { 55 std::unique_ptr<ir::Module> module = BuildModule( 56 SPV_ENV_UNIVERSAL_1_1, consumer_, original, assemble_options_); 57 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 58 << original << std::endl; 59 if (!module) { 60 return std::make_tuple(std::vector<uint32_t>(), 61 opt::Pass::Status::Failure); 62 } 63 64 const auto status = pass->Process(module.get()); 65 66 std::vector<uint32_t> binary; 67 module->ToBinary(&binary, skip_nop); 68 return std::make_tuple(binary, status); 69 } 70 71 // Runs a single pass of class |PassT| on the binary assembled from the 72 // |assembly|. Returns a tuple of the optimized binary and the boolean value 73 // from the pass Process() function. 74 template <typename PassT, typename... Args> SinglePassRunToBinary(const std::string & assembly,bool skip_nop,Args &&...args)75 std::tuple<std::vector<uint32_t>, opt::Pass::Status> SinglePassRunToBinary( 76 const std::string& assembly, bool skip_nop, Args&&... args) { 77 auto pass = MakeUnique<PassT>(std::forward<Args>(args)...); 78 pass->SetMessageConsumer(consumer_); 79 return OptimizeToBinary(pass.get(), assembly, skip_nop); 80 } 81 82 // Runs a single pass of class |PassT| on the binary assembled from the 83 // |assembly|, disassembles the optimized binary. Returns a tuple of 84 // disassembly string and the boolean value from the pass Process() function. 85 template <typename PassT, typename... Args> SinglePassRunAndDisassemble(const std::string & assembly,bool skip_nop,Args &&...args)86 std::tuple<std::string, opt::Pass::Status> SinglePassRunAndDisassemble( 87 const std::string& assembly, bool skip_nop, Args&&... args) { 88 std::vector<uint32_t> optimized_bin; 89 auto status = opt::Pass::Status::SuccessWithoutChange; 90 std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>( 91 assembly, skip_nop, std::forward<Args>(args)...); 92 std::string optimized_asm; 93 EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm, 94 disassemble_options_)) 95 << "Disassembling failed for shader:\n" 96 << assembly << std::endl; 97 return std::make_tuple(optimized_asm, status); 98 } 99 100 // Runs a single pass of class |PassT| on the binary assembled from the 101 // |original| assembly, and checks whether the optimized binary can be 102 // disassembled to the |expected| assembly. Optionally will also validate 103 // the optimized binary. This does *not* involve pass manager. Callers 104 // are suggested to use SCOPED_TRACE() for better messages. 105 template <typename PassT, typename... Args> SinglePassRunAndCheck(const std::string & original,const std::string & expected,bool skip_nop,bool do_validation,Args &&...args)106 void SinglePassRunAndCheck(const std::string& original, 107 const std::string& expected, bool skip_nop, 108 bool do_validation, Args&&... args) { 109 std::vector<uint32_t> optimized_bin; 110 auto status = opt::Pass::Status::SuccessWithoutChange; 111 std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>( 112 original, skip_nop, std::forward<Args>(args)...); 113 // Check whether the pass returns the correct modification indication. 114 EXPECT_NE(opt::Pass::Status::Failure, status); 115 EXPECT_EQ(original == expected, 116 status == opt::Pass::Status::SuccessWithoutChange); 117 if (do_validation) { 118 spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1; 119 spv_context context = spvContextCreate(target_env); 120 spv_diagnostic diagnostic = nullptr; 121 spv_const_binary_t binary = {optimized_bin.data(), 122 optimized_bin.size()}; 123 spv_result_t error = spvValidate(context, &binary, &diagnostic); 124 EXPECT_EQ(error, 0); 125 if (error != 0) 126 spvDiagnosticPrint(diagnostic); 127 spvDiagnosticDestroy(diagnostic); 128 spvContextDestroy(context); 129 } 130 std::string optimized_asm; 131 EXPECT_TRUE(tools_.Disassemble(optimized_bin, &optimized_asm, 132 disassemble_options_)) 133 << "Disassembling failed for shader:\n" 134 << original << std::endl; 135 EXPECT_EQ(expected, optimized_asm); 136 } 137 138 // Runs a single pass of class |PassT| on the binary assembled from the 139 // |original| assembly, and checks whether the optimized binary can be 140 // disassembled to the |expected| assembly. This does *not* involve pass 141 // manager. Callers are suggested to use SCOPED_TRACE() for better messages. 142 template <typename PassT, typename... Args> SinglePassRunAndCheck(const std::string & original,const std::string & expected,bool skip_nop,Args &&...args)143 void SinglePassRunAndCheck(const std::string& original, 144 const std::string& expected, bool skip_nop, 145 Args&&... args) { 146 SinglePassRunAndCheck<PassT>(original, expected, skip_nop, false, 147 std::forward<Args>(args)...); 148 } 149 150 // Adds a pass to be run. 151 template <typename PassT, typename... Args> AddPass(Args &&...args)152 void AddPass(Args&&... args) { 153 manager_->AddPass<PassT>(std::forward<Args>(args)...); 154 } 155 156 // Renews the pass manager, including clearing all previously added passes. RenewPassManger()157 void RenewPassManger() { 158 manager_.reset(new opt::PassManager()); 159 manager_->SetMessageConsumer(consumer_); 160 } 161 162 // Runs the passes added thus far using a pass manager on the binary assembled 163 // from the |original| assembly, and checks whether the optimized binary can 164 // be disassembled to the |expected| assembly. Callers are suggested to use 165 // SCOPED_TRACE() for better messages. RunAndCheck(const std::string & original,const std::string & expected)166 void RunAndCheck(const std::string& original, const std::string& expected) { 167 assert(manager_->NumPasses()); 168 169 std::unique_ptr<ir::Module> module = BuildModule( 170 SPV_ENV_UNIVERSAL_1_1, nullptr, original, assemble_options_); 171 ASSERT_NE(nullptr, module); 172 173 manager_->Run(module.get()); 174 175 std::vector<uint32_t> binary; 176 module->ToBinary(&binary, /* skip_nop = */ false); 177 178 std::string optimized; 179 EXPECT_TRUE(tools_.Disassemble(binary, &optimized, 180 disassemble_options_)); 181 EXPECT_EQ(expected, optimized); 182 } 183 SetAssembleOptions(uint32_t assemble_options)184 void SetAssembleOptions(uint32_t assemble_options) { 185 assemble_options_ = assemble_options; 186 } 187 SetDisassembleOptions(uint32_t disassemble_options)188 void SetDisassembleOptions(uint32_t disassemble_options) { 189 disassemble_options_ = disassemble_options; 190 } 191 192 private: 193 MessageConsumer consumer_; // Message consumer. 194 SpirvTools tools_; // An instance for calling SPIRV-Tools functionalities. 195 std::unique_ptr<opt::PassManager> manager_; // The pass manager. 196 uint32_t assemble_options_; 197 uint32_t disassemble_options_; 198 }; 199 200 } // namespace spvtools 201 202 #endif // LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_ 203