diff options
Diffstat (limited to 'js/src/jit/TrampolineNatives.cpp')
-rw-r--r-- | js/src/jit/TrampolineNatives.cpp | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/js/src/jit/TrampolineNatives.cpp b/js/src/jit/TrampolineNatives.cpp new file mode 100644 index 0000000000..0bde6d9985 --- /dev/null +++ b/js/src/jit/TrampolineNatives.cpp @@ -0,0 +1,274 @@ +/* -*- 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/TrampolineNatives.h" + +#include "jit/CalleeToken.h" +#include "jit/Ion.h" +#include "jit/JitCommon.h" +#include "jit/JitRuntime.h" +#include "jit/MacroAssembler.h" +#include "jit/PerfSpewer.h" +#include "js/CallArgs.h" +#include "js/experimental/JitInfo.h" + +#include "jit/MacroAssembler-inl.h" +#include "vm/Activation-inl.h" + +using namespace js; +using namespace js::jit; + +#define ADD_NATIVE(native) \ + const JSJitInfo js::jit::JitInfo_##native{ \ + {nullptr}, \ + {uint16_t(TrampolineNative::native)}, \ + {0}, \ + JSJitInfo::TrampolineNative}; +TRAMPOLINE_NATIVE_LIST(ADD_NATIVE) +#undef ADD_NATIVE + +void js::jit::SetTrampolineNativeJitEntry(JSContext* cx, JSFunction* fun, + TrampolineNative native) { + if (!cx->runtime()->jitRuntime()) { + // No JIT support so there's no trampoline. + return; + } + void** entry = cx->runtime()->jitRuntime()->trampolineNativeJitEntry(native); + MOZ_ASSERT(entry); + MOZ_ASSERT(*entry); + fun->setTrampolineNativeJitEntry(entry); +} + +uint32_t JitRuntime::generateArraySortTrampoline(MacroAssembler& masm) { + AutoCreatedBy acb(masm, "JitRuntime::generateArraySortTrampoline"); + + const uint32_t offset = startTrampolineCode(masm); + + // The stack for the trampoline frame will look like this: + // + // [TrampolineNativeFrameLayout] + // * this and arguments passed by the caller + // * CalleeToken + // * Descriptor + // * Return Address + // * Saved frame pointer <= FramePointer + // [ArraySortData] + // * ... + // * Comparator this + argument Values --+ -> comparator JitFrameLayout + // * Comparator (CalleeToken) | + // * Descriptor ----+ <= StackPointer + // + // The call to the comparator pushes the return address and the frame pointer, + // so we check the alignment after pushing these two pointers. + constexpr size_t FrameSize = sizeof(ArraySortData); + constexpr size_t PushedByCall = 2 * sizeof(void*); + static_assert((FrameSize + PushedByCall) % JitStackAlignment == 0); + + // Assert ArraySortData comparator data matches JitFrameLayout. + static_assert(PushedByCall + ArraySortData::offsetOfDescriptor() == + JitFrameLayout::offsetOfDescriptor()); + static_assert(PushedByCall + ArraySortData::offsetOfComparator() == + JitFrameLayout::offsetOfCalleeToken()); + static_assert(PushedByCall + ArraySortData::offsetOfComparatorThis() == + JitFrameLayout::offsetOfThis()); + static_assert(PushedByCall + ArraySortData::offsetOfComparatorArgs() == + JitFrameLayout::offsetOfActualArgs()); + static_assert(CalleeToken_Function == 0, + "JSFunction* is valid CalleeToken for non-constructor calls"); + + // Compute offsets from FramePointer. + constexpr int32_t ComparatorOffset = + -int32_t(FrameSize) + ArraySortData::offsetOfComparator(); + constexpr int32_t RvalOffset = + -int32_t(FrameSize) + ArraySortData::offsetOfComparatorReturnValue(); + constexpr int32_t DescriptorOffset = + -int32_t(FrameSize) + ArraySortData::offsetOfDescriptor(); + +#ifdef JS_USE_LINK_REGISTER + masm.pushReturnAddress(); +#endif + masm.push(FramePointer); + masm.moveStackPtrTo(FramePointer); + + AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); + regs.takeUnchecked(ReturnReg); + regs.takeUnchecked(JSReturnOperand); + Register temp0 = regs.takeAny(); + Register temp1 = regs.takeAny(); + Register temp2 = regs.takeAny(); + + // Reserve space and check alignment of the comparator frame. + masm.reserveStack(FrameSize); + masm.assertStackAlignment(JitStackAlignment, PushedByCall); + + // Trampoline control flow looks like this: + // + // call ArraySortFromJit + // goto checkReturnValue + // call_comparator: + // call comparator + // call ArraySortData::sortWithComparator + // checkReturnValue: + // check return value, jump to call_comparator if needed + // return rval + + auto pushExitFrame = [&](Register cxReg, Register scratchReg) { + MOZ_ASSERT(masm.framePushed() == FrameSize); + masm.PushFrameDescriptor(FrameType::TrampolineNative); + masm.Push(ImmWord(0)); // Fake return address. + masm.Push(FramePointer); + masm.enterFakeExitFrame(cxReg, scratchReg, ExitFrameType::Bare); + }; + + // Call ArraySortFromJit. + using Fn1 = ArraySortResult (*)(JSContext* cx, + jit::TrampolineNativeFrameLayout* frame); + masm.loadJSContext(temp0); + pushExitFrame(temp0, temp1); + masm.setupAlignedABICall(); + masm.passABIArg(temp0); + masm.passABIArg(FramePointer); + masm.callWithABI<Fn1, ArraySortFromJit>( + ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); + + // Check return value. + Label checkReturnValue; + masm.jump(&checkReturnValue); + masm.setFramePushed(FrameSize); + + // Call the comparator. Store the frame descriptor before each call to ensure + // the HASCACHEDSAVEDFRAME_BIT flag from a previous call is cleared. + uintptr_t jitCallDescriptor = MakeFrameDescriptorForJitCall( + jit::FrameType::TrampolineNative, ArraySortData::ComparatorActualArgs); + Label callDone, jitCallFast, jitCallSlow; + masm.bind(&jitCallFast); + { + masm.storePtr(ImmWord(jitCallDescriptor), + Address(FramePointer, DescriptorOffset)); + masm.loadPtr(Address(FramePointer, ComparatorOffset), temp0); + masm.loadJitCodeRaw(temp0, temp1); + masm.callJit(temp1); + masm.jump(&callDone); + } + masm.bind(&jitCallSlow); + { + masm.storePtr(ImmWord(jitCallDescriptor), + Address(FramePointer, DescriptorOffset)); + masm.loadPtr(Address(FramePointer, ComparatorOffset), temp0); + masm.loadJitCodeRaw(temp0, temp1); + masm.switchToObjectRealm(temp0, temp2); + + // Handle arguments underflow. + Label noUnderflow, restoreRealm; + masm.loadFunctionArgCount(temp0, temp0); + masm.branch32(Assembler::BelowOrEqual, temp0, + Imm32(ArraySortData::ComparatorActualArgs), &noUnderflow); + { + Label rectifier; + bindLabelToOffset(&rectifier, argumentsRectifierOffset_); + masm.call(&rectifier); + masm.jump(&restoreRealm); + } + masm.bind(&noUnderflow); + masm.callJit(temp1); + + masm.bind(&restoreRealm); + Address calleeToken(FramePointer, + TrampolineNativeFrameLayout::offsetOfCalleeToken()); + masm.loadFunctionFromCalleeToken(calleeToken, temp0); + masm.switchToObjectRealm(temp0, temp1); + } + + // Store the comparator's return value. + masm.bind(&callDone); + masm.storeValue(JSReturnOperand, Address(FramePointer, RvalOffset)); + + // Call ArraySortData::sortWithComparator. + using Fn2 = ArraySortResult (*)(ArraySortData* data); + masm.moveStackPtrTo(temp2); + masm.loadJSContext(temp0); + pushExitFrame(temp0, temp1); + masm.setupAlignedABICall(); + masm.passABIArg(temp2); + masm.callWithABI<Fn2, ArraySortData::sortWithComparator>( + ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); + + // Check return value. + masm.bind(&checkReturnValue); + masm.branch32(Assembler::Equal, ReturnReg, + Imm32(int32_t(ArraySortResult::Failure)), masm.failureLabel()); + masm.freeStack(ExitFrameLayout::SizeWithFooter()); + masm.branch32(Assembler::Equal, ReturnReg, + Imm32(int32_t(ArraySortResult::CallJSSameRealmNoRectifier)), + &jitCallFast); + masm.branch32(Assembler::Equal, ReturnReg, + Imm32(int32_t(ArraySortResult::CallJS)), &jitCallSlow); +#ifdef DEBUG + Label ok; + masm.branch32(Assembler::Equal, ReturnReg, + Imm32(int32_t(ArraySortResult::Done)), &ok); + masm.assumeUnreachable("Unexpected return value"); + masm.bind(&ok); +#endif + + masm.loadValue(Address(FramePointer, RvalOffset), JSReturnOperand); + masm.moveToStackPtr(FramePointer); + masm.pop(FramePointer); + masm.ret(); + + return offset; +} + +void JitRuntime::generateTrampolineNatives( + MacroAssembler& masm, TrampolineNativeJitEntryOffsets& offsets, + PerfSpewerRangeRecorder& rangeRecorder) { + offsets[TrampolineNative::ArraySort] = generateArraySortTrampoline(masm); + rangeRecorder.recordOffset("Trampoline: ArraySort"); +} + +bool jit::CallTrampolineNativeJitCode(JSContext* cx, TrampolineNative native, + CallArgs& args) { + // Use the EnterJit trampoline to enter the native's trampoline code. + + AutoCheckRecursionLimit recursion(cx); + if (!recursion.check(cx)) { + return false; + } + + MOZ_ASSERT(!args.isConstructing()); + CalleeToken calleeToken = CalleeToToken(&args.callee().as<JSFunction>(), + /* constructing = */ false); + + Value* maxArgv = args.array() - 1; // -1 to include |this| + size_t maxArgc = args.length() + 1; + + Rooted<Value> result(cx, Int32Value(args.length())); + + AssertRealmUnchanged aru(cx); + ActivationEntryMonitor entryMonitor(cx, calleeToken); + JitActivation activation(cx); + + EnterJitCode enter = cx->runtime()->jitRuntime()->enterJit(); + void* code = *cx->runtime()->jitRuntime()->trampolineNativeJitEntry(native); + + CALL_GENERATED_CODE(enter, code, maxArgc, maxArgv, /* osrFrame = */ nullptr, + calleeToken, /* envChain = */ nullptr, + /* osrNumStackValues = */ 0, result.address()); + + // Ensure the counter was reset to zero after exiting from JIT code. + MOZ_ASSERT(!cx->isInUnsafeRegion()); + + // Release temporary buffer used for OSR into Ion. + cx->runtime()->jitRuntime()->freeIonOsrTempData(); + + if (result.isMagic()) { + MOZ_ASSERT(result.isMagic(JS_ION_ERROR)); + return false; + } + + args.rval().set(result); + return true; +} |