tor-browser

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

InterpreterEntryTrampoline.cpp (8554B)


      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 #include "jit/InterpreterEntryTrampoline.h"
      8 #include "jit/JitRuntime.h"
      9 #include "jit/Linker.h"
     10 #include "vm/Interpreter.h"
     11 
     12 #include "gc/Marking-inl.h"
     13 #include "jit/MacroAssembler-inl.h"
     14 
     15 using namespace js;
     16 using namespace js::jit;
     17 
     18 void js::ClearInterpreterEntryMap(JSRuntime* runtime) {
     19  if (runtime->hasJitRuntime() &&
     20      runtime->jitRuntime()->hasInterpreterEntryMap()) {
     21    runtime->jitRuntime()->getInterpreterEntryMap()->clear();
     22  }
     23 }
     24 
     25 void EntryTrampolineMap::traceTrampolineCode(JSTracer* trc) {
     26  for (jit::EntryTrampolineMap::Enum e(*this); !e.empty(); e.popFront()) {
     27    EntryTrampoline& trampoline = e.front().value();
     28    trampoline.trace(trc);
     29  }
     30 }
     31 
     32 void EntryTrampolineMap::updateScriptsAfterMovingGC(void) {
     33  for (jit::EntryTrampolineMap::Enum e(*this); !e.empty(); e.popFront()) {
     34    BaseScript* script = e.front().key();
     35    if (IsForwarded(script)) {
     36      script = Forwarded(script);
     37      e.rekeyFront(script);
     38    }
     39  }
     40 }
     41 
     42 #ifdef JSGC_HASH_TABLE_CHECKS
     43 void EntryTrampoline::checkTrampolineAfterMovingGC() const {
     44  JitCode* trampoline = entryTrampoline_;
     45  CheckGCThingAfterMovingGC(trampoline);
     46 }
     47 
     48 void EntryTrampolineMap::checkScriptsAfterMovingGC() {
     49  gc::CheckTableAfterMovingGC(*this, [](const auto& entry) {
     50    BaseScript* script = entry.key();
     51    CheckGCThingAfterMovingGC(script);
     52    entry.value().checkTrampolineAfterMovingGC();
     53    return script;
     54  });
     55 }
     56 #endif
     57 
     58 void JitRuntime::generateBaselineInterpreterEntryTrampoline(
     59    MacroAssembler& masm) {
     60  AutoCreatedBy acb(masm,
     61                    "JitRuntime::generateBaselineInterpreterEntryTrampoline");
     62 
     63 #ifdef JS_USE_LINK_REGISTER
     64  masm.pushReturnAddress();
     65 #endif
     66  masm.push(FramePointer);
     67  masm.moveStackPtrTo(FramePointer);
     68 
     69  AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     70  Register nargs = regs.takeAny();
     71  Register callee = regs.takeAny();
     72  Register scratch = regs.takeAny();
     73 
     74  // Load callee token and keep it in a register as it will be used often
     75  Address calleeTokenAddr(
     76      FramePointer, BaselineInterpreterEntryFrameLayout::offsetOfCalleeToken());
     77  masm.loadPtr(calleeTokenAddr, callee);
     78 
     79  // Load argc into nargs.
     80  masm.loadNumActualArgs(FramePointer, nargs);
     81 
     82  Label notFunction;
     83  {
     84    // Check if calleetoken is script or function
     85    masm.branchTestPtr(Assembler::NonZero, callee, Imm32(CalleeTokenScriptBit),
     86                       &notFunction);
     87 
     88    // CalleeToken is a function, load |nformals| into scratch
     89    masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), callee, scratch);
     90    masm.loadFunctionArgCount(scratch, scratch);
     91 
     92    // Take max(nformals, argc).
     93    Label noUnderflow;
     94    masm.branch32(Assembler::AboveOrEqual, nargs, scratch, &noUnderflow);
     95    {
     96      masm.movePtr(scratch, nargs);
     97    }
     98    masm.bind(&noUnderflow);
     99 
    100    // Add 1 to nargs if constructing.
    101    static_assert(
    102        CalleeToken_FunctionConstructing == 1,
    103        "Ensure that we can use the constructing bit to count the value");
    104    masm.movePtr(callee, scratch);
    105    masm.and32(Imm32(uint32_t(CalleeToken_FunctionConstructing)), scratch);
    106    masm.addPtr(scratch, nargs);
    107  }
    108  masm.bind(&notFunction);
    109 
    110  // Align stack
    111  masm.alignJitStackBasedOnNArgs(nargs, /*countIncludesThis = */ false);
    112 
    113  // Point argPtr to the topmost argument.
    114  static_assert(sizeof(Value) == 8,
    115                "Using TimesEight for scale of sizeof(Value).");
    116  BaseIndex topPtrAddr(FramePointer, nargs, TimesEight,
    117                       sizeof(BaselineInterpreterEntryFrameLayout));
    118  Register argPtr = nargs;
    119  masm.computeEffectiveAddress(topPtrAddr, argPtr);
    120 
    121  // Load the end address into scratch, which is the callee token.
    122  masm.computeEffectiveAddress(calleeTokenAddr, scratch);
    123 
    124  // Copy |this|+arguments
    125  Label loop;
    126  masm.bind(&loop);
    127  {
    128    masm.pushValue(Address(argPtr, 0));
    129    masm.subPtr(Imm32(sizeof(Value)), argPtr);
    130    masm.branchPtr(Assembler::Above, argPtr, scratch, &loop);
    131  }
    132 
    133  // Copy callee token
    134  masm.push(callee);
    135 
    136  // Save a new descriptor using BaselineInterpreterEntry frame type.
    137  masm.loadNumActualArgs(FramePointer, scratch);
    138  masm.pushFrameDescriptorForJitCall(FrameType::BaselineInterpreterEntry,
    139                                     scratch, scratch);
    140 
    141  // Call into baseline interpreter
    142  uint8_t* blinterpAddr = baselineInterpreter().codeRaw();
    143  masm.assertStackAlignment(JitStackAlignment, 2 * sizeof(uintptr_t));
    144  masm.call(ImmPtr(blinterpAddr));
    145 
    146  masm.moveToStackPtr(FramePointer);
    147  masm.pop(FramePointer);
    148  masm.ret();
    149 }
    150 
    151 void JitRuntime::generateInterpreterEntryTrampoline(MacroAssembler& masm) {
    152  AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterEntryTrampoline");
    153 
    154  // If BLI is disabled, we don't need an offset.
    155  if (IsBaselineInterpreterEnabled()) {
    156    uint32_t offset = startTrampolineCode(masm);
    157    if (!vmInterpreterEntryOffset_) {
    158      vmInterpreterEntryOffset_ = offset;
    159    }
    160  }
    161 
    162 #ifdef JS_CODEGEN_ARM64
    163  // Use the normal stack pointer for the initial pushes.
    164  masm.SetStackPointer64(sp);
    165 
    166  // Push lr and fp together to maintain 16-byte alignment.
    167  masm.push(lr, FramePointer);
    168  masm.moveStackPtrTo(FramePointer);
    169 
    170  // Save the PSP register (r28), and a scratch (r19).
    171  masm.push(r19, r28);
    172 
    173  // Setup the PSP so we can use callWithABI below.
    174  masm.SetStackPointer64(PseudoStackPointer64);
    175  masm.initPseudoStackPtr();
    176 
    177  Register arg0 = IntArgReg0;
    178  Register arg1 = IntArgReg1;
    179  Register scratch = r19;
    180 #elif defined(JS_CODEGEN_X86)
    181  masm.push(FramePointer);
    182  masm.moveStackPtrTo(FramePointer);
    183 
    184  AllocatableRegisterSet regs(RegisterSet::Volatile());
    185  Register arg0 = regs.takeAnyGeneral();
    186  Register arg1 = regs.takeAnyGeneral();
    187  Register scratch = regs.takeAnyGeneral();
    188 
    189  // First two arguments are passed on the stack in 32-bit.
    190  Address cxAddr(FramePointer, 2 * sizeof(void*));
    191  Address stateAddr(FramePointer, 3 * sizeof(void*));
    192  masm.loadPtr(cxAddr, arg0);
    193  masm.loadPtr(stateAddr, arg1);
    194 #else
    195  masm.push(FramePointer);
    196  masm.moveStackPtrTo(FramePointer);
    197 
    198  AllocatableRegisterSet regs(RegisterSet::Volatile());
    199  regs.take(IntArgReg0);
    200  regs.take(IntArgReg1);
    201  Register arg0 = IntArgReg0;
    202  Register arg1 = IntArgReg1;
    203  Register scratch = regs.takeAnyGeneral();
    204 #endif
    205 
    206  using Fn = bool (*)(JSContext* cx, js::RunState& state);
    207  masm.setupUnalignedABICall(scratch);
    208  masm.passABIArg(arg0);  // cx
    209  masm.passABIArg(arg1);  // state
    210  masm.callWithABI<Fn, Interpret>(
    211      ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
    212 
    213 #ifdef JS_CODEGEN_ARM64
    214  masm.syncStackPtr();
    215  masm.SetStackPointer64(sp);
    216 
    217  // Restore r28 and r19.
    218  masm.pop(r28, r19);
    219 
    220  // Restore old fp and pop lr for return.
    221  masm.pop(FramePointer, lr);
    222  masm.abiret();
    223 
    224  // Reset stack pointer.
    225  masm.SetStackPointer64(PseudoStackPointer64);
    226 #else
    227  masm.moveToStackPtr(FramePointer);
    228  masm.pop(FramePointer);
    229  masm.ret();
    230 #endif
    231 }
    232 
    233 JitCode* JitRuntime::generateEntryTrampolineForScript(JSContext* cx,
    234                                                      JSScript* script) {
    235  if (JitSpewEnabled(JitSpew_Codegen)) {
    236    UniqueChars funName;
    237    if (script->function() && script->function()->fullDisplayAtom()) {
    238      funName =
    239          AtomToPrintableString(cx, script->function()->fullDisplayAtom());
    240    }
    241 
    242    JitSpew(JitSpew_Codegen,
    243            "# Emitting Interpreter Entry Trampoline for %s (%s:%u:%u)",
    244            funName ? funName.get() : "*", script->filename(), script->lineno(),
    245            script->column().oneOriginValue());
    246  }
    247 
    248  TempAllocator temp(&cx->tempLifoAlloc());
    249  JitContext jctx(cx);
    250  StackMacroAssembler masm(cx, temp);
    251  AutoCreatedBy acb(masm, "JitRuntime::generateEntryTrampolineForScript");
    252  PerfSpewerRangeRecorder rangeRecorder(masm);
    253 
    254  if (IsBaselineInterpreterEnabled()) {
    255    generateBaselineInterpreterEntryTrampoline(masm);
    256    rangeRecorder.recordOffset("BaselineInterpreter", cx, script);
    257  }
    258 
    259  generateInterpreterEntryTrampoline(masm);
    260  rangeRecorder.recordOffset("Interpreter", cx, script);
    261 
    262  Linker linker(masm);
    263  JitCode* code = linker.newCode(cx, CodeKind::Other);
    264  if (!code) {
    265    return nullptr;
    266  }
    267  rangeRecorder.collectRangesForJitCode(code);
    268  JitSpew(JitSpew_Codegen, "# code = %p", code->raw());
    269  return code;
    270 }