tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */