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