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