Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Trampoline-arm64.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "jit/arm64/SharedICHelpers-arm64.h"
#include "jit/Bailouts.h"
#include "jit/BaselineFrame.h"
#include "jit/CalleeToken.h"
#include "jit/JitFrames.h"
#include "jit/JitRuntime.h"
#include "jit/PerfSpewer.h"
#include "jit/VMFunctions.h"
#include "vm/JitActivation.h"  // js::jit::JitActivation
#include "vm/JSContext.h"

#include "jit/MacroAssembler-inl.h"

using namespace js;
using namespace js::jit;

/* This method generates a trampoline on ARM64 for a c++ function with
 * the following signature:
 *   bool blah(void* code, int argc, Value* argv,
 *             JSObject* scopeChain, Value* vp)
 *   ...using standard AArch64 calling convention
 */

void JitRuntime::generateEnterJIT(JSContext* cx, MacroAssembler& masm) {
  AutoCreatedBy acb(masm, "JitRuntime::generateEnterJIT");

  enterJITOffset_ = startTrampolineCode(masm);

  const Register reg_code = IntArgReg0;      // EnterJitData::jitcode.
  const Register reg_argc = IntArgReg1;      // EnterJitData::maxArgc.
  const Register reg_argv = IntArgReg2;      // EnterJitData::maxArgv.
  const Register reg_osrFrame = IntArgReg3;  // EnterJitData::osrFrame.
  const Register reg_callee = IntArgReg4;    // EnterJitData::calleeToken.
  const Register reg_scope = IntArgReg5;     // EnterJitData::scopeChain.
  const Register reg_osrNStack =
      IntArgReg6;                      // EnterJitData::osrNumStackValues.
  const Register reg_vp = IntArgReg7;  // Address of EnterJitData::result.

  static_assert(OsrFrameReg == IntArgReg3);

  // During the pushes below, use the normal stack pointer.
  masm.SetStackPointer64(sp);

  // Save return address and old frame pointer; set new frame pointer.
  masm.push(r30, r29);
  masm.moveStackPtrTo(r29);

  // Save callee-save integer registers.
  // Also save x7 (reg_vp) and x30 (lr), for use later.
  masm.push(r19, r20, r21, r22);
  masm.push(r23, r24, r25, r26);
  masm.push(r27, r28, r7, r30);

  // Save callee-save floating-point registers.
  // AArch64 ABI specifies that only the lower 64 bits must be saved.
  masm.push(d8, d9, d10, d11);
  masm.push(d12, d13, d14, d15);

#ifdef DEBUG
  // Emit stack canaries.
  masm.movePtr(ImmWord(0xdeadd00d), r23);
  masm.movePtr(ImmWord(0xdeadd11d), r24);
  masm.push(r23, r24);
#endif

  // Common code below attempts to push single registers at a time,
  // which breaks the stack pointer's 16-byte alignment requirement.
  // Note that movePtr() is invalid because StackPointer is treated as xzr.
  //
  // FIXME: After testing, this entire function should be rewritten to not
  // use the PseudoStackPointer: since the amount of data pushed is
  // precalculated, we can just allocate the whole frame header at once and
  // index off sp. This will save a significant number of instructions where
  // Push() updates sp.
  masm.Mov(PseudoStackPointer64, sp);
  masm.SetStackPointer64(PseudoStackPointer64);

  // Remember stack depth without padding and arguments.
  masm.moveStackPtrTo(r19);

  // If constructing, include newTarget in argument vector.
  {
    Label noNewTarget;
    Imm32 constructingToken(CalleeToken_FunctionConstructing);
    masm.branchTest32(Assembler::Zero, reg_callee, constructingToken,
                      &noNewTarget);
    masm.add32(Imm32(1), reg_argc);
    masm.bind(&noNewTarget);
  }

  // JitFrameLayout is as follows (higher is higher in memory):
  //  N*8  - [ JS argument vector ] (base 16-byte aligned)
  //  8    - calleeToken
  //  8    - frameDescriptor (16-byte aligned)
  //  8    - returnAddress
  //  8    - frame pointer (16-byte aligned, pushed by callee)

  // Touch frame incrementally (a requirement for Windows).
  //
  // Use already saved callee-save registers r20 and r21 as temps.
  //
  // This has to be done outside the ScratchRegisterScope, as the temps are
  // under demand inside the touchFrameValues call.

  // Give sp 16-byte alignment and sync stack pointers.
  masm.andToStackPtr(Imm32(~0xf));
  // We needn't worry about the Gecko Profiler mark because touchFrameValues
  // touches in large increments.
  masm.touchFrameValues(reg_argc, r20, r21);
  // Restore stack pointer, preserved above.
  masm.moveToStackPtr(r19);

  // Push the argument vector onto the stack.
  // WARNING: destructively modifies reg_argv
  {
    vixl::UseScratchRegisterScope temps(&masm.asVIXL());

    const ARMRegister tmp_argc = temps.AcquireX();
    const ARMRegister tmp_sp = temps.AcquireX();

    Label noArguments;
    Label loopHead;

    masm.movePtr(reg_argc, tmp_argc.asUnsized());

    // sp -= 8
    // Since we're using PostIndex Str below, this is necessary to avoid
    // overwriting the Gecko Profiler mark pushed above.
    masm.subFromStackPtr(Imm32(8));

    // sp -= 8 * argc
    masm.Sub(PseudoStackPointer64, PseudoStackPointer64,
             Operand(tmp_argc, vixl::SXTX, 3));

    // Give sp 16-byte alignment and sync stack pointers.
    masm.andToStackPtr(Imm32(~0xf));
    masm.moveStackPtrTo(tmp_sp.asUnsized());

    masm.branchTestPtr(Assembler::Zero, reg_argc, reg_argc, &noArguments);

    // Begin argument-pushing loop.
    // This could be optimized using Ldp and Stp.
    {
      masm.bind(&loopHead);

      // Load an argument from argv, then increment argv by 8.
      masm.Ldr(x24, MemOperand(ARMRegister(reg_argv, 64), Operand(8),
                               vixl::PostIndex));

      // Store the argument to tmp_sp, then increment tmp_sp by 8.
      masm.Str(x24, MemOperand(tmp_sp, Operand(8), vixl::PostIndex));

      // Decrement tmp_argc and set the condition codes for the new value.
      masm.Subs(tmp_argc, tmp_argc, Operand(1));

      // Branch if arguments remain.
      masm.B(&loopHead, vixl::Condition::NonZero);
    }

    masm.bind(&noArguments);
  }
  masm.checkStackAlignment();

  // Push the calleeToken and the frame descriptor.
  // The result address is used to store the actual number of arguments
  // without adding an argument to EnterJIT.
  {
    vixl::UseScratchRegisterScope temps(&masm.asVIXL());
    MOZ_ASSERT(temps.IsAvailable(ScratchReg64));  // ip0
    temps.Exclude(ScratchReg64);
    Register scratch = ScratchReg64.asUnsized();
    masm.push(reg_callee);

    // Push the descriptor.
    masm.unboxInt32(Address(reg_vp, 0x0), scratch);
    masm.PushFrameDescriptorForJitCall(FrameType::CppToJSJit, scratch, scratch);
  }
  masm.checkStackAlignment();

  Label osrReturnPoint;
  {
    // Check for Interpreter -> Baseline OSR.

    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
    MOZ_ASSERT(!regs.has(FramePointer));
    regs.take(OsrFrameReg);
    regs.take(reg_code);
    regs.take(reg_osrNStack);
    MOZ_ASSERT(!regs.has(ReturnReg), "ReturnReg matches reg_code");

    Label notOsr;
    masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, ¬Osr);

    Register scratch = regs.takeAny();

    // Frame prologue.
    masm.Adr(ARMRegister(scratch, 64), &osrReturnPoint);
    masm.push(scratch, FramePointer);
    masm.moveStackPtrTo(FramePointer);

    // Reserve frame.
    masm.subFromStackPtr(Imm32(BaselineFrame::Size()));

    Register framePtrScratch = regs.takeAny();
    masm.touchFrameValues(reg_osrNStack, scratch, framePtrScratch);
    masm.moveStackPtrTo(framePtrScratch);

    // Reserve space for locals and stack values.
    // scratch = num_stack_values * sizeof(Value).
    masm.Lsl(ARMRegister(scratch, 32), ARMRegister(reg_osrNStack, 32), 3);
    masm.subFromStackPtr(scratch);

    // Enter exit frame.
    masm.pushFrameDescriptor(FrameType::BaselineJS);
    masm.push(xzr);  // Push xzr for a fake return address.
    masm.push(FramePointer);
    // No GC things to mark: push a bare token.
    masm.loadJSContext(scratch);
    masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::Bare);

    masm.push(reg_code);

    // Initialize the frame, including filling in the slots.
    using Fn = bool (*)(BaselineFrame* frame, InterpreterFrame* interpFrame,
                        uint32_t numStackValues);
    masm.setupUnalignedABICall(r19);
    masm.passABIArg(framePtrScratch);  // BaselineFrame.
    masm.passABIArg(reg_osrFrame);     // InterpreterFrame.
    masm.passABIArg(reg_osrNStack);
    masm.callWithABI<Fn, jit::InitBaselineFrameForOsr>(
        ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);

    masm.pop(scratch);
    MOZ_ASSERT(scratch != ReturnReg);

    masm.addToStackPtr(Imm32(ExitFrameLayout::SizeWithFooter()));

    Label error;
    masm.branchIfFalseBool(ReturnReg, &error);

    // If OSR-ing, then emit instrumentation for setting lastProfilerFrame
    // if profiler instrumentation is enabled.
    {
      Label skipProfilingInstrumentation;
      AbsoluteAddress addressOfEnabled(
          cx->runtime()->geckoProfiler().addressOfEnabled());
      masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
                    &skipProfilingInstrumentation);
      masm.profilerEnterFrame(FramePointer, regs.getAny());
      masm.bind(&skipProfilingInstrumentation);
    }

    masm.jump(scratch);

    // OOM: frame epilogue, load error value, discard return address and return.
    masm.bind(&error);
    masm.moveToStackPtr(FramePointer);
    masm.pop(FramePointer);
    masm.addToStackPtr(Imm32(sizeof(uintptr_t)));  // Return address.
    masm.syncStackPtr();
    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
    masm.B(&osrReturnPoint);

    masm.bind(¬Osr);
    masm.movePtr(reg_scope, R1_);
  }

  // The callee will push the return address and frame pointer on the stack,
  // thus we check that the stack would be aligned once the call is complete.
  masm.assertStackAlignment(JitStackAlignment, 2 * sizeof(uintptr_t));

  // Call function.
  // Since AArch64 doesn't have the pc register available, the callee must push
  // lr.
  masm.callJitNoProfiler(reg_code);

  // Interpreter -> Baseline OSR will return here.
  masm.bind(&osrReturnPoint);

  // Discard arguments and padding. Set sp to the address of the saved
  // registers. In debug builds we have to include the two stack canaries
  // checked below.
#ifdef DEBUG
  static constexpr size_t SavedRegSize = 22 * sizeof(void*);
#else
  static constexpr size_t SavedRegSize = 20 * sizeof(void*);
#endif
  masm.computeEffectiveAddress(Address(FramePointer, -int32_t(SavedRegSize)),
                               masm.getStackPointer());

  masm.syncStackPtr();
  masm.SetStackPointer64(sp);

#ifdef DEBUG
  // Check that canaries placed on function entry are still present.
  masm.pop(r24, r23);
  Label x23OK, x24OK;

  masm.branchPtr(Assembler::Equal, r23, ImmWord(0xdeadd00d), &x23OK);
  masm.breakpoint();
  masm.bind(&x23OK);

  masm.branchPtr(Assembler::Equal, r24, ImmWord(0xdeadd11d), &x24OK);
  masm.breakpoint();
  masm.bind(&x24OK);
#endif

  // Restore callee-save floating-point registers.
  masm.pop(d15, d14, d13, d12);
  masm.pop(d11, d10, d9, d8);

  // Restore callee-save integer registers.
  // Also restore x7 (reg_vp) and x30 (lr).
  masm.pop(r30, r7, r28, r27);
  masm.pop(r26, r25, r24, r23);
  masm.pop(r22, r21, r20, r19);

  // Store return value (in JSReturnReg = x2 to just-popped reg_vp).
  masm.storeValue(JSReturnOperand, Address(reg_vp, 0));

  // Restore old frame pointer.
  masm.pop(r29, r30);

  // Return using the value popped into x30.
  masm.abiret();

  // Reset stack pointer.
  masm.SetStackPointer64(PseudoStackPointer64);
}

// static
mozilla::Maybe<::JS::ProfilingFrameIterator::RegisterState>
JitRuntime::getCppEntryRegisters(JitFrameLayout* frameStackAddress) {
  // Not supported, or not implemented yet.
  // TODO: Implement along with the corresponding stack-walker changes, in
  // coordination with the Gecko Profiler, see bug 1635987 and follow-ups.
  return mozilla::Nothing{};
}

static void PushRegisterDump(MacroAssembler& masm) {
  const LiveRegisterSet First28GeneralRegisters = LiveRegisterSet(
      GeneralRegisterSet(Registers::AllMask &
                         ~(1 << 31 | 1 << 30 | 1 << 29 | 1 << 28)),
      FloatRegisterSet(FloatRegisters::NoneMask));

  const LiveRegisterSet AllFloatRegisters =
      LiveRegisterSet(GeneralRegisterSet(Registers::NoneMask),
                      FloatRegisterSet(FloatRegisters::AllMask));

  // Push all general-purpose registers.
  //
  // The ARM64 ABI does not treat SP as a normal register that can
  // be pushed. So pushing happens in two phases.
  //
  // Registers are pushed in reverse order of code.
  //
  // See block comment in MacroAssembler.h for further required invariants.

  // First, push the last four registers, passing zero for sp.
  // Zero is pushed for x28 and x31: the pseudo-SP and SP, respectively.
  masm.asVIXL().Push(xzr, x30, x29, xzr);

  // Second, push the first 28 registers that serve no special purpose.
  masm.PushRegsInMask(First28GeneralRegisters);

  // Finally, push all floating-point registers, completing the RegisterDump.
  masm.PushRegsInMask(AllFloatRegisters);
}

void JitRuntime::generateInvalidator(MacroAssembler& masm, Label* bailoutTail) {
  AutoCreatedBy acb(masm, "JitRuntime::generateInvalidator");

  invalidatorOffset_ = startTrampolineCode(masm);

  // The InvalidationBailoutStack saved in r0 must be:
  // - osiPointReturnAddress_
  // - ionScript_  (pushed by CodeGeneratorARM64::generateInvalidateEpilogue())
  // - regs_  (pushed here)
  // - fpregs_  (pushed here) [=r0]
  PushRegisterDump(masm);
  masm.moveStackPtrTo(r0);

  // Reserve space for InvalidationBailout's bailoutInfo outparam.
  masm.Sub(x1, masm.GetStackPointer64(), Operand(sizeof(void*)));
  masm.moveToStackPtr(r1);

  using Fn = bool (*)(InvalidationBailoutStack* sp, BaselineBailoutInfo** info);
  masm.setupUnalignedABICall(r10);
  masm.passABIArg(r0);
  masm.passABIArg(r1);

  masm.callWithABI<Fn, InvalidationBailout>(
      ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);

  masm.pop(r2);  // Get the bailoutInfo outparam.

  // Pop the machine state and the dead frame.
  masm.moveToStackPtr(FramePointer);

  // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
  masm.jump(bailoutTail);
}

void JitRuntime::generateArgumentsRectifier(MacroAssembler& masm,
                                            ArgumentsRectifierKind kind) {
  AutoCreatedBy acb(masm, "JitRuntime::generateArgumentsRectifier");

  switch (kind) {
    case ArgumentsRectifierKind::Normal:
      argumentsRectifierOffset_ = startTrampolineCode(masm);
      break;
    case ArgumentsRectifierKind::TrialInlining:
      trialInliningArgumentsRectifierOffset_ = startTrampolineCode(masm);
      break;
  }

  // Save the return address for later.
  masm.push(lr);

  // Frame prologue.
  //
  // NOTE: if this changes, fix the Baseline bailout code too!
  // See BaselineStackBuilder::calculatePrevFramePtr and
  // BaselineStackBuilder::buildRectifierFrame (in BaselineBailouts.cpp).
  masm.push(FramePointer);
  masm.moveStackPtrTo(FramePointer);

  // Load the information that the rectifier needs from the stack.
  masm.loadNumActualArgs(FramePointer, r0);
  masm.loadPtr(
      Address(FramePointer, RectifierFrameLayout::offsetOfCalleeToken()), r1);

  // Extract a JSFunction pointer from the callee token and keep the
  // intermediary to avoid later recalculation.
  masm.And(x5, x1, Operand(CalleeTokenMask));

  // Get the arguments from the function object.
  masm.loadFunctionArgCount(x5.asUnsized(), x6.asUnsized());

  static_assert(CalleeToken_FunctionConstructing == 0x1,
                "Constructing must be low-order bit");
  masm.And(x4, x1, Operand(CalleeToken_FunctionConstructing));
  masm.Add(x7, x6, x4);

  // Copy the number of actual arguments into r8.
  masm.mov(r0, r8);

  // Calculate the position that our arguments are at before sp gets modified.
  masm.Add(x3, masm.GetStackPointer64(), Operand(x8, vixl::LSL, 3));
  masm.Add(x3, x3, Operand(sizeof(RectifierFrameLayout)));

  // If the number of Values without |this| is even, push 8 padding bytes to
  // ensure the stack is 16-byte aligned.
  Label noPadding;
  masm.Tbnz(x7, 0, &noPadding);
  masm.asVIXL().Push(xzr);
  masm.bind(&noPadding);

  {
    Label notConstructing;
    masm.Cbz(x4, ¬Constructing);

    // new.target lives at the end of the pushed args
    // NB: The arg vector holder starts at the beginning of the last arg,
    //     add a value to get to argv[argc]
    masm.loadPtr(Address(r3, sizeof(Value)), r4);
    masm.Push(r4);

    masm.bind(¬Constructing);
  }

  // Calculate the number of undefineds that need to be pushed.
  masm.Sub(w2, w6, w8);

  // Put an undefined in a register so it can be pushed.
  masm.moveValue(UndefinedValue(), ValueOperand(r4));

  // Push undefined N times.
  {
    Label undefLoopTop;
    masm.bind(&undefLoopTop);
    masm.Push(r4);
    masm.Subs(w2, w2, Operand(1));
    masm.B(&undefLoopTop, Assembler::NonZero);
  }

  // Arguments copy loop. Copy for x8 >= 0 to include |this|.
  {
    Label copyLoopTop;
    masm.bind(©LoopTop);
    masm.Ldr(x4, MemOperand(x3, -sizeof(Value), vixl::PostIndex));
    masm.Push(r4);
    masm.Subs(x8, x8, Operand(1));
    masm.B(©LoopTop, Assembler::NotSigned);
  }

  masm.push(r1);  // Callee token.
  masm.pushFrameDescriptorForJitCall(FrameType::Rectifier, r0, r0);

  // Call the target function.
  switch (kind) {
    case ArgumentsRectifierKind::Normal:
      masm.loadJitCodeRaw(r5, r3);
      argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(r3);
      break;
    case ArgumentsRectifierKind::TrialInlining:
      Label noBaselineScript, done;
      masm.loadBaselineJitCodeRaw(r5, r3, &noBaselineScript);
      masm.callJitNoProfiler(r3);
      masm.jump(&done);

      // See BaselineCacheIRCompiler::emitCallInlinedFunction.
      masm.bind(&noBaselineScript);
      masm.loadJitCodeRaw(r5, r3);
      masm.callJitNoProfiler(r3);
      masm.bind(&done);
      break;
  }

  masm.moveToStackPtr(FramePointer);
  masm.pop(FramePointer);
  masm.ret();
}

static void PushBailoutFrame(MacroAssembler& masm, Register spArg) {
  // This assumes no SIMD registers, as JS does not support SIMD.

  // The stack saved in spArg must be (higher entries have higher memory
  // addresses):
  // - snapshotOffset_
  // - frameSize_
  // - regs_
  // - fpregs_ (spArg + 0)
  PushRegisterDump(masm);
  masm.moveStackPtrTo(spArg);
}

static void GenerateBailoutThunk(MacroAssembler& masm, Label* bailoutTail) {
  PushBailoutFrame(masm, r0);

  // SP % 8 == 4
  // STEP 1c: Call the bailout function, giving a pointer to the
  //          structure we just blitted onto the stack.
  // Make space for the BaselineBailoutInfo* outparam.
  masm.reserveStack(sizeof(void*));
  masm.moveStackPtrTo(r1);

  using Fn = bool (*)(BailoutStack* sp, BaselineBailoutInfo** info);
  masm.setupUnalignedABICall(r2);
  masm.passABIArg(r0);
  masm.passABIArg(r1);
  masm.callWithABI<Fn, Bailout>(ABIType::General,
                                CheckUnsafeCallWithABI::DontCheckOther);

  // Get the bailoutInfo outparam.
  masm.pop(r2);

  // Remove both the bailout frame and the topmost Ion frame's stack.
  masm.moveToStackPtr(FramePointer);

  // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
  masm.jump(bailoutTail);
}

void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                        Label* bailoutTail) {
  AutoCreatedBy acb(masm, "JitRuntime::generateBailoutHandler");

  bailoutHandlerOffset_ = startTrampolineCode(masm);

  GenerateBailoutThunk(masm, bailoutTail);
}

bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
                                   VMFunctionId id, const VMFunctionData& f,
                                   DynFn nativeFun, uint32_t* wrapperOffset) {
  AutoCreatedBy acb(masm, "JitRuntime::generateVMWrapper");

  *wrapperOffset = startTrampolineCode(masm);

  // Avoid conflicts with argument registers while discarding the result after
  // the function call.
  AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);

  static_assert(
      (Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
      "Wrapper register set must be a superset of the Volatile register set.");

  // The first argument is the JSContext.
  Register reg_cx = IntArgReg0;
  regs.take(reg_cx);
  Register temp = regs.getAny();

  // On entry, the stack is:
  //   ... frame ...
  //  [args]
  //  descriptor
  //
  // Before we pass arguments (potentially pushing some of them on the stack),
  // we want:
  //  ... frame ...
  //  [args]
  //  descriptor           \
  //  return address       | <- exit frame
  //  saved frame pointer  /
  //  VM id                  <- exit frame footer
  //  [space for out-param, if necessary]]
  //  [alignment padding, if necessary]
  //
  // To minimize PSP overhead, we compute the final stack size and update the
  // stack pointer all in one go. Then we use the PSP to "push" the required
  // values into the pre-allocated stack space.
  size_t stackAdjustment = 0;

  // The descriptor was already pushed.
  stackAdjustment += ExitFrameLayout::SizeWithFooter() - sizeof(uintptr_t);
  stackAdjustment += f.sizeOfOutParamStackSlot();

  masm.SetStackPointer64(sp);

  // First, update the actual stack pointer to its final aligned value.
  masm.Sub(ARMRegister(temp, 64), masm.GetStackPointer64(),
           Operand(stackAdjustment));
  masm.And(sp, ARMRegister(temp, 64), ~(uint64_t(JitStackAlignment) - 1));

  // On link-register platforms, it is the responsibility of the VM *callee* to
  // push the return address, while the caller must ensure that the address
  // is stored in lr on entry. This allows the VM wrapper to work with both
  // direct calls and tail calls.
  masm.str(ARMRegister(lr, 64),
           MemOperand(PseudoStackPointer64, -8, vixl::PreIndex));

  // Push the frame pointer using the PSP.
  masm.str(ARMRegister(FramePointer, 64),
           MemOperand(PseudoStackPointer64, -8, vixl::PreIndex));

  // Because we've been moving the PSP as we fill in the frame, we can set the
  // frame pointer for this frame directly from the PSP.
  masm.movePtr(PseudoStackPointer, FramePointer);

  masm.loadJSContext(reg_cx);

  // Finish the exit frame. See MacroAssembler::enterExitFrame.

  // linkExitFrame
  masm.loadPtr(Address(reg_cx, JSContext::offsetOfActivation()), temp);
  masm.storePtr(FramePointer,
                Address(temp, JitActivation::offsetOfPackedExitFP()));

  // Push `ExitFrameType::VMFunction + VMFunctionId`
  uint32_t type = uint32_t(ExitFrameType::VMFunction) + uint32_t(id);
  masm.move32(Imm32(type), temp);
  masm.str(ARMRegister(temp, 64),
           MemOperand(PseudoStackPointer64, -8, vixl::PreIndex));

  // If the out parameter is a handle, initialize it to empty.
  // See MacroAssembler::reserveVMFunctionOutParamSpace and PushEmptyRooted.
  if (f.outParam == Type_Handle) {
    switch (f.outParamRootType) {
      case VMFunctionData::RootNone:
        MOZ_CRASH("Handle must have root type");
      case VMFunctionData::RootObject:
      case VMFunctionData::RootString:
      case VMFunctionData::RootCell:
      case VMFunctionData::RootBigInt:
        masm.str(xzr, MemOperand(PseudoStackPointer64, -8, vixl::PreIndex));
        break;
      case VMFunctionData::RootValue:
        masm.movePtr(ImmWord(UndefinedValue().asRawBits()), temp);
        masm.str(ARMRegister(temp, 64),
                 MemOperand(PseudoStackPointer64, -8, vixl::PreIndex));
        break;
      case VMFunctionData::RootId:
        masm.movePtr(ImmWord(JS::PropertyKey::Void().asRawBits()), temp);
        masm.str(ARMRegister(temp, 64),
                 MemOperand(PseudoStackPointer64, -8, vixl::PreIndex));
    }
  }

  // Now that we've filled in the stack frame, synchronize the PSP with the
  // real stack pointer and return to PSP-mode while we pass arguments.
  masm.moveStackPtrTo(PseudoStackPointer);
  masm.SetStackPointer64(PseudoStackPointer64);

  MOZ_ASSERT(masm.framePushed() == 0);
  masm.setupAlignedABICall();
  masm.passABIArg(reg_cx);

  size_t argDisp = ExitFrameLayout::Size();

  // Copy arguments.
  for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
    switch (f.argProperties(explicitArg)) {
      case VMFunctionData::WordByValue:
        masm.passABIArg(
            MoveOperand(FramePointer, argDisp),
            (f.argPassedInFloatReg(explicitArg) ? ABIType::Float64
                                                : ABIType::General));
        argDisp += sizeof(void*);
        break;

      case VMFunctionData::WordByRef:
        masm.passABIArg(MoveOperand(FramePointer, argDisp,
                                    MoveOperand::Kind::EffectiveAddress),
                        ABIType::General);
        argDisp += sizeof(void*);
        break;

      case VMFunctionData::DoubleByValue:
      case VMFunctionData::DoubleByRef:
        MOZ_CRASH("NYI: AArch64 callVM should not be used with 128bit values.");
    }
  }

  // Copy the semi-implicit outparam, if any.
  // It is not a C++-abi outparam, which would get passed in the
  // outparam register, but a real parameter to the function, which
  // was stack-allocated above.
  const int32_t outParamOffset =
      -int32_t(ExitFooterFrame::Size()) - f.sizeOfOutParamStackSlot();
  if (f.outParam != Type_Void) {
    masm.passABIArg(MoveOperand(FramePointer, outParamOffset,
                                MoveOperand::Kind::EffectiveAddress),
                    ABIType::General);
  }

  masm.callWithABI(nativeFun, ABIType::General,
                   CheckUnsafeCallWithABI::DontCheckHasExitFrame);

  // Test for failure.
  switch (f.failType()) {
    case Type_Cell:
      masm.branchTestPtr(Assembler::Zero, r0, r0, masm.failureLabel());
      break;
    case Type_Bool:
      masm.branchIfFalseBool(r0, masm.failureLabel());
      break;
    case Type_Void:
      break;
    default:
      MOZ_CRASH("unknown failure kind");
  }

  // Load the outparam.
  masm.loadVMFunctionOutParam(f, Address(FramePointer, outParamOffset));

  // Until C++ code is instrumented against Spectre, prevent speculative
  // execution from returning any private data.
  if (f.returnsData() && JitOptions.spectreJitToCxxCalls) {
    masm.speculationBarrier();
  }

  // Pop frame and restore frame pointer. We call Mov here directly instead
  // of `moveToStackPtr` to avoid a syncStackPtr. The stack pointer will be
  // synchronized as part of retn, after adjusting the PSP.
  masm.Mov(masm.GetStackPointer64(), ARMRegister(FramePointer, 64));
  masm.pop(FramePointer);

  // Return. Subtract sizeof(void*) for the frame pointer.
  masm.retn(Imm32(sizeof(ExitFrameLayout) - sizeof(void*) +
                  f.explicitStackSlots() * sizeof(void*) +
                  f.extraValuesToPop * sizeof(Value)));

  return true;
}

uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,
                                        MIRType type) {
  AutoCreatedBy acb(masm, "JitRuntime::generatePreBarrier");

  uint32_t offset = startTrampolineCode(masm);

  static_assert(PreBarrierReg == r1);
  Register temp1 = r2;
  Register temp2 = r3;
  Register temp3 = r4;
  masm.push(temp1);
  masm.push(temp2);
  masm.push(temp3);

  Label noBarrier;
  masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3,
                              &noBarrier);

  // Call into C++ to mark this GC thing.
  masm.pop(temp3);
  masm.pop(temp2);
  masm.pop(temp1);

  LiveRegisterSet regs =
      LiveRegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                      FloatRegisterSet(FloatRegisters::VolatileMask));

  // Also preserve the return address.
  regs.add(lr);

  masm.PushRegsInMask(regs);

  masm.movePtr(ImmPtr(cx->runtime()), r3);

  masm.setupUnalignedABICall(r0);
  masm.passABIArg(r3);
  masm.passABIArg(PreBarrierReg);
  masm.callWithABI(JitPreWriteBarrier(type));

  // Pop the volatile regs and restore LR.
  masm.PopRegsInMask(regs);
  masm.abiret();

  masm.bind(&noBarrier);
  masm.pop(temp3);
  masm.pop(temp2);
  masm.pop(temp1);
  masm.abiret();

  return offset;
}

void JitRuntime::generateBailoutTailStub(MacroAssembler& masm,
                                         Label* bailoutTail) {
  AutoCreatedBy acb(masm, "JitRuntime::generateBailoutTailStub");

  masm.bind(bailoutTail);
  masm.generateBailoutTail(r1, r2);
}

Messung V0.5
C=88 H=100 G=94

¤ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge