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