Home | History | Annotate | Download | only in test
      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