Home | History | Annotate | Download | only in asmjs
      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 
     16 AsmCallableType* AsmType::AsCallableType() {
     17   if (AsValueType() != nullptr) {
     18     return nullptr;
     19   }
     20 
     21   return reinterpret_cast<AsmCallableType*>(this);
     22 }
     23 
     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 
     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 
     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 
     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 
     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 
    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 
    142 bool AsmCallableType::IsA(AsmType* other) {
    143   return other->AsCallableType() == this;
    144 }
    145 
    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 
    165   AsmFroundType() : AsmCallableType() {}
    166 
    167   bool CanBeInvokedWith(AsmType* return_type,
    168                         const ZoneVector<AsmType*>& args) override;
    169 
    170   std::string Name() override { return "fround"; }
    171 };
    172 }  // namespace
    173 
    174 AsmType* AsmType::FroundType(Zone* zone) {
    175   auto* Fround = new (zone) AsmFroundType();
    176   return reinterpret_cast<AsmType*>(Fround);
    177 }
    178 
    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 
    199   AsmMinMaxType(AsmType* dest, AsmType* src)
    200       : AsmCallableType(), return_type_(dest), arg_(src) {}
    201 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    318 void AsmOverloadedFunctionType::AddOverload(AsmType* overload) {
    319   DCHECK(overload->AsCallableType() != nullptr);
    320   overloads_.push_back(overload);
    321 }
    322 
    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.
    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 
    344 std::string AsmFunctionTableType::Name() {
    345   return "(" + signature_->Name() + ")[" + ToString(length_) + "]";
    346 }
    347 
    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