|  | // Copyright (c) 2016 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. | 
|  |  | 
|  | #include "source/util/parse_number.h" | 
|  |  | 
|  | #include <functional> | 
|  | #include <iomanip> | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  | #include <tuple> | 
|  |  | 
|  | #include "source/util/hex_float.h" | 
|  | #include "source/util/make_unique.h" | 
|  |  | 
|  | namespace spvtools { | 
|  | namespace utils { | 
|  | namespace { | 
|  |  | 
|  | // A helper class that temporarily stores error messages and dump the messages | 
|  | // to a string which given as as pointer when it is destructed. If the given | 
|  | // pointer is a nullptr, this class does not store error message. | 
|  | class ErrorMsgStream { | 
|  | public: | 
|  | explicit ErrorMsgStream(std::string* error_msg_sink) | 
|  | : error_msg_sink_(error_msg_sink) { | 
|  | if (error_msg_sink_) stream_ = MakeUnique<std::ostringstream>(); | 
|  | } | 
|  | ~ErrorMsgStream() { | 
|  | if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str(); | 
|  | } | 
|  | template <typename T> | 
|  | ErrorMsgStream& operator<<(T val) { | 
|  | if (stream_) *stream_ << val; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<std::ostringstream> stream_; | 
|  | // The destination string to which this class dump the error message when | 
|  | // destructor is called. | 
|  | std::string* error_msg_sink_; | 
|  | }; | 
|  | }  // namespace | 
|  |  | 
|  | EncodeNumberStatus ParseAndEncodeIntegerNumber( | 
|  | const char* text, const NumberType& type, | 
|  | std::function<void(uint32_t)> emit, std::string* error_msg) { | 
|  | if (!text) { | 
|  | ErrorMsgStream(error_msg) << "The given text is a nullptr"; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  |  | 
|  | if (!IsIntegral(type)) { | 
|  | ErrorMsgStream(error_msg) << "The expected type is not a integer type"; | 
|  | return EncodeNumberStatus::kInvalidUsage; | 
|  | } | 
|  |  | 
|  | const uint32_t bit_width = AssumedBitWidth(type); | 
|  |  | 
|  | if (bit_width > 64) { | 
|  | ErrorMsgStream(error_msg) | 
|  | << "Unsupported " << bit_width << "-bit integer literals"; | 
|  | return EncodeNumberStatus::kUnsupported; | 
|  | } | 
|  |  | 
|  | // Either we are expecting anything or integer. | 
|  | bool is_negative = text[0] == '-'; | 
|  | bool can_be_signed = IsSigned(type); | 
|  |  | 
|  | if (is_negative && !can_be_signed) { | 
|  | ErrorMsgStream(error_msg) | 
|  | << "Cannot put a negative number in an unsigned literal"; | 
|  | return EncodeNumberStatus::kInvalidUsage; | 
|  | } | 
|  |  | 
|  | const bool is_hex = text[0] == '0' && (text[1] == 'x' || text[1] == 'X'); | 
|  |  | 
|  | uint64_t decoded_bits; | 
|  | if (is_negative) { | 
|  | int64_t decoded_signed = 0; | 
|  |  | 
|  | if (!ParseNumber(text, &decoded_signed)) { | 
|  | ErrorMsgStream(error_msg) << "Invalid signed integer literal: " << text; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  |  | 
|  | if (!CheckRangeAndIfHexThenSignExtend(decoded_signed, type, is_hex, | 
|  | &decoded_signed)) { | 
|  | ErrorMsgStream(error_msg) | 
|  | << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase | 
|  | << decoded_signed << " does not fit in a " << std::dec << bit_width | 
|  | << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  | decoded_bits = decoded_signed; | 
|  | } else { | 
|  | // There's no leading minus sign, so parse it as an unsigned integer. | 
|  | if (!ParseNumber(text, &decoded_bits)) { | 
|  | ErrorMsgStream(error_msg) << "Invalid unsigned integer literal: " << text; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  | if (!CheckRangeAndIfHexThenSignExtend(decoded_bits, type, is_hex, | 
|  | &decoded_bits)) { | 
|  | ErrorMsgStream(error_msg) | 
|  | << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase | 
|  | << decoded_bits << " does not fit in a " << std::dec << bit_width | 
|  | << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  | } | 
|  | if (bit_width > 32) { | 
|  | uint32_t low = uint32_t(0x00000000ffffffff & decoded_bits); | 
|  | uint32_t high = uint32_t((0xffffffff00000000 & decoded_bits) >> 32); | 
|  | emit(low); | 
|  | emit(high); | 
|  | } else { | 
|  | emit(uint32_t(decoded_bits)); | 
|  | } | 
|  | return EncodeNumberStatus::kSuccess; | 
|  | } | 
|  |  | 
|  | EncodeNumberStatus ParseAndEncodeFloatingPointNumber( | 
|  | const char* text, const NumberType& type, | 
|  | std::function<void(uint32_t)> emit, std::string* error_msg) { | 
|  | if (!text) { | 
|  | ErrorMsgStream(error_msg) << "The given text is a nullptr"; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  |  | 
|  | if (!IsFloating(type)) { | 
|  | ErrorMsgStream(error_msg) << "The expected type is not a float type"; | 
|  | return EncodeNumberStatus::kInvalidUsage; | 
|  | } | 
|  |  | 
|  | const auto bit_width = AssumedBitWidth(type); | 
|  | switch (bit_width) { | 
|  | case 16: { | 
|  | HexFloat<FloatProxy<Float16>> hVal(0); | 
|  | if (!ParseNumber(text, &hVal)) { | 
|  | ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  | // getAsFloat will return the Float16 value, and get_value | 
|  | // will return a uint16_t representing the bits of the float. | 
|  | // The encoding is therefore correct from the perspective of the SPIR-V | 
|  | // spec since the top 16 bits will be 0. | 
|  | emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value())); | 
|  | return EncodeNumberStatus::kSuccess; | 
|  | } break; | 
|  | case 32: { | 
|  | HexFloat<FloatProxy<float>> fVal(0.0f); | 
|  | if (!ParseNumber(text, &fVal)) { | 
|  | ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  | emit(BitwiseCast<uint32_t>(fVal)); | 
|  | return EncodeNumberStatus::kSuccess; | 
|  | } break; | 
|  | case 64: { | 
|  | HexFloat<FloatProxy<double>> dVal(0.0); | 
|  | if (!ParseNumber(text, &dVal)) { | 
|  | ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  | uint64_t decoded_val = BitwiseCast<uint64_t>(dVal); | 
|  | uint32_t low = uint32_t(0x00000000ffffffff & decoded_val); | 
|  | uint32_t high = uint32_t((0xffffffff00000000 & decoded_val) >> 32); | 
|  | emit(low); | 
|  | emit(high); | 
|  | return EncodeNumberStatus::kSuccess; | 
|  | } break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | ErrorMsgStream(error_msg) | 
|  | << "Unsupported " << bit_width << "-bit float literals"; | 
|  | return EncodeNumberStatus::kUnsupported; | 
|  | } | 
|  |  | 
|  | EncodeNumberStatus ParseAndEncodeNumber(const char* text, | 
|  | const NumberType& type, | 
|  | std::function<void(uint32_t)> emit, | 
|  | std::string* error_msg) { | 
|  | if (!text) { | 
|  | ErrorMsgStream(error_msg) << "The given text is a nullptr"; | 
|  | return EncodeNumberStatus::kInvalidText; | 
|  | } | 
|  |  | 
|  | if (IsUnknown(type)) { | 
|  | ErrorMsgStream(error_msg) | 
|  | << "The expected type is not a integer or float type"; | 
|  | return EncodeNumberStatus::kInvalidUsage; | 
|  | } | 
|  |  | 
|  | // If we explicitly expect a floating-point number, we should handle that | 
|  | // first. | 
|  | if (IsFloating(type)) { | 
|  | return ParseAndEncodeFloatingPointNumber(text, type, emit, error_msg); | 
|  | } | 
|  |  | 
|  | return ParseAndEncodeIntegerNumber(text, type, emit, error_msg); | 
|  | } | 
|  |  | 
|  | }  // namespace utils | 
|  | }  // namespace spvtools |