1 // Copyright (c) 2015-2016 The Khronos Group 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 TEST_UNIT_SPIRV_H_ 16 #define TEST_UNIT_SPIRV_H_ 17 18 #include <stdint.h> 19 20 #include <iomanip> 21 #include <string> 22 #include <vector> 23 24 #include "gtest/gtest.h" 25 #include "source/assembly_grammar.h" 26 #include "source/binary.h" 27 #include "source/diagnostic.h" 28 #include "source/enum_set.h" 29 #include "source/opcode.h" 30 #include "source/spirv_endian.h" 31 #include "source/text.h" 32 #include "source/text_handler.h" 33 #include "source/val/validate.h" 34 #include "spirv-tools/libspirv.h" 35 36 #ifdef __ANDROID__ 37 #include <sstream> 38 namespace std { 39 template <typename T> 40 std::string to_string(const T& val) { 41 std::ostringstream os; 42 os << val; 43 return os.str(); 44 } 45 } // namespace std 46 #endif 47 48 // Determine endianness & predicate tests on it 49 enum { 50 I32_ENDIAN_LITTLE = 0x03020100ul, 51 I32_ENDIAN_BIG = 0x00010203ul, 52 }; 53 54 static const union { 55 unsigned char bytes[4]; 56 uint32_t value; 57 } o32_host_order = {{0, 1, 2, 3}}; 58 #define I32_ENDIAN_HOST (o32_host_order.value) 59 60 // A namespace for utilities used in SPIR-V Tools unit tests. 61 namespace spvtest { 62 63 class WordVector; 64 65 // Emits the given word vector to the given stream. 66 // This function can be used by the gtest value printer. 67 void PrintTo(const WordVector& words, ::std::ostream* os); 68 69 // A proxy class to allow us to easily write out vectors of SPIR-V words. 70 class WordVector { 71 public: 72 explicit WordVector(const std::vector<uint32_t>& val) : value_(val) {} 73 explicit WordVector(const spv_binary_t& binary) 74 : value_(binary.code, binary.code + binary.wordCount) {} 75 76 // Returns the underlying vector. 77 const std::vector<uint32_t>& value() const { return value_; } 78 79 // Returns the string representation of this word vector. 80 std::string str() const { 81 std::ostringstream os; 82 PrintTo(*this, &os); 83 return os.str(); 84 } 85 86 private: 87 const std::vector<uint32_t> value_; 88 }; 89 90 inline void PrintTo(const WordVector& words, ::std::ostream* os) { 91 size_t count = 0; 92 const auto saved_flags = os->flags(); 93 const auto saved_fill = os->fill(); 94 for (uint32_t value : words.value()) { 95 *os << "0x" << std::setw(8) << std::setfill('0') << std::hex << value 96 << " "; 97 if (count++ % 8 == 7) { 98 *os << std::endl; 99 } 100 } 101 os->flags(saved_flags); 102 os->fill(saved_fill); 103 } 104 105 // Returns a vector of words representing a single instruction with the 106 // given opcode and operand words as a vector. 107 inline std::vector<uint32_t> MakeInstruction( 108 SpvOp opcode, const std::vector<uint32_t>& args) { 109 std::vector<uint32_t> result{ 110 spvOpcodeMake(uint16_t(args.size() + 1), opcode)}; 111 result.insert(result.end(), args.begin(), args.end()); 112 return result; 113 } 114 115 // Returns a vector of words representing a single instruction with the 116 // given opcode and whose operands are the concatenation of the two given 117 // argument lists. 118 inline std::vector<uint32_t> MakeInstruction( 119 SpvOp opcode, std::vector<uint32_t> args, 120 const std::vector<uint32_t>& extra_args) { 121 args.insert(args.end(), extra_args.begin(), extra_args.end()); 122 return MakeInstruction(opcode, args); 123 } 124 125 // Returns the vector of words representing the concatenation 126 // of all input vectors. 127 inline std::vector<uint32_t> Concatenate( 128 const std::vector<std::vector<uint32_t>>& instructions) { 129 std::vector<uint32_t> result; 130 for (const auto& instruction : instructions) { 131 result.insert(result.end(), instruction.begin(), instruction.end()); 132 } 133 return result; 134 } 135 136 // Encodes a string as a sequence of words, using the SPIR-V encoding. 137 inline std::vector<uint32_t> MakeVector(std::string input) { 138 std::vector<uint32_t> result; 139 uint32_t word = 0; 140 size_t num_bytes = input.size(); 141 // SPIR-V strings are null-terminated. The byte_index == num_bytes 142 // case is used to push the terminating null byte. 143 for (size_t byte_index = 0; byte_index <= num_bytes; byte_index++) { 144 const auto new_byte = 145 (byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0)); 146 word |= (new_byte << (8 * (byte_index % sizeof(uint32_t)))); 147 if (3 == (byte_index % sizeof(uint32_t))) { 148 result.push_back(word); 149 word = 0; 150 } 151 } 152 // Emit a trailing partial word. 153 if ((num_bytes + 1) % sizeof(uint32_t)) { 154 result.push_back(word); 155 } 156 return result; 157 } 158 159 // A type for easily creating spv_text_t values, with an implicit conversion to 160 // spv_text. 161 struct AutoText { 162 explicit AutoText(const std::string& value) 163 : str(value), text({str.data(), str.size()}) {} 164 operator spv_text() { return &text; } 165 std::string str; 166 spv_text_t text; 167 }; 168 169 // An example case for an enumerated value, optionally with operands. 170 template <typename E> 171 class EnumCase { 172 public: 173 EnumCase() = default; // Required by ::testing::Combine(). 174 EnumCase(E val, std::string enum_name, std::vector<uint32_t> ops = {}) 175 : enum_value_(val), name_(enum_name), operands_(ops) {} 176 // Returns the enum value as a uint32_t. 177 uint32_t value() const { return static_cast<uint32_t>(enum_value_); } 178 // Returns the name of the enumerant. 179 const std::string& name() const { return name_; } 180 // Returns a reference to the operands. 181 const std::vector<uint32_t>& operands() const { return operands_; } 182 183 private: 184 E enum_value_; 185 std::string name_; 186 std::vector<uint32_t> operands_; 187 }; 188 189 // Returns a string with num_4_byte_chars Unicode characters, 190 // each of which has a 4-byte UTF-8 encoding. 191 inline std::string MakeLongUTF8String(size_t num_4_byte_chars) { 192 // An example of a longest valid UTF-8 character. 193 // Be explicit about the character type because Microsoft compilers can 194 // otherwise interpret the character string as being over wide (16-bit) 195 // characters. Ideally, we would just use a C++11 UTF-8 string literal, 196 // but we want to support older Microsoft compilers. 197 const std::basic_string<char> earth_africa("\xF0\x9F\x8C\x8D"); 198 EXPECT_EQ(4u, earth_africa.size()); 199 200 std::string result; 201 result.reserve(num_4_byte_chars * 4); 202 for (size_t i = 0; i < num_4_byte_chars; i++) { 203 result += earth_africa; 204 } 205 EXPECT_EQ(4 * num_4_byte_chars, result.size()); 206 return result; 207 } 208 209 // Returns a vector of all valid target environment enums. 210 inline std::vector<spv_target_env> AllTargetEnvironments() { 211 return { 212 SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, 213 SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_1_2, 214 SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_0, 215 SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_EMBEDDED_2_1, 216 SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_EMBEDDED_2_2, 217 SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, 218 SPV_ENV_OPENGL_4_1, SPV_ENV_OPENGL_4_2, 219 SPV_ENV_OPENGL_4_3, SPV_ENV_OPENGL_4_5, 220 SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, 221 SPV_ENV_VULKAN_1_1, SPV_ENV_WEBGPU_0, 222 }; 223 } 224 225 // Returns the capabilities in a CapabilitySet as an ordered vector. 226 inline std::vector<SpvCapability> ElementsIn( 227 const spvtools::CapabilitySet& capabilities) { 228 std::vector<SpvCapability> result; 229 capabilities.ForEach([&result](SpvCapability c) { result.push_back(c); }); 230 return result; 231 } 232 233 } // namespace spvtest 234 #endif // TEST_UNIT_SPIRV_H_ 235