// 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