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