CacheIRWriter.h (26250B)
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 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef jit_CacheIRWriter_h 8 #define jit_CacheIRWriter_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/Casting.h" 13 #include "mozilla/Maybe.h" 14 15 #include <stddef.h> 16 #include <stdint.h> 17 18 #include "jstypes.h" 19 #include "NamespaceImports.h" 20 21 #include "gc/AllocKind.h" 22 #include "jit/ABIFunctions.h" 23 #include "jit/CacheIR.h" 24 #include "jit/CacheIROpsGenerated.h" 25 #include "jit/CompactBuffer.h" 26 #include "jit/ICState.h" 27 #include "jit/Simulator.h" 28 #include "jit/TypeData.h" 29 #include "js/AllocPolicy.h" 30 #include "js/CallArgs.h" 31 #include "js/Class.h" 32 #include "js/experimental/JitInfo.h" 33 #include "js/Id.h" 34 #include "js/RootingAPI.h" 35 #include "js/ScalarType.h" 36 #include "js/Value.h" 37 #include "js/Vector.h" 38 #include "util/Memory.h" 39 #include "vm/GuardFuse.h" 40 #include "vm/JSFunction.h" 41 #include "vm/JSScript.h" 42 #include "vm/List.h" 43 #include "vm/Opcodes.h" 44 #include "vm/RealmFuses.h" 45 #include "vm/RuntimeFuses.h" 46 #include "vm/Shape.h" 47 #include "vm/TypeofEqOperand.h" // TypeofEqOperand 48 #include "wasm/WasmConstants.h" 49 #include "wasm/WasmValType.h" 50 51 class JS_PUBLIC_API JSTracer; 52 struct JS_PUBLIC_API JSContext; 53 54 class JSObject; 55 class JSString; 56 57 namespace JS { 58 class Symbol; 59 } 60 61 namespace js { 62 63 class GetterSetter; 64 enum class UnaryMathFunction : uint8_t; 65 66 namespace gc { 67 class AllocSite; 68 } 69 70 namespace jit { 71 72 class ICScript; 73 struct CacheIRAOTStub; 74 75 // Class to record CacheIR + some additional metadata for code generation. 76 class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { 77 #ifdef DEBUG 78 JSContext* cx_; 79 #endif 80 CompactBufferWriter buffer_; 81 82 uint32_t nextOperandId_; 83 uint32_t nextInstructionId_; 84 uint32_t numInputOperands_; 85 86 TypeData typeData_; 87 88 // The data (shapes, slot offsets, etc.) that will be stored in the ICStub. 89 Vector<StubField, 8, SystemAllocPolicy> stubFields_; 90 size_t stubDataSize_; 91 92 // For each operand id, record which instruction accessed it last. This 93 // information greatly improves register allocation. 94 Vector<uint32_t, 8, SystemAllocPolicy> operandLastUsed_; 95 96 // OperandId and stub offsets are stored in a single byte, so make sure 97 // this doesn't overflow. We use a very conservative limit for now. 98 static const size_t MaxOperandIds = 20; 99 static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t); 100 bool tooLarge_; 101 102 // Assume this stub can't be trial inlined until we see a scripted call/inline 103 // instruction. 104 TrialInliningState trialInliningState_ = TrialInliningState::Failure; 105 106 ObjOperandId savedScriptedGetterSetterCallee_; 107 108 // Basic caching to avoid quadatic lookup behaviour in readStubField. 109 mutable uint32_t lastOffset_; 110 mutable uint32_t lastIndex_; 111 112 #ifdef DEBUG 113 // Information for assertLengthMatches. 114 mozilla::Maybe<CacheOp> currentOp_; 115 size_t currentOpArgsStart_ = 0; 116 #endif 117 118 #ifdef DEBUG 119 void assertSameCompartment(JSObject* obj); 120 void assertSameZone(Shape* shape); 121 #else 122 void assertSameCompartment(JSObject* obj) {} 123 void assertSameZone(Shape* shape) {} 124 #endif 125 126 void writeOp(CacheOp op) { 127 buffer_.writeFixedUint16_t(uint16_t(op)); 128 nextInstructionId_++; 129 #ifdef DEBUG 130 MOZ_ASSERT(currentOp_.isNothing(), "Missing call to assertLengthMatches?"); 131 currentOp_.emplace(op); 132 currentOpArgsStart_ = buffer_.length(); 133 #endif 134 } 135 136 void assertLengthMatches() { 137 #ifdef DEBUG 138 // After writing arguments, assert the length matches CacheIROpArgLengths. 139 size_t expectedLen = CacheIROpInfos[size_t(*currentOp_)].argLength; 140 MOZ_ASSERT_IF(!failed(), 141 buffer_.length() - currentOpArgsStart_ == expectedLen); 142 currentOp_.reset(); 143 #endif 144 } 145 146 void writeOperandId(OperandId opId) { 147 if (opId.id() < MaxOperandIds) { 148 static_assert(MaxOperandIds <= UINT8_MAX, 149 "operand id must fit in a single byte"); 150 buffer_.writeByte(opId.id()); 151 } else { 152 tooLarge_ = true; 153 return; 154 } 155 if (opId.id() >= operandLastUsed_.length()) { 156 buffer_.propagateOOM(operandLastUsed_.resize(opId.id() + 1)); 157 if (buffer_.oom()) { 158 return; 159 } 160 } 161 MOZ_ASSERT(nextInstructionId_ > 0); 162 operandLastUsed_[opId.id()] = nextInstructionId_ - 1; 163 } 164 165 void writeCallFlagsImm(CallFlags flags) { buffer_.writeByte(flags.toByte()); } 166 167 void addStubField(uint64_t value, StubField::Type fieldType) { 168 size_t fieldOffset = stubDataSize_; 169 #ifndef JS_64BIT 170 // On 32-bit platforms there are two stub field sizes (4 bytes and 8 bytes). 171 // Ensure 8-byte fields are properly aligned. 172 if (StubField::sizeIsInt64(fieldType)) { 173 fieldOffset = AlignBytes(fieldOffset, sizeof(uint64_t)); 174 } 175 #endif 176 MOZ_ASSERT((fieldOffset % StubField::sizeInBytes(fieldType)) == 0); 177 178 size_t newStubDataSize = fieldOffset + StubField::sizeInBytes(fieldType); 179 if (newStubDataSize < MaxStubDataSizeInBytes) { 180 #ifndef JS_64BIT 181 // Add a RawInt32 stub field for padding if necessary, because when we 182 // iterate over the stub fields we assume there are no 'holes'. 183 if (fieldOffset != stubDataSize_) { 184 MOZ_ASSERT((stubDataSize_ + sizeof(uintptr_t)) == fieldOffset); 185 buffer_.propagateOOM( 186 stubFields_.append(StubField(0, StubField::Type::RawInt32))); 187 } 188 #endif 189 buffer_.propagateOOM(stubFields_.append(StubField(value, fieldType))); 190 MOZ_ASSERT((fieldOffset % sizeof(uintptr_t)) == 0); 191 buffer_.writeByte(fieldOffset / sizeof(uintptr_t)); 192 stubDataSize_ = newStubDataSize; 193 } else { 194 tooLarge_ = true; 195 } 196 } 197 198 void writeShapeField(Shape* shape) { 199 MOZ_ASSERT(shape); 200 assertSameZone(shape); 201 addStubField(uintptr_t(shape), StubField::Type::Shape); 202 } 203 void writeWeakShapeField(Shape* shape) { 204 MOZ_ASSERT(shape); 205 assertSameZone(shape); 206 addStubField(uintptr_t(shape), StubField::Type::WeakShape); 207 } 208 void writeObjectField(JSObject* obj) { 209 MOZ_ASSERT(obj); 210 assertSameCompartment(obj); 211 addStubField(uintptr_t(obj), StubField::Type::JSObject); 212 } 213 void writeWeakObjectField(JSObject* obj) { 214 MOZ_ASSERT(obj); 215 assertSameCompartment(obj); 216 addStubField(uintptr_t(obj), StubField::Type::WeakObject); 217 } 218 void writeStringField(JSString* str) { 219 MOZ_ASSERT(str); 220 addStubField(uintptr_t(str), StubField::Type::String); 221 } 222 void writeSymbolField(JS::Symbol* sym) { 223 MOZ_ASSERT(sym); 224 addStubField(uintptr_t(sym), StubField::Type::Symbol); 225 } 226 void writeWeakBaseScriptField(BaseScript* script) { 227 MOZ_ASSERT(script); 228 addStubField(uintptr_t(script), StubField::Type::WeakBaseScript); 229 } 230 void writeJitCodeField(JitCode* code) { 231 MOZ_ASSERT(code); 232 addStubField(uintptr_t(code), StubField::Type::JitCode); 233 } 234 void writeRawInt32Field(uint32_t val) { 235 addStubField(val, StubField::Type::RawInt32); 236 } 237 void writeRawPointerField(const void* ptr) { 238 addStubField(uintptr_t(ptr), StubField::Type::RawPointer); 239 } 240 void writeIdField(jsid id) { 241 addStubField(id.asRawBits(), StubField::Type::Id); 242 } 243 void writeValueField(const Value& val) { 244 addStubField(val.asRawBits(), StubField::Type::Value); 245 } 246 void writeWeakValueField(const Value& val) { 247 MOZ_ASSERT(val.isGCThing()); 248 addStubField(val.asRawBits(), StubField::Type::WeakValue); 249 } 250 void writeRawInt64Field(uint64_t val) { 251 addStubField(val, StubField::Type::RawInt64); 252 } 253 void writeDoubleField(double d) { 254 uint64_t bits = mozilla::BitwiseCast<uint64_t>(d); 255 addStubField(bits, StubField::Type::Double); 256 } 257 void writeAllocSiteField(gc::AllocSite* ptr) { 258 addStubField(uintptr_t(ptr), StubField::Type::AllocSite); 259 } 260 261 void writeJSOpImm(JSOp op) { 262 static_assert(sizeof(JSOp) == sizeof(uint8_t), "JSOp must fit in a byte"); 263 buffer_.writeByte(uint8_t(op)); 264 } 265 void writeTypeofEqOperandImm(TypeofEqOperand operand) { 266 buffer_.writeByte(operand.rawValue()); 267 } 268 void writeGuardClassKindImm(GuardClassKind kind) { 269 static_assert(sizeof(GuardClassKind) == sizeof(uint8_t), 270 "GuardClassKind must fit in a byte"); 271 buffer_.writeByte(uint8_t(kind)); 272 } 273 void writeArrayBufferViewKindImm(ArrayBufferViewKind kind) { 274 static_assert(sizeof(ArrayBufferViewKind) == sizeof(uint8_t), 275 "ArrayBufferViewKind must fit in a byte"); 276 buffer_.writeByte(uint8_t(kind)); 277 } 278 void writeValueTypeImm(ValueType type) { 279 static_assert(sizeof(ValueType) == sizeof(uint8_t), 280 "ValueType must fit in uint8_t"); 281 buffer_.writeByte(uint8_t(type)); 282 } 283 void writeJSWhyMagicImm(JSWhyMagic whyMagic) { 284 static_assert(JS_WHY_MAGIC_COUNT <= UINT8_MAX, 285 "JSWhyMagic must fit in uint8_t"); 286 buffer_.writeByte(uint8_t(whyMagic)); 287 } 288 void writeScalarTypeImm(Scalar::Type type) { 289 MOZ_ASSERT(size_t(type) <= UINT8_MAX); 290 buffer_.writeByte(uint8_t(type)); 291 } 292 void writeUnaryMathFunctionImm(UnaryMathFunction fun) { 293 static_assert(sizeof(UnaryMathFunction) == sizeof(uint8_t), 294 "UnaryMathFunction must fit in a byte"); 295 buffer_.writeByte(uint8_t(fun)); 296 } 297 void writeCompletionKindImm(CompletionKind kind) { 298 static_assert(sizeof(CompletionKind) == sizeof(uint8_t), 299 "CompletionKind must fit in a byte"); 300 buffer_.writeByte(uint8_t(kind)); 301 } 302 void writeBoolImm(bool b) { buffer_.writeByte(uint32_t(b)); } 303 void writeRealmFuseIndexImm(RealmFuses::FuseIndex realmFuseIndex) { 304 static_assert(sizeof(RealmFuses::FuseIndex) == sizeof(uint8_t), 305 "RealmFuses::FuseIndex must fit in a byte"); 306 buffer_.writeByte(uint8_t(realmFuseIndex)); 307 } 308 void writeRuntimeFuseIndexImm(RuntimeFuses::FuseIndex runtimeFuseIndex) { 309 static_assert(sizeof(RuntimeFuses::FuseIndex) == sizeof(uint8_t), 310 "RuntimeFuses::FuseIndex must fit in a byte"); 311 buffer_.writeByte(uint8_t(runtimeFuseIndex)); 312 } 313 314 void writeByteImm(uint32_t b) { 315 MOZ_ASSERT(b <= UINT8_MAX); 316 buffer_.writeByte(b); 317 } 318 319 void writeInt32Imm(int32_t i32) { buffer_.writeFixedUint32_t(i32); } 320 void writeUInt32Imm(uint32_t u32) { buffer_.writeFixedUint32_t(u32); } 321 void writePointer(const void* ptr) { buffer_.writeRawPointer(ptr); } 322 323 void writeJSNativeImm(JSNative native) { 324 writePointer(JS_FUNC_TO_DATA_PTR(void*, native)); 325 } 326 void writeStaticStringImm(const char* str) { writePointer(str); } 327 328 void writeWasmValTypeImm(wasm::ValType::Kind kind) { 329 static_assert(unsigned(wasm::TypeCode::Limit) <= UINT8_MAX); 330 buffer_.writeByte(uint8_t(kind)); 331 } 332 333 void writeAllocKindImm(gc::AllocKind kind) { 334 static_assert(unsigned(gc::AllocKind::LIMIT) <= UINT8_MAX); 335 buffer_.writeByte(uint8_t(kind)); 336 } 337 338 uint32_t newOperandId() { return nextOperandId_++; } 339 340 CacheIRWriter(const CacheIRWriter&) = delete; 341 CacheIRWriter& operator=(const CacheIRWriter&) = delete; 342 343 public: 344 explicit CacheIRWriter(JSContext* cx) 345 : CustomAutoRooter(cx), 346 #ifdef DEBUG 347 cx_(cx), 348 #endif 349 nextOperandId_(0), 350 nextInstructionId_(0), 351 numInputOperands_(0), 352 stubDataSize_(0), 353 tooLarge_(false), 354 lastOffset_(0), 355 lastIndex_(0) { 356 } 357 358 #ifdef ENABLE_JS_AOT_ICS 359 CacheIRWriter(JSContext* cx, const CacheIRAOTStub& aot); 360 #endif 361 362 bool tooLarge() const { return tooLarge_; } 363 bool oom() const { return buffer_.oom(); } 364 bool failed() const { return tooLarge() || oom(); } 365 366 TrialInliningState trialInliningState() const { return trialInliningState_; } 367 368 uint32_t numInputOperands() const { return numInputOperands_; } 369 uint32_t numOperandIds() const { return nextOperandId_; } 370 uint32_t numInstructions() const { return nextInstructionId_; } 371 372 size_t numStubFields() const { return stubFields_.length(); } 373 const StubField& stubField(uint32_t i) const { return stubFields_[i]; } 374 StubField::Type stubFieldType(uint32_t i) const { 375 return stubFields_[i].type(); 376 } 377 378 uint32_t setInputOperandId(uint32_t op) { 379 MOZ_ASSERT(op == nextOperandId_); 380 nextOperandId_++; 381 numInputOperands_++; 382 return op; 383 } 384 385 TypeData typeData() const { return typeData_; } 386 void setTypeData(TypeData data) { typeData_ = data; } 387 388 void trace(JSTracer* trc) override { 389 // For now, assert we only GC before we append stub fields. 390 MOZ_RELEASE_ASSERT(stubFields_.empty()); 391 } 392 393 size_t stubDataSize() const { return stubDataSize_; } 394 void copyStubData(uint8_t* dest) const; 395 bool stubDataEquals(const uint8_t* stubData) const; 396 bool stubDataEqualsIgnoringShapeAndOffset( 397 const uint8_t* stubData, uint32_t shapeFieldOffset, 398 mozilla::Maybe<uint32_t> offsetFieldOffset) const; 399 400 bool operandIsDead(uint32_t operandId, uint32_t currentInstruction) const { 401 if (operandId >= operandLastUsed_.length()) { 402 return false; 403 } 404 return currentInstruction > operandLastUsed_[operandId]; 405 } 406 uint32_t operandLastUsed(uint32_t operandId) const { 407 return operandLastUsed_[operandId]; 408 } 409 410 const uint8_t* codeStart() const { 411 MOZ_ASSERT(!failed()); 412 return buffer_.buffer(); 413 } 414 415 const uint8_t* codeEnd() const { 416 MOZ_ASSERT(!failed()); 417 return buffer_.buffer() + buffer_.length(); 418 } 419 420 uint32_t codeLength() const { 421 MOZ_ASSERT(!failed()); 422 return buffer_.length(); 423 } 424 425 // This should not be used when compiling Baseline code, as Baseline code 426 // shouldn't bake in stub values. 427 StubField readStubField(uint32_t offset, StubField::Type type) const; 428 429 ObjOperandId guardToObject(ValOperandId input) { 430 guardToObject_(input); 431 return ObjOperandId(input.id()); 432 } 433 434 StringOperandId guardToString(ValOperandId input) { 435 guardToString_(input); 436 return StringOperandId(input.id()); 437 } 438 439 SymbolOperandId guardToSymbol(ValOperandId input) { 440 guardToSymbol_(input); 441 return SymbolOperandId(input.id()); 442 } 443 444 BigIntOperandId guardToBigInt(ValOperandId input) { 445 guardToBigInt_(input); 446 return BigIntOperandId(input.id()); 447 } 448 449 BooleanOperandId guardToBoolean(ValOperandId input) { 450 guardToBoolean_(input); 451 return BooleanOperandId(input.id()); 452 } 453 454 Int32OperandId guardToInt32(ValOperandId input) { 455 guardToInt32_(input); 456 return Int32OperandId(input.id()); 457 } 458 459 NumberOperandId guardIsNumber(ValOperandId input) { 460 guardIsNumber_(input); 461 return NumberOperandId(input.id()); 462 } 463 464 StringOperandId stringToAtom(StringOperandId input) { 465 stringToAtom_(input); 466 return input; 467 } 468 469 ValOperandId boxObject(ObjOperandId input) { 470 return ValOperandId(input.id()); 471 } 472 473 void guardShapeForClass(ObjOperandId obj, Shape* shape) { 474 // Guard shape to ensure that object class is unchanged. This is true 475 // for all shapes. 476 guardShape(obj, shape); 477 } 478 479 void guardShapeForOwnProperties(ObjOperandId obj, Shape* shape) { 480 // Guard shape to detect changes to (non-dense) own properties. This 481 // also implies |guardShapeForClass|. 482 MOZ_ASSERT(shape->getObjectClass()->isNativeObject()); 483 guardShape(obj, shape); 484 } 485 486 public: 487 void guardSpecificFunction(ObjOperandId obj, JSFunction* expected) { 488 // Guard object is a specific function. This implies immutable fields on 489 // the JSFunction struct itself are unchanged. 490 // Bake in the nargs and FunctionFlags so Warp can use them off-main thread, 491 // instead of directly using the JSFunction fields. 492 uint32_t nargsAndFlags = expected->flagsAndArgCountRaw(); 493 guardSpecificFunction_(obj, expected, nargsAndFlags); 494 } 495 496 void guardFunctionScript(ObjOperandId fun, BaseScript* expected) { 497 // Guard function has a specific BaseScript. This implies immutable fields 498 // on the JSFunction struct itself are unchanged and are equivalent for 499 // lambda clones. 500 // Bake in the nargs and FunctionFlags so Warp can use them off-main thread, 501 // instead of directly using the JSFunction fields. 502 uint32_t nargsAndFlags = expected->function()->flagsAndArgCountRaw(); 503 guardFunctionScript_(fun, expected, nargsAndFlags); 504 } 505 506 ValOperandId loadArgumentFixedSlot(ArgumentKind kind, uint32_t argc, 507 CallFlags flags) { 508 bool addArgc; 509 int32_t slotIndex = GetIndexOfArgument(kind, flags, &addArgc); 510 if (addArgc) { 511 slotIndex += argc; 512 } 513 MOZ_ASSERT(slotIndex >= 0); 514 MOZ_RELEASE_ASSERT(slotIndex <= UINT8_MAX); 515 return loadArgumentFixedSlot_(slotIndex); 516 } 517 518 ValOperandId loadArgumentDynamicSlot(ArgumentKind kind, Int32OperandId argcId, 519 CallFlags flags) { 520 bool addArgc; 521 int32_t slotIndex = GetIndexOfArgument(kind, flags, &addArgc); 522 if (addArgc) { 523 return loadArgumentDynamicSlot_(argcId, slotIndex); 524 } 525 return loadArgumentFixedSlot_(slotIndex); 526 } 527 528 ObjOperandId loadSpreadArgs() { 529 ArgumentKind kind = ArgumentKind::Arg0; 530 uint32_t argc = 1; 531 CallFlags flags(CallFlags::Spread); 532 return ObjOperandId(loadArgumentFixedSlot(kind, argc, flags).id()); 533 } 534 535 void callScriptedFunction(ObjOperandId callee, Int32OperandId argc, 536 CallFlags flags, uint32_t argcFixed) { 537 callScriptedFunction_(callee, argc, flags, argcFixed); 538 trialInliningState_ = TrialInliningState::Candidate; 539 } 540 541 void callInlinedFunction(ObjOperandId callee, Int32OperandId argc, 542 ICScript* icScript, CallFlags flags, 543 uint32_t argcFixed) { 544 callInlinedFunction_(callee, argc, icScript, flags, argcFixed); 545 trialInliningState_ = TrialInliningState::Inlined; 546 } 547 548 void callNativeFunction(ObjOperandId calleeId, Int32OperandId argc, JSOp op, 549 JSFunction* calleeFunc, CallFlags flags, 550 uint32_t argcFixed) { 551 // Some native functions can be implemented faster if we know that 552 // the return value is ignored. 553 bool ignoresReturnValue = 554 op == JSOp::CallIgnoresRv && calleeFunc->hasJitInfo() && 555 calleeFunc->jitInfo()->type() == JSJitInfo::IgnoresReturnValueNative; 556 557 #ifdef JS_SIMULATOR 558 // The simulator requires VM calls to be redirected to a special 559 // swi instruction to handle them, so we store the redirected 560 // pointer in the stub and use that instead of the original one. 561 // If we are calling the ignoresReturnValue version of a native 562 // function, we bake it into the redirected pointer. 563 // (See BaselineCacheIRCompiler::emitCallNativeFunction.) 564 JSNative target = ignoresReturnValue 565 ? calleeFunc->jitInfo()->ignoresReturnValueMethod 566 : calleeFunc->native(); 567 void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, target); 568 void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3); 569 callNativeFunction_(calleeId, argc, flags, argcFixed, redirected); 570 #else 571 // If we are not running in the simulator, we generate different jitcode 572 // to find the ignoresReturnValue version of a native function. 573 callNativeFunction_(calleeId, argc, flags, argcFixed, ignoresReturnValue); 574 #endif 575 } 576 577 void callDOMFunction(ObjOperandId calleeId, Int32OperandId argc, 578 ObjOperandId thisObjId, JSFunction* calleeFunc, 579 CallFlags flags, uint32_t argcFixed) { 580 #ifdef JS_SIMULATOR 581 void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, calleeFunc->native()); 582 void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3); 583 callDOMFunction_(calleeId, argc, thisObjId, flags, argcFixed, redirected); 584 #else 585 callDOMFunction_(calleeId, argc, thisObjId, flags, argcFixed); 586 #endif 587 } 588 589 void callDOMFunctionWithAllocSite(ObjOperandId calleeId, Int32OperandId argc, 590 ObjOperandId thisObjId, 591 JSFunction* calleeFunc, CallFlags flags, 592 uint32_t argcFixed, 593 gc::AllocSite* allocSite) { 594 #ifdef JS_SIMULATOR 595 void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, calleeFunc->native()); 596 void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3); 597 callDOMFunctionWithAllocSite_(calleeId, argc, thisObjId, flags, argcFixed, 598 allocSite, redirected); 599 #else 600 callDOMFunctionWithAllocSite_(calleeId, argc, thisObjId, flags, argcFixed, 601 allocSite); 602 #endif 603 } 604 605 void callAnyNativeFunction(ObjOperandId calleeId, Int32OperandId argc, 606 CallFlags flags, uint32_t argcFixed) { 607 MOZ_ASSERT(!flags.isSameRealm()); 608 #ifdef JS_SIMULATOR 609 const void* redirected = RedirectedCallAnyNative(); 610 callNativeFunction_(calleeId, argc, flags, argcFixed, redirected); 611 #else 612 callNativeFunction_(calleeId, argc, flags, argcFixed, 613 /* ignoresReturnValue = */ false); 614 #endif 615 } 616 617 void callClassHook(ObjOperandId calleeId, Int32OperandId argc, JSNative hook, 618 CallFlags flags, uint32_t argcFixed) { 619 MOZ_ASSERT(!flags.isSameRealm()); 620 void* target = JS_FUNC_TO_DATA_PTR(void*, hook); 621 #ifdef JS_SIMULATOR 622 // The simulator requires VM calls to be redirected to a special 623 // swi instruction to handle them, so we store the redirected 624 // pointer in the stub and use that instead of the original one. 625 target = Simulator::RedirectNativeFunction(target, Args_General3); 626 #endif 627 callClassHook_(calleeId, argc, flags, argcFixed, target); 628 } 629 630 void saveScriptedGetterSetterCallee(ObjOperandId callee) { 631 MOZ_ASSERT(!hasSavedScriptedGetterSetterCallee()); 632 savedScriptedGetterSetterCallee_ = callee; 633 } 634 635 private: 636 bool hasSavedScriptedGetterSetterCallee() { 637 return savedScriptedGetterSetterCallee_.valid(); 638 } 639 640 ObjOperandId getterSetterCalleeOperand(JSFunction* target) { 641 if (hasSavedScriptedGetterSetterCallee()) { 642 return savedScriptedGetterSetterCallee_; 643 } 644 return loadObject(target); 645 } 646 647 public: 648 void callScriptedGetterResult(ValOperandId receiver, JSFunction* getter, 649 bool sameRealm) { 650 MOZ_ASSERT(getter->hasJitEntry()); 651 uint32_t nargsAndFlags = getter->flagsAndArgCountRaw(); 652 ObjOperandId callee = getterSetterCalleeOperand(getter); 653 callScriptedGetterResult_(receiver, callee, sameRealm, nargsAndFlags); 654 trialInliningState_ = TrialInliningState::Candidate; 655 } 656 657 void callInlinedGetterResult(ValOperandId receiver, ObjOperandId callee, 658 JSFunction* getter, ICScript* icScript, 659 bool sameRealm) { 660 MOZ_ASSERT(getter->hasJitEntry()); 661 MOZ_ASSERT(!hasSavedScriptedGetterSetterCallee()); 662 uint32_t nargsAndFlags = getter->flagsAndArgCountRaw(); 663 callInlinedGetterResult_(receiver, callee, icScript, sameRealm, 664 nargsAndFlags); 665 trialInliningState_ = TrialInliningState::Inlined; 666 } 667 668 void callNativeGetterResult(ValOperandId receiver, JSFunction* getter, 669 bool sameRealm) { 670 MOZ_ASSERT(getter->isNativeWithoutJitEntry()); 671 MOZ_ASSERT(!hasSavedScriptedGetterSetterCallee()); 672 uint32_t nargsAndFlags = getter->flagsAndArgCountRaw(); 673 callNativeGetterResult_(receiver, getter, sameRealm, nargsAndFlags); 674 } 675 676 void callScriptedSetter(ObjOperandId receiver, JSFunction* setter, 677 ValOperandId rhs, bool sameRealm) { 678 MOZ_ASSERT(setter->hasJitEntry()); 679 uint32_t nargsAndFlags = setter->flagsAndArgCountRaw(); 680 ObjOperandId callee = getterSetterCalleeOperand(setter); 681 callScriptedSetter_(receiver, callee, rhs, sameRealm, nargsAndFlags); 682 trialInliningState_ = TrialInliningState::Candidate; 683 } 684 685 void callInlinedSetter(ObjOperandId receiver, ObjOperandId callee, 686 JSFunction* setter, ValOperandId rhs, 687 ICScript* icScript, bool sameRealm) { 688 MOZ_ASSERT(setter->hasJitEntry()); 689 MOZ_ASSERT(!hasSavedScriptedGetterSetterCallee()); 690 uint32_t nargsAndFlags = setter->flagsAndArgCountRaw(); 691 callInlinedSetter_(receiver, callee, rhs, icScript, sameRealm, 692 nargsAndFlags); 693 trialInliningState_ = TrialInliningState::Inlined; 694 } 695 696 void callNativeSetter(ObjOperandId receiver, JSFunction* setter, 697 ValOperandId rhs, bool sameRealm) { 698 MOZ_ASSERT(setter->isNativeWithoutJitEntry()); 699 MOZ_ASSERT(!hasSavedScriptedGetterSetterCallee()); 700 uint32_t nargsAndFlags = setter->flagsAndArgCountRaw(); 701 callNativeSetter_(receiver, setter, rhs, sameRealm, nargsAndFlags); 702 } 703 704 #ifdef JS_PUNBOX64 705 void callScriptedProxyGetResult(ValOperandId target, ObjOperandId receiver, 706 ObjOperandId handler, ObjOperandId trapId, 707 JSFunction* trap, HandleId property) { 708 MOZ_ASSERT(trap->hasJitEntry()); 709 uint32_t nargsAndFlags = trap->flagsAndArgCountRaw(); 710 callScriptedProxyGetResult_(target, receiver, handler, trapId, property, 711 nargsAndFlags); 712 } 713 714 void callScriptedProxyGetByValueResult( 715 ValOperandId target, ObjOperandId receiver, ObjOperandId handler, 716 ValOperandId property, ObjOperandId trapId, JSFunction* trap) { 717 MOZ_ASSERT(trap->hasJitEntry()); 718 uint32_t nargsAndFlags = trap->flagsAndArgCountRaw(); 719 callScriptedProxyGetByValueResult_(target, receiver, handler, property, 720 trapId, nargsAndFlags); 721 } 722 #endif 723 724 void metaScriptedThisShape(Shape* thisShape) { 725 metaScriptedThisShape_(thisShape); 726 } 727 728 void guardMultipleShapes(ObjOperandId obj, ListObject* shapes) { 729 MOZ_ASSERT(shapes->length() > 0); 730 guardMultipleShapes_(obj, shapes); 731 } 732 733 Int32OperandId guardMultipleShapesToOffset(ObjOperandId obj, 734 ListObject* shapes) { 735 MOZ_ASSERT(shapes->length() > 0); 736 return guardMultipleShapesToOffset_(obj, shapes); 737 } 738 739 friend class CacheIRCloner; 740 741 CACHE_IR_WRITER_GENERATED 742 }; 743 744 } // namespace jit 745 } // namespace js 746 747 #endif /* jit_CacheIRWriter_h */