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
27namespace spvtools {
28namespace utils {
29namespace {
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.
34class 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
57EncodeNumberStatus 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
134EncodeNumberStatus 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
192EncodeNumberStatus 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