| 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 | #include "source/util/parse_number.h" |
| 16 | |
| 17 | #include <functional> |
| 18 | #include <iomanip> |
| 19 | #include <memory> |
| 20 | #include <sstream> |
| 21 | #include <string> |
| 22 | #include <tuple> |
| 23 | |
| 24 | #include "source/util/hex_float.h" |
| 25 | #include "source/util/make_unique.h" |
| 26 | |
| 27 | namespace spvtools { |
| 28 | namespace utils { |
| 29 | namespace { |
| 30 | |
| 31 | // A helper class that temporarily stores error messages and dump the messages |
| 32 | // to a string which given as as pointer when it is destructed. If the given |
| 33 | // pointer is a nullptr, this class does not store error message. |
| 34 | class ErrorMsgStream { |
| 35 | public: |
| 36 | explicit ErrorMsgStream(std::string* error_msg_sink) |
| 37 | : error_msg_sink_(error_msg_sink) { |
| 38 | if (error_msg_sink_) stream_ = MakeUnique<std::ostringstream>(); |
| 39 | } |
| 40 | ~ErrorMsgStream() { |
| 41 | if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str(); |
| 42 | } |
| 43 | template <typename T> |
| 44 | ErrorMsgStream& operator<<(T val) { |
| 45 | if (stream_) *stream_ << val; |
| 46 | return *this; |
| 47 | } |
| 48 | |
| 49 | private: |
| 50 | std::unique_ptr<std::ostringstream> stream_; |
| 51 | // The destination string to which this class dump the error message when |
| 52 | // destructor is called. |
| 53 | std::string* error_msg_sink_; |
| 54 | }; |
| 55 | } // namespace |
| 56 | |
| 57 | EncodeNumberStatus ParseAndEncodeIntegerNumber( |
| 58 | const char* text, const NumberType& type, |
| 59 | std::function<void(uint32_t)> emit, std::string* error_msg) { |
| 60 | if (!text) { |
| 61 | ErrorMsgStream(error_msg) << "The given text is a nullptr" ; |
| 62 | return EncodeNumberStatus::kInvalidText; |
| 63 | } |
| 64 | |
| 65 | if (!IsIntegral(type)) { |
| 66 | ErrorMsgStream(error_msg) << "The expected type is not a integer type" ; |
| 67 | return EncodeNumberStatus::kInvalidUsage; |
| 68 | } |
| 69 | |
| 70 | const uint32_t bit_width = AssumedBitWidth(type); |
| 71 | |
| 72 | if (bit_width > 64) { |
| 73 | ErrorMsgStream(error_msg) |
| 74 | << "Unsupported " << bit_width << "-bit integer literals" ; |
| 75 | return EncodeNumberStatus::kUnsupported; |
| 76 | } |
| 77 | |
| 78 | // Either we are expecting anything or integer. |
| 79 | bool is_negative = text[0] == '-'; |
| 80 | bool can_be_signed = IsSigned(type); |
| 81 | |
| 82 | if (is_negative && !can_be_signed) { |
| 83 | ErrorMsgStream(error_msg) |
| 84 | << "Cannot put a negative number in an unsigned literal" ; |
| 85 | return EncodeNumberStatus::kInvalidUsage; |
| 86 | } |
| 87 | |
| 88 | const bool is_hex = text[0] == '0' && (text[1] == 'x' || text[1] == 'X'); |
| 89 | |
| 90 | uint64_t decoded_bits; |
| 91 | if (is_negative) { |
| 92 | int64_t decoded_signed = 0; |
| 93 | |
| 94 | if (!ParseNumber(text, &decoded_signed)) { |
| 95 | ErrorMsgStream(error_msg) << "Invalid signed integer literal: " << text; |
| 96 | return EncodeNumberStatus::kInvalidText; |
| 97 | } |
| 98 | |
| 99 | if (!CheckRangeAndIfHexThenSignExtend(decoded_signed, type, is_hex, |
| 100 | &decoded_signed)) { |
| 101 | ErrorMsgStream(error_msg) |
| 102 | << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase |
| 103 | << decoded_signed << " does not fit in a " << std::dec << bit_width |
| 104 | << "-bit " << (IsSigned(type) ? "signed" : "unsigned" ) << " integer" ; |
| 105 | return EncodeNumberStatus::kInvalidText; |
| 106 | } |
| 107 | decoded_bits = decoded_signed; |
| 108 | } else { |
| 109 | // There's no leading minus sign, so parse it as an unsigned integer. |
| 110 | if (!ParseNumber(text, &decoded_bits)) { |
| 111 | ErrorMsgStream(error_msg) << "Invalid unsigned integer literal: " << text; |
| 112 | return EncodeNumberStatus::kInvalidText; |
| 113 | } |
| 114 | if (!CheckRangeAndIfHexThenSignExtend(decoded_bits, type, is_hex, |
| 115 | &decoded_bits)) { |
| 116 | ErrorMsgStream(error_msg) |
| 117 | << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase |
| 118 | << decoded_bits << " does not fit in a " << std::dec << bit_width |
| 119 | << "-bit " << (IsSigned(type) ? "signed" : "unsigned" ) << " integer" ; |
| 120 | return EncodeNumberStatus::kInvalidText; |
| 121 | } |
| 122 | } |
| 123 | if (bit_width > 32) { |
| 124 | uint32_t low = uint32_t(0x00000000ffffffff & decoded_bits); |
| 125 | uint32_t high = uint32_t((0xffffffff00000000 & decoded_bits) >> 32); |
| 126 | emit(low); |
| 127 | emit(high); |
| 128 | } else { |
| 129 | emit(uint32_t(decoded_bits)); |
| 130 | } |
| 131 | return EncodeNumberStatus::kSuccess; |
| 132 | } |
| 133 | |
| 134 | EncodeNumberStatus ParseAndEncodeFloatingPointNumber( |
| 135 | const char* text, const NumberType& type, |
| 136 | std::function<void(uint32_t)> emit, std::string* error_msg) { |
| 137 | if (!text) { |
| 138 | ErrorMsgStream(error_msg) << "The given text is a nullptr" ; |
| 139 | return EncodeNumberStatus::kInvalidText; |
| 140 | } |
| 141 | |
| 142 | if (!IsFloating(type)) { |
| 143 | ErrorMsgStream(error_msg) << "The expected type is not a float type" ; |
| 144 | return EncodeNumberStatus::kInvalidUsage; |
| 145 | } |
| 146 | |
| 147 | const auto bit_width = AssumedBitWidth(type); |
| 148 | switch (bit_width) { |
| 149 | case 16: { |
| 150 | HexFloat<FloatProxy<Float16>> hVal(0); |
| 151 | if (!ParseNumber(text, &hVal)) { |
| 152 | ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text; |
| 153 | return EncodeNumberStatus::kInvalidText; |
| 154 | } |
| 155 | // getAsFloat will return the Float16 value, and get_value |
| 156 | // will return a uint16_t representing the bits of the float. |
| 157 | // The encoding is therefore correct from the perspective of the SPIR-V |
| 158 | // spec since the top 16 bits will be 0. |
| 159 | emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value())); |
| 160 | return EncodeNumberStatus::kSuccess; |
| 161 | } break; |
| 162 | case 32: { |
| 163 | HexFloat<FloatProxy<float>> fVal(0.0f); |
| 164 | if (!ParseNumber(text, &fVal)) { |
| 165 | ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text; |
| 166 | return EncodeNumberStatus::kInvalidText; |
| 167 | } |
| 168 | emit(BitwiseCast<uint32_t>(fVal)); |
| 169 | return EncodeNumberStatus::kSuccess; |
| 170 | } break; |
| 171 | case 64: { |
| 172 | HexFloat<FloatProxy<double>> dVal(0.0); |
| 173 | if (!ParseNumber(text, &dVal)) { |
| 174 | ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text; |
| 175 | return EncodeNumberStatus::kInvalidText; |
| 176 | } |
| 177 | uint64_t decoded_val = BitwiseCast<uint64_t>(dVal); |
| 178 | uint32_t low = uint32_t(0x00000000ffffffff & decoded_val); |
| 179 | uint32_t high = uint32_t((0xffffffff00000000 & decoded_val) >> 32); |
| 180 | emit(low); |
| 181 | emit(high); |
| 182 | return EncodeNumberStatus::kSuccess; |
| 183 | } break; |
| 184 | default: |
| 185 | break; |
| 186 | } |
| 187 | ErrorMsgStream(error_msg) |
| 188 | << "Unsupported " << bit_width << "-bit float literals" ; |
| 189 | return EncodeNumberStatus::kUnsupported; |
| 190 | } |
| 191 | |
| 192 | EncodeNumberStatus ParseAndEncodeNumber(const char* text, |
| 193 | const NumberType& type, |
| 194 | std::function<void(uint32_t)> emit, |
| 195 | std::string* error_msg) { |
| 196 | if (!text) { |
| 197 | ErrorMsgStream(error_msg) << "The given text is a nullptr" ; |
| 198 | return EncodeNumberStatus::kInvalidText; |
| 199 | } |
| 200 | |
| 201 | if (IsUnknown(type)) { |
| 202 | ErrorMsgStream(error_msg) |
| 203 | << "The expected type is not a integer or float type" ; |
| 204 | return EncodeNumberStatus::kInvalidUsage; |
| 205 | } |
| 206 | |
| 207 | // If we explicitly expect a floating-point number, we should handle that |
| 208 | // first. |
| 209 | if (IsFloating(type)) { |
| 210 | return ParseAndEncodeFloatingPointNumber(text, type, emit, error_msg); |
| 211 | } |
| 212 | |
| 213 | return ParseAndEncodeIntegerNumber(text, type, emit, error_msg); |
| 214 | } |
| 215 | |
| 216 | } // namespace utils |
| 217 | } // namespace spvtools |
| 218 | |