1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/asmjs/asm-types.h"
6
7 #include <cinttypes>
8
9 #include "src/v8.h"
10
11 namespace v8 {
12 namespace internal {
13 namespace wasm {
14
AsCallableType()15 AsmCallableType* AsmType::AsCallableType() {
16 if (AsValueType() != nullptr) {
17 return nullptr;
18 }
19
20 return reinterpret_cast<AsmCallableType*>(this);
21 }
22
Name()23 std::string AsmType::Name() {
24 AsmValueType* avt = this->AsValueType();
25 if (avt != nullptr) {
26 switch (avt->Bitset()) {
27 #define RETURN_TYPE_NAME(CamelName, string_name, number, parent_types) \
28 case AsmValueType::kAsm##CamelName: \
29 return string_name;
30 FOR_EACH_ASM_VALUE_TYPE_LIST(RETURN_TYPE_NAME)
31 #undef RETURN_TYPE_NAME
32 default:
33 UNREACHABLE();
34 }
35 }
36
37 return this->AsCallableType()->Name();
38 }
39
IsExactly(AsmType * that)40 bool AsmType::IsExactly(AsmType* that) {
41 // TODO(jpp): maybe this can become this == that.
42 AsmValueType* avt = this->AsValueType();
43 if (avt != nullptr) {
44 AsmValueType* tavt = that->AsValueType();
45 if (tavt == nullptr) {
46 return false;
47 }
48 return avt->Bitset() == tavt->Bitset();
49 }
50
51 // TODO(jpp): is it useful to allow non-value types to be tested with
52 // IsExactly?
53 return that == this;
54 }
55
IsA(AsmType * that)56 bool AsmType::IsA(AsmType* that) {
57 // IsA is used for querying inheritance relationships. Therefore it is only
58 // meaningful for basic types.
59 if (auto* avt = this->AsValueType()) {
60 if (auto* tavt = that->AsValueType()) {
61 return (avt->Bitset() & tavt->Bitset()) == tavt->Bitset();
62 }
63 return false;
64 }
65
66 if (auto* as_callable = this->AsCallableType()) {
67 return as_callable->IsA(that);
68 }
69
70 UNREACHABLE();
71 return that == this;
72 }
73
ElementSizeInBytes()74 int32_t AsmType::ElementSizeInBytes() {
75 auto* value = AsValueType();
76 if (value == nullptr) {
77 return AsmType::kNotHeapType;
78 }
79 switch (value->Bitset()) {
80 case AsmValueType::kAsmInt8Array:
81 case AsmValueType::kAsmUint8Array:
82 return 1;
83 case AsmValueType::kAsmInt16Array:
84 case AsmValueType::kAsmUint16Array:
85 return 2;
86 case AsmValueType::kAsmInt32Array:
87 case AsmValueType::kAsmUint32Array:
88 case AsmValueType::kAsmFloat32Array:
89 return 4;
90 case AsmValueType::kAsmFloat64Array:
91 return 8;
92 default:
93 return AsmType::kNotHeapType;
94 }
95 }
96
LoadType()97 AsmType* AsmType::LoadType() {
98 auto* value = AsValueType();
99 if (value == nullptr) {
100 return AsmType::None();
101 }
102 switch (value->Bitset()) {
103 case AsmValueType::kAsmInt8Array:
104 case AsmValueType::kAsmUint8Array:
105 case AsmValueType::kAsmInt16Array:
106 case AsmValueType::kAsmUint16Array:
107 case AsmValueType::kAsmInt32Array:
108 case AsmValueType::kAsmUint32Array:
109 return AsmType::Intish();
110 case AsmValueType::kAsmFloat32Array:
111 return AsmType::FloatQ();
112 case AsmValueType::kAsmFloat64Array:
113 return AsmType::DoubleQ();
114 default:
115 return AsmType::None();
116 }
117 }
118
StoreType()119 AsmType* AsmType::StoreType() {
120 auto* value = AsValueType();
121 if (value == nullptr) {
122 return AsmType::None();
123 }
124 switch (value->Bitset()) {
125 case AsmValueType::kAsmInt8Array:
126 case AsmValueType::kAsmUint8Array:
127 case AsmValueType::kAsmInt16Array:
128 case AsmValueType::kAsmUint16Array:
129 case AsmValueType::kAsmInt32Array:
130 case AsmValueType::kAsmUint32Array:
131 return AsmType::Intish();
132 case AsmValueType::kAsmFloat32Array:
133 return AsmType::FloatishDoubleQ();
134 case AsmValueType::kAsmFloat64Array:
135 return AsmType::FloatQDoubleQ();
136 default:
137 return AsmType::None();
138 }
139 }
140
IsA(AsmType * other)141 bool AsmCallableType::IsA(AsmType* other) {
142 return other->AsCallableType() == this;
143 }
144
Name()145 std::string AsmFunctionType::Name() {
146 std::string ret;
147 ret += "(";
148 for (size_t ii = 0; ii < args_.size(); ++ii) {
149 ret += args_[ii]->Name();
150 if (ii != args_.size() - 1) {
151 ret += ", ";
152 }
153 }
154 ret += ") -> ";
155 ret += return_type_->Name();
156 return ret;
157 }
158
159 namespace {
160 class AsmFroundType final : public AsmCallableType {
161 public:
162 friend AsmType;
163
AsmFroundType()164 AsmFroundType() : AsmCallableType() {}
165
166 bool CanBeInvokedWith(AsmType* return_type,
167 const ZoneVector<AsmType*>& args) override;
168
Name()169 std::string Name() override { return "fround"; }
170 };
171 } // namespace
172
FroundType(Zone * zone)173 AsmType* AsmType::FroundType(Zone* zone) {
174 auto* Fround = new (zone) AsmFroundType();
175 return reinterpret_cast<AsmType*>(Fround);
176 }
177
CanBeInvokedWith(AsmType * return_type,const ZoneVector<AsmType * > & args)178 bool AsmFroundType::CanBeInvokedWith(AsmType* return_type,
179 const ZoneVector<AsmType*>& args) {
180 if (args.size() != 1) {
181 return false;
182 }
183
184 auto* arg = args[0];
185 if (!arg->IsA(AsmType::Floatish()) && !arg->IsA(AsmType::DoubleQ()) &&
186 !arg->IsA(AsmType::Signed()) && !arg->IsA(AsmType::Unsigned())) {
187 return false;
188 }
189
190 return true;
191 }
192
193 namespace {
194 class AsmMinMaxType final : public AsmCallableType {
195 private:
196 friend AsmType;
197
AsmMinMaxType(AsmType * dest,AsmType * src)198 AsmMinMaxType(AsmType* dest, AsmType* src)
199 : AsmCallableType(), return_type_(dest), arg_(src) {}
200
CanBeInvokedWith(AsmType * return_type,const ZoneVector<AsmType * > & args)201 bool CanBeInvokedWith(AsmType* return_type,
202 const ZoneVector<AsmType*>& args) override {
203 if (!return_type_->IsExactly(return_type)) {
204 return false;
205 }
206
207 if (args.size() < 2) {
208 return false;
209 }
210
211 for (size_t ii = 0; ii < args.size(); ++ii) {
212 if (!args[ii]->IsA(arg_)) {
213 return false;
214 }
215 }
216
217 return true;
218 }
219
Name()220 std::string Name() override {
221 return "(" + arg_->Name() + ", " + arg_->Name() + "...) -> " +
222 return_type_->Name();
223 }
224
225 AsmType* return_type_;
226 AsmType* arg_;
227 };
228 } // namespace
229
MinMaxType(Zone * zone,AsmType * dest,AsmType * src)230 AsmType* AsmType::MinMaxType(Zone* zone, AsmType* dest, AsmType* src) {
231 DCHECK(dest->AsValueType() != nullptr);
232 DCHECK(src->AsValueType() != nullptr);
233 auto* MinMax = new (zone) AsmMinMaxType(dest, src);
234 return reinterpret_cast<AsmType*>(MinMax);
235 }
236
CanBeInvokedWith(AsmType * return_type,const ZoneVector<AsmType * > & args)237 bool AsmFFIType::CanBeInvokedWith(AsmType* return_type,
238 const ZoneVector<AsmType*>& args) {
239 if (return_type->IsExactly(AsmType::Float())) {
240 return false;
241 }
242
243 for (size_t ii = 0; ii < args.size(); ++ii) {
244 if (!args[ii]->IsA(AsmType::Extern())) {
245 return false;
246 }
247 }
248
249 return true;
250 }
251
IsA(AsmType * other)252 bool AsmFunctionType::IsA(AsmType* other) {
253 auto* that = other->AsFunctionType();
254 if (that == nullptr) {
255 return false;
256 }
257 if (!return_type_->IsExactly(that->return_type_)) {
258 return false;
259 }
260
261 if (args_.size() != that->args_.size()) {
262 return false;
263 }
264
265 for (size_t ii = 0; ii < args_.size(); ++ii) {
266 if (!args_[ii]->IsExactly(that->args_[ii])) {
267 return false;
268 }
269 }
270
271 return true;
272 }
273
CanBeInvokedWith(AsmType * return_type,const ZoneVector<AsmType * > & args)274 bool AsmFunctionType::CanBeInvokedWith(AsmType* return_type,
275 const ZoneVector<AsmType*>& args) {
276 if (!return_type_->IsExactly(return_type)) {
277 return false;
278 }
279
280 if (args_.size() != args.size()) {
281 return false;
282 }
283
284 for (size_t ii = 0; ii < args_.size(); ++ii) {
285 if (!args[ii]->IsA(args_[ii])) {
286 return false;
287 }
288 }
289
290 return true;
291 }
292
Name()293 std::string AsmOverloadedFunctionType::Name() {
294 std::string ret;
295
296 for (size_t ii = 0; ii < overloads_.size(); ++ii) {
297 if (ii != 0) {
298 ret += " /\\ ";
299 }
300 ret += overloads_[ii]->Name();
301 }
302
303 return ret;
304 }
305
CanBeInvokedWith(AsmType * return_type,const ZoneVector<AsmType * > & args)306 bool AsmOverloadedFunctionType::CanBeInvokedWith(
307 AsmType* return_type, const ZoneVector<AsmType*>& args) {
308 for (size_t ii = 0; ii < overloads_.size(); ++ii) {
309 if (overloads_[ii]->AsCallableType()->CanBeInvokedWith(return_type, args)) {
310 return true;
311 }
312 }
313
314 return false;
315 }
316
AddOverload(AsmType * overload)317 void AsmOverloadedFunctionType::AddOverload(AsmType* overload) {
318 DCHECK(overload->AsCallableType() != nullptr);
319 overloads_.push_back(overload);
320 }
321
AsmFunctionTableType(size_t length,AsmType * signature)322 AsmFunctionTableType::AsmFunctionTableType(size_t length, AsmType* signature)
323 : length_(length), signature_(signature) {
324 DCHECK(signature_ != nullptr);
325 DCHECK(signature_->AsFunctionType() != nullptr);
326 }
327
328 namespace {
329 // ToString is used for reporting function tables' names. It converts its
330 // argument to uint32_t because asm.js integers are 32-bits, thus effectively
331 // limiting the max function table's length.
ToString(size_t s)332 std::string ToString(size_t s) {
333 auto u32 = static_cast<uint32_t>(s);
334 // 16 bytes is more than enough to represent a 32-bit integer as a base 10
335 // string.
336 char digits[16];
337 int length = base::OS::SNPrintF(digits, arraysize(digits), "%" PRIu32, u32);
338 DCHECK_NE(length, -1);
339 return std::string(digits, length);
340 }
341 } // namespace
342
Name()343 std::string AsmFunctionTableType::Name() {
344 return "(" + signature_->Name() + ")[" + ToString(length_) + "]";
345 }
346
CanBeInvokedWith(AsmType * return_type,const ZoneVector<AsmType * > & args)347 bool AsmFunctionTableType::CanBeInvokedWith(AsmType* return_type,
348 const ZoneVector<AsmType*>& args) {
349 return signature_->AsCallableType()->CanBeInvokedWith(return_type, args);
350 }
351
352 } // namespace wasm
353 } // namespace internal
354 } // namespace v8
355