218 lines
7.4 KiB
C++
218 lines
7.4 KiB
C++
// 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
|