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:
ErrorMsgStream(std::string * error_msg_sink)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   }
~ErrorMsgStream()40   ~ErrorMsgStream() {
41     if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str();
42   }
43   template <typename T>
operator <<(T val)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 
ParseAndEncodeIntegerNumber(const char * text,const NumberType & type,std::function<void (uint32_t)> emit,std::string * error_msg)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 
ParseAndEncodeFloatingPointNumber(const char * text,const NumberType & type,std::function<void (uint32_t)> emit,std::string * error_msg)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 
ParseAndEncodeNumber(const char * text,const NumberType & type,std::function<void (uint32_t)> emit,std::string * error_msg)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