WasmInitExpr.cpp (20395B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * 4 * Copyright 2021 Mozilla Foundation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #include "wasm/WasmInitExpr.h" 20 21 #include "mozilla/Maybe.h" 22 23 #include "js/Value.h" 24 25 #include "wasm/WasmGcObject.h" 26 #include "wasm/WasmInstance.h" 27 #include "wasm/WasmOpIter.h" 28 #include "wasm/WasmSerialize.h" 29 #include "wasm/WasmUtility.h" 30 #include "wasm/WasmValidate.h" 31 32 #include "wasm/WasmInstance-inl.h" 33 34 using namespace js; 35 using namespace js::wasm; 36 37 using mozilla::Maybe; 38 using mozilla::Nothing; 39 using mozilla::Some; 40 41 class MOZ_STACK_CLASS InitExprInterpreter { 42 public: 43 explicit InitExprInterpreter(JSContext* cx, 44 Handle<WasmInstanceObject*> instanceObj) 45 : features(FeatureArgs::build(cx, FeatureOptions())), 46 stack(cx), 47 instanceObj(cx, instanceObj), 48 types(instanceObj->instance().codeMeta().types) {} 49 50 bool evaluate(JSContext* cx, Decoder& d); 51 52 Val result() { 53 MOZ_ASSERT(stack.length() == 1); 54 return stack.popCopy(); 55 } 56 57 private: 58 FeatureArgs features; 59 RootedValVectorN<48> stack; 60 Rooted<WasmInstanceObject*> instanceObj; 61 SharedTypeContext types; 62 63 Instance& instance() { return instanceObj->instance(); } 64 65 [[nodiscard]] bool pushI32(int32_t c) { 66 return stack.append(Val(uint32_t(c))); 67 } 68 [[nodiscard]] bool pushI64(int64_t c) { 69 return stack.append(Val(uint64_t(c))); 70 } 71 [[nodiscard]] bool pushF32(float c) { return stack.append(Val(c)); } 72 [[nodiscard]] bool pushF64(double c) { return stack.append(Val(c)); } 73 [[nodiscard]] bool pushV128(V128 c) { return stack.append(Val(c)); } 74 [[nodiscard]] bool pushRef(ValType type, AnyRef ref) { 75 return stack.append(Val(type, ref)); 76 } 77 [[nodiscard]] bool pushFuncRef(RefType type, FuncRef ref) { 78 return stack.append(Val(type, ref)); 79 } 80 81 int32_t popI32() { 82 uint32_t result = stack.back().i32(); 83 stack.popBack(); 84 return int32_t(result); 85 } 86 int64_t popI64() { 87 uint64_t result = stack.back().i64(); 88 stack.popBack(); 89 return int64_t(result); 90 } 91 92 bool evalGlobalGet(JSContext* cx, uint32_t index) { 93 RootedVal val(cx); 94 instance().constantGlobalGet(index, &val); 95 return stack.append(val); 96 } 97 bool evalI32Const(int32_t c) { return pushI32(c); } 98 bool evalI64Const(int64_t c) { return pushI64(c); } 99 bool evalF32Const(float c) { return pushF32(c); } 100 bool evalF64Const(double c) { return pushF64(c); } 101 bool evalV128Const(V128 c) { return pushV128(c); } 102 bool evalRefFunc(JSContext* cx, uint32_t funcIndex) { 103 RootedFunction func(cx); 104 if (!instance().getExportedFunction(cx, funcIndex, &func)) { 105 return false; 106 } 107 const TypeDef& t = instance().codeMeta().getFuncTypeDef(funcIndex); 108 return pushFuncRef(RefType::fromTypeDef(&t, false), 109 FuncRef::fromJSFunction(func.get())); 110 } 111 bool evalRefNull(RefType type) { return pushRef(type, AnyRef::null()); } 112 bool evalI32Add() { 113 uint32_t b = popI32(); 114 uint32_t a = popI32(); 115 return pushI32(a + b); 116 } 117 bool evalI32Sub() { 118 uint32_t b = popI32(); 119 uint32_t a = popI32(); 120 return pushI32(a - b); 121 } 122 bool evalI32Mul() { 123 uint32_t b = popI32(); 124 uint32_t a = popI32(); 125 return pushI32(a * b); 126 } 127 bool evalI64Add() { 128 uint64_t b = popI64(); 129 uint64_t a = popI64(); 130 return pushI64(a + b); 131 } 132 bool evalI64Sub() { 133 uint64_t b = popI64(); 134 uint64_t a = popI64(); 135 return pushI64(a - b); 136 } 137 bool evalI64Mul() { 138 uint64_t b = popI64(); 139 uint64_t a = popI64(); 140 return pushI64(a * b); 141 } 142 bool evalStructNew(JSContext* cx, uint32_t typeIndex) { 143 const TypeDef& typeDef = instance().codeMeta().types->type(typeIndex); 144 const StructType& structType = typeDef.structType(); 145 146 Rooted<WasmStructObject*> structObj( 147 cx, instance().constantStructNewDefault(cx, typeIndex)); 148 if (!structObj) { 149 return false; 150 } 151 152 uint32_t numFields = structType.fields_.length(); 153 for (uint32_t forwardIndex = 0; forwardIndex < numFields; forwardIndex++) { 154 uint32_t reverseIndex = numFields - forwardIndex - 1; 155 const Val& val = stack.back(); 156 structObj->storeVal(val, reverseIndex); 157 stack.popBack(); 158 } 159 160 return pushRef(RefType::fromTypeDef(&typeDef, false), 161 AnyRef::fromJSObject(*structObj)); 162 } 163 164 bool evalStructNewDefault(JSContext* cx, uint32_t typeIndex) { 165 Rooted<WasmStructObject*> structObj( 166 cx, instance().constantStructNewDefault(cx, typeIndex)); 167 if (!structObj) { 168 return false; 169 } 170 171 const TypeDef& typeDef = instance().codeMeta().types->type(typeIndex); 172 return pushRef(RefType::fromTypeDef(&typeDef, false), 173 AnyRef::fromJSObject(*structObj)); 174 } 175 176 bool evalArrayNew(JSContext* cx, uint32_t typeIndex) { 177 uint32_t numElements = popI32(); 178 Rooted<WasmArrayObject*> arrayObj( 179 cx, instance().constantArrayNewDefault(cx, typeIndex, numElements)); 180 if (!arrayObj) { 181 return false; 182 } 183 184 const Val& val = stack.back(); 185 arrayObj->fillVal(val, 0, numElements); 186 stack.popBack(); 187 188 const TypeDef& typeDef = instance().codeMeta().types->type(typeIndex); 189 return pushRef(RefType::fromTypeDef(&typeDef, false), 190 AnyRef::fromJSObject(*arrayObj)); 191 } 192 193 bool evalArrayNewDefault(JSContext* cx, uint32_t typeIndex) { 194 uint32_t numElements = popI32(); 195 Rooted<WasmArrayObject*> arrayObj( 196 cx, instance().constantArrayNewDefault(cx, typeIndex, numElements)); 197 if (!arrayObj) { 198 return false; 199 } 200 201 const TypeDef& typeDef = instance().codeMeta().types->type(typeIndex); 202 return pushRef(RefType::fromTypeDef(&typeDef, false), 203 AnyRef::fromJSObject(*arrayObj)); 204 } 205 206 bool evalArrayNewFixed(JSContext* cx, uint32_t typeIndex, 207 uint32_t numElements) { 208 Rooted<WasmArrayObject*> arrayObj( 209 cx, instance().constantArrayNewDefault(cx, typeIndex, numElements)); 210 if (!arrayObj) { 211 return false; 212 } 213 214 for (uint32_t forwardIndex = 0; forwardIndex < numElements; 215 forwardIndex++) { 216 uint32_t reverseIndex = numElements - forwardIndex - 1; 217 const Val& val = stack.back(); 218 arrayObj->storeVal(val, reverseIndex); 219 stack.popBack(); 220 } 221 222 const TypeDef& typeDef = instance().codeMeta().types->type(typeIndex); 223 return pushRef(RefType::fromTypeDef(&typeDef, false), 224 AnyRef::fromJSObject(*arrayObj)); 225 } 226 227 bool evalI31New(JSContext* cx) { 228 uint32_t value = stack.back().i32(); 229 stack.popBack(); 230 return pushRef(RefType::i31().asNonNullable(), 231 AnyRef::fromUint32Truncate(value)); 232 } 233 234 bool evalAnyConvertExtern(JSContext* cx) { 235 AnyRef ref = stack.back().ref(); 236 stack.popBack(); 237 return pushRef(RefType::extern_(), ref); 238 } 239 240 bool evalExternConvertAny(JSContext* cx) { 241 AnyRef ref = stack.back().ref(); 242 stack.popBack(); 243 return pushRef(RefType::any(), ref); 244 } 245 }; 246 247 bool InitExprInterpreter::evaluate(JSContext* cx, Decoder& d) { 248 #define CHECK(c) \ 249 if (!(c)) return false; \ 250 break 251 252 while (true) { 253 OpBytes op; 254 if (!d.readOp(&op)) { 255 return false; 256 } 257 258 switch (op.b0) { 259 case uint16_t(Op::End): { 260 return true; 261 } 262 case uint16_t(Op::GlobalGet): { 263 uint32_t index; 264 if (!d.readGlobalIndex(&index)) { 265 return false; 266 } 267 CHECK(evalGlobalGet(cx, index)); 268 } 269 case uint16_t(Op::I32Const): { 270 int32_t c; 271 if (!d.readI32Const(&c)) { 272 return false; 273 } 274 CHECK(evalI32Const(c)); 275 } 276 case uint16_t(Op::I64Const): { 277 int64_t c; 278 if (!d.readI64Const(&c)) { 279 return false; 280 } 281 CHECK(evalI64Const(c)); 282 } 283 case uint16_t(Op::F32Const): { 284 float c; 285 if (!d.readF32Const(&c)) { 286 return false; 287 } 288 CHECK(evalF32Const(c)); 289 } 290 case uint16_t(Op::F64Const): { 291 double c; 292 if (!d.readF64Const(&c)) { 293 return false; 294 } 295 CHECK(evalF64Const(c)); 296 } 297 #ifdef ENABLE_WASM_SIMD 298 case uint16_t(Op::SimdPrefix): { 299 MOZ_RELEASE_ASSERT(op.b1 == uint32_t(SimdOp::V128Const)); 300 V128 c; 301 if (!d.readV128Const(&c)) { 302 return false; 303 } 304 CHECK(evalV128Const(c)); 305 } 306 #endif 307 case uint16_t(Op::RefFunc): { 308 uint32_t funcIndex; 309 if (!d.readFuncIndex(&funcIndex)) { 310 return false; 311 } 312 CHECK(evalRefFunc(cx, funcIndex)); 313 } 314 case uint16_t(Op::RefNull): { 315 RefType type; 316 if (!d.readRefNull(*types, features, &type)) { 317 return false; 318 } 319 CHECK(evalRefNull(type)); 320 } 321 case uint16_t(Op::I32Add): { 322 if (!d.readBinary()) { 323 return false; 324 } 325 CHECK(evalI32Add()); 326 } 327 case uint16_t(Op::I32Sub): { 328 if (!d.readBinary()) { 329 return false; 330 } 331 CHECK(evalI32Sub()); 332 } 333 case uint16_t(Op::I32Mul): { 334 if (!d.readBinary()) { 335 return false; 336 } 337 CHECK(evalI32Mul()); 338 } 339 case uint16_t(Op::I64Add): { 340 if (!d.readBinary()) { 341 return false; 342 } 343 CHECK(evalI64Add()); 344 } 345 case uint16_t(Op::I64Sub): { 346 if (!d.readBinary()) { 347 return false; 348 } 349 CHECK(evalI64Sub()); 350 } 351 case uint16_t(Op::I64Mul): { 352 if (!d.readBinary()) { 353 return false; 354 } 355 CHECK(evalI64Mul()); 356 } 357 case uint16_t(Op::GcPrefix): { 358 switch (op.b1) { 359 case uint32_t(GcOp::StructNew): { 360 uint32_t typeIndex; 361 if (!d.readTypeIndex(&typeIndex)) { 362 return false; 363 } 364 CHECK(evalStructNew(cx, typeIndex)); 365 } 366 case uint32_t(GcOp::StructNewDefault): { 367 uint32_t typeIndex; 368 if (!d.readTypeIndex(&typeIndex)) { 369 return false; 370 } 371 CHECK(evalStructNewDefault(cx, typeIndex)); 372 } 373 case uint32_t(GcOp::ArrayNew): { 374 uint32_t typeIndex; 375 if (!d.readTypeIndex(&typeIndex)) { 376 return false; 377 } 378 CHECK(evalArrayNew(cx, typeIndex)); 379 } 380 case uint32_t(GcOp::ArrayNewFixed): { 381 uint32_t typeIndex, len; 382 if (!d.readTypeIndex(&typeIndex)) { 383 return false; 384 } 385 if (!d.readVarU32(&len)) { 386 return false; 387 } 388 CHECK(evalArrayNewFixed(cx, typeIndex, len)); 389 } 390 case uint32_t(GcOp::ArrayNewDefault): { 391 uint32_t typeIndex; 392 if (!d.readTypeIndex(&typeIndex)) { 393 return false; 394 } 395 CHECK(evalArrayNewDefault(cx, typeIndex)); 396 } 397 case uint32_t(GcOp::RefI31): { 398 CHECK(evalI31New(cx)); 399 } 400 case uint32_t(GcOp::AnyConvertExtern): { 401 CHECK(evalAnyConvertExtern(cx)); 402 } 403 case uint32_t(GcOp::ExternConvertAny): { 404 CHECK(evalExternConvertAny(cx)); 405 } 406 default: { 407 MOZ_CRASH(); 408 } 409 } 410 break; 411 } 412 default: { 413 MOZ_CRASH(); 414 } 415 } 416 } 417 418 #undef CHECK 419 } 420 421 bool wasm::DecodeConstantExpression(Decoder& d, CodeMetadata* codeMeta, 422 ValType expected, Maybe<LitVal>* literal) { 423 ValTypeVector locals; 424 ValidatingOpIter iter(*codeMeta, d, locals, ValidatingOpIter::InitExpr); 425 426 if (!iter.startInitExpr(expected)) { 427 return false; 428 } 429 430 // Perform trivial constant recovery, this is done so that codegen may 431 // generate optimal code for global.get on immutable globals with simple 432 // initializers. 433 // 434 // We simply update the last seen literal value while validating an 435 // instruction with a literal value, and clear the literal value when 436 // validating an instruction with a dynamic value. The last value is the 437 // literal for this init expressions, if any. This is correct because there 438 // are no drops or control flow allowed in init expressions. 439 *literal = Nothing(); 440 441 while (true) { 442 OpBytes op; 443 if (!iter.readOp(&op)) { 444 return false; 445 } 446 447 Nothing nothing; 448 NothingVector nothings{}; 449 ResultType unusedType; 450 451 switch (op.b0) { 452 case uint16_t(Op::End): { 453 LabelKind kind; 454 if (!iter.readEnd(&kind, &unusedType, ¬hings, ¬hings)) { 455 return false; 456 } 457 MOZ_ASSERT(kind == LabelKind::Body); 458 iter.popEnd(); 459 if (iter.controlStackEmpty()) { 460 return iter.endInitExpr(); 461 } 462 break; 463 } 464 case uint16_t(Op::GlobalGet): { 465 uint32_t index; 466 if (!iter.readGetGlobal(&index)) { 467 return false; 468 } 469 *literal = Nothing(); 470 break; 471 } 472 case uint16_t(Op::I32Const): { 473 int32_t c; 474 if (!iter.readI32Const(&c)) { 475 return false; 476 } 477 *literal = Some(LitVal(uint32_t(c))); 478 break; 479 } 480 case uint16_t(Op::I64Const): { 481 int64_t c; 482 if (!iter.readI64Const(&c)) { 483 return false; 484 } 485 *literal = Some(LitVal(uint64_t(c))); 486 break; 487 } 488 case uint16_t(Op::F32Const): { 489 float c; 490 if (!iter.readF32Const(&c)) { 491 return false; 492 } 493 *literal = Some(LitVal(c)); 494 break; 495 } 496 case uint16_t(Op::F64Const): { 497 double c; 498 if (!iter.readF64Const(&c)) { 499 return false; 500 } 501 *literal = Some(LitVal(c)); 502 break; 503 } 504 #ifdef ENABLE_WASM_SIMD 505 case uint16_t(Op::SimdPrefix): { 506 if (!codeMeta->simdAvailable()) { 507 return d.fail("v128 not enabled"); 508 } 509 if (op.b1 != uint32_t(SimdOp::V128Const)) { 510 return iter.unrecognizedOpcode(&op); 511 } 512 V128 c; 513 if (!iter.readV128Const(&c)) { 514 return false; 515 } 516 *literal = Some(LitVal(c)); 517 break; 518 } 519 #endif 520 case uint16_t(Op::RefFunc): { 521 uint32_t funcIndex; 522 if (!iter.readRefFunc(&funcIndex)) { 523 return false; 524 } 525 codeMeta->funcs[funcIndex].declareFuncExported(/* eager */ false, 526 /* canRefFunc */ true); 527 *literal = Nothing(); 528 break; 529 } 530 case uint16_t(Op::RefNull): { 531 RefType type; 532 if (!iter.readRefNull(&type)) { 533 return false; 534 } 535 *literal = Some(LitVal(ValType(type))); 536 break; 537 } 538 case uint16_t(Op::I32Add): 539 case uint16_t(Op::I32Sub): 540 case uint16_t(Op::I32Mul): { 541 if (!iter.readBinary(ValType::I32, ¬hing, ¬hing)) { 542 return false; 543 } 544 *literal = Nothing(); 545 break; 546 } 547 case uint16_t(Op::I64Add): 548 case uint16_t(Op::I64Sub): 549 case uint16_t(Op::I64Mul): { 550 if (!iter.readBinary(ValType::I64, ¬hing, ¬hing)) { 551 return false; 552 } 553 *literal = Nothing(); 554 break; 555 } 556 case uint16_t(Op::GcPrefix): { 557 switch (op.b1) { 558 case uint32_t(GcOp::StructNew): { 559 uint32_t typeIndex; 560 if (!iter.readStructNew(&typeIndex, ¬hings)) { 561 return false; 562 } 563 break; 564 } 565 case uint32_t(GcOp::StructNewDefault): { 566 uint32_t typeIndex; 567 if (!iter.readStructNewDefault(&typeIndex)) { 568 return false; 569 } 570 break; 571 } 572 case uint32_t(GcOp::ArrayNew): { 573 uint32_t typeIndex; 574 if (!iter.readArrayNew(&typeIndex, ¬hing, ¬hing)) { 575 return false; 576 } 577 break; 578 } 579 case uint32_t(GcOp::ArrayNewFixed): { 580 uint32_t typeIndex, len; 581 if (!iter.readArrayNewFixed(&typeIndex, &len, ¬hings)) { 582 return false; 583 } 584 break; 585 } 586 case uint32_t(GcOp::ArrayNewDefault): { 587 uint32_t typeIndex; 588 if (!iter.readArrayNewDefault(&typeIndex, ¬hing)) { 589 return false; 590 } 591 break; 592 } 593 case uint32_t(GcOp::RefI31): { 594 Nothing value; 595 if (!iter.readConversion(ValType::I32, 596 ValType(RefType::i31().asNonNullable()), 597 &value)) { 598 return false; 599 } 600 break; 601 } 602 case uint32_t(GcOp::AnyConvertExtern): { 603 Nothing value; 604 if (!iter.readRefConversion(RefType::extern_(), RefType::any(), 605 &value)) { 606 return false; 607 } 608 break; 609 } 610 case uint32_t(GcOp::ExternConvertAny): { 611 Nothing value; 612 if (!iter.readRefConversion(RefType::any(), RefType::extern_(), 613 &value)) { 614 return false; 615 } 616 break; 617 } 618 default: { 619 return iter.unrecognizedOpcode(&op); 620 } 621 } 622 *literal = Nothing(); 623 break; 624 } 625 default: { 626 return iter.unrecognizedOpcode(&op); 627 } 628 } 629 } 630 } 631 632 bool InitExpr::decodeAndValidate(Decoder& d, CodeMetadata* codeMeta, 633 ValType expected, InitExpr* expr) { 634 Maybe<LitVal> literal = Nothing(); 635 const uint8_t* exprStart = d.currentPosition(); 636 if (!DecodeConstantExpression(d, codeMeta, expected, &literal)) { 637 return false; 638 } 639 const uint8_t* exprEnd = d.currentPosition(); 640 641 if (!expr->bytecode_.append(exprStart, exprEnd)) { 642 return false; 643 } 644 645 MOZ_ASSERT(expr->kind_ == InitExprKind::None); 646 expr->type_ = expected; 647 648 if (literal) { 649 literal->unsafeSetType(expected); 650 expr->kind_ = InitExprKind::Literal; 651 expr->literal_ = *literal; 652 return true; 653 } 654 655 expr->kind_ = InitExprKind::Variable; 656 return true; 657 } 658 659 /* static */ bool InitExpr::decodeAndEvaluate( 660 JSContext* cx, Handle<WasmInstanceObject*> instanceObj, Decoder& d, 661 ValType expectedType, MutableHandleVal result) { 662 InitExprInterpreter interp(cx, instanceObj); 663 if (!interp.evaluate(cx, d)) { 664 return false; 665 } 666 667 Val interpResult = interp.result(); 668 // The interpreter evaluation stack does not track the precise type of values. 669 // Users of the result expect the precise type though, so we need to overwrite 670 // it with the one we validated with. 671 interpResult.unsafeSetType(expectedType); 672 result.set(interpResult); 673 return true; 674 } 675 676 bool InitExpr::evaluate(JSContext* cx, Handle<WasmInstanceObject*> instanceObj, 677 MutableHandleVal result) const { 678 MOZ_ASSERT(kind_ != InitExprKind::None); 679 680 if (isLiteral()) { 681 result.set(Val(literal())); 682 return true; 683 } 684 685 UniqueChars error; 686 Decoder d(bytecode_.begin(), bytecode_.end(), 0, &error); 687 if (!decodeAndEvaluate(cx, instanceObj, d, type_, result)) { 688 // This expression should have been validated already. So we should only be 689 // able to OOM, which is reported by having no error message. 690 MOZ_RELEASE_ASSERT(!error); 691 return false; 692 } 693 694 return true; 695 } 696 697 bool InitExpr::clone(const InitExpr& src) { 698 kind_ = src.kind_; 699 MOZ_ASSERT(bytecode_.empty()); 700 if (!bytecode_.appendAll(src.bytecode_)) { 701 return false; 702 } 703 literal_ = src.literal_; 704 type_ = src.type_; 705 return true; 706 } 707 708 size_t InitExpr::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 709 return bytecode_.sizeOfExcludingThis(mallocSizeOf); 710 }