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/. */
7 #include "jit/TrampolineNatives.h"
9 #include "jit/CalleeToken.h"
11 #include "jit/JitCommon.h"
12 #include "jit/JitRuntime.h"
13 #include "jit/MacroAssembler.h"
14 #include "jit/PerfSpewer.h"
15 #include "js/CallArgs.h"
16 #include "js/experimental/JitInfo.h"
18 #include "jit/MacroAssembler-inl.h"
19 #include "vm/Activation-inl.h"
22 using namespace js::jit
;
24 #define ADD_NATIVE(native) \
25 const JSJitInfo js::jit::JitInfo_##native{ \
27 {uint16_t(TrampolineNative::native)}, \
29 JSJitInfo::TrampolineNative};
30 TRAMPOLINE_NATIVE_LIST(ADD_NATIVE
)
33 void js::jit::SetTrampolineNativeJitEntry(JSContext
* cx
, JSFunction
* fun
,
34 TrampolineNative native
) {
35 if (!cx
->runtime()->jitRuntime()) {
36 // No JIT support so there's no trampoline.
39 void** entry
= cx
->runtime()->jitRuntime()->trampolineNativeJitEntry(native
);
42 fun
->setTrampolineNativeJitEntry(entry
);
45 uint32_t JitRuntime::generateArraySortTrampoline(MacroAssembler
& masm
) {
46 AutoCreatedBy
acb(masm
, "JitRuntime::generateArraySortTrampoline");
48 const uint32_t offset
= startTrampolineCode(masm
);
50 // The stack for the trampoline frame will look like this:
52 // [TrampolineNativeFrameLayout]
53 // * this and arguments passed by the caller
57 // * Saved frame pointer <= FramePointer
60 // * Comparator this + argument Values --+ -> comparator JitFrameLayout
61 // * Comparator (CalleeToken) |
62 // * Descriptor ----+ <= StackPointer
64 // The call to the comparator pushes the return address and the frame pointer,
65 // so we check the alignment after pushing these two pointers.
66 constexpr size_t FrameSize
= sizeof(ArraySortData
);
67 constexpr size_t PushedByCall
= 2 * sizeof(void*);
68 static_assert((FrameSize
+ PushedByCall
) % JitStackAlignment
== 0);
70 // Assert ArraySortData comparator data matches JitFrameLayout.
71 static_assert(PushedByCall
+ ArraySortData::offsetOfDescriptor() ==
72 JitFrameLayout::offsetOfDescriptor());
73 static_assert(PushedByCall
+ ArraySortData::offsetOfComparator() ==
74 JitFrameLayout::offsetOfCalleeToken());
75 static_assert(PushedByCall
+ ArraySortData::offsetOfComparatorThis() ==
76 JitFrameLayout::offsetOfThis());
77 static_assert(PushedByCall
+ ArraySortData::offsetOfComparatorArgs() ==
78 JitFrameLayout::offsetOfActualArgs());
79 static_assert(CalleeToken_Function
== 0,
80 "JSFunction* is valid CalleeToken for non-constructor calls");
82 // Compute offsets from FramePointer.
83 constexpr int32_t ComparatorOffset
=
84 -int32_t(FrameSize
) + ArraySortData::offsetOfComparator();
85 constexpr int32_t RvalOffset
=
86 -int32_t(FrameSize
) + ArraySortData::offsetOfComparatorReturnValue();
87 constexpr int32_t DescriptorOffset
=
88 -int32_t(FrameSize
) + ArraySortData::offsetOfDescriptor();
90 #ifdef JS_USE_LINK_REGISTER
91 masm
.pushReturnAddress();
93 masm
.push(FramePointer
);
94 masm
.moveStackPtrTo(FramePointer
);
96 AllocatableGeneralRegisterSet
regs(GeneralRegisterSet::All());
97 regs
.takeUnchecked(ReturnReg
);
98 regs
.takeUnchecked(JSReturnOperand
);
99 Register temp0
= regs
.takeAny();
100 Register temp1
= regs
.takeAny();
101 Register temp2
= regs
.takeAny();
103 // Reserve space and check alignment of the comparator frame.
104 masm
.reserveStack(FrameSize
);
105 masm
.assertStackAlignment(JitStackAlignment
, PushedByCall
);
107 // Trampoline control flow looks like this:
109 // call ArraySortFromJit
110 // goto checkReturnValue
113 // call ArraySortData::sortWithComparator
115 // check return value, jump to call_comparator if needed
118 auto pushExitFrame
= [&](Register cxReg
, Register scratchReg
) {
119 MOZ_ASSERT(masm
.framePushed() == FrameSize
);
120 masm
.PushFrameDescriptor(FrameType::TrampolineNative
);
121 masm
.Push(ImmWord(0)); // Fake return address.
122 masm
.Push(FramePointer
);
123 masm
.enterFakeExitFrame(cxReg
, scratchReg
, ExitFrameType::Bare
);
126 // Call ArraySortFromJit.
127 using Fn1
= ArraySortResult (*)(JSContext
* cx
,
128 jit::TrampolineNativeFrameLayout
* frame
);
129 masm
.loadJSContext(temp0
);
130 pushExitFrame(temp0
, temp1
);
131 masm
.setupAlignedABICall();
132 masm
.passABIArg(temp0
);
133 masm
.passABIArg(FramePointer
);
134 masm
.callWithABI
<Fn1
, ArraySortFromJit
>(
135 ABIType::General
, CheckUnsafeCallWithABI::DontCheckHasExitFrame
);
137 // Check return value.
138 Label checkReturnValue
;
139 masm
.jump(&checkReturnValue
);
140 masm
.setFramePushed(FrameSize
);
142 // Call the comparator. Store the frame descriptor before each call to ensure
143 // the HASCACHEDSAVEDFRAME_BIT flag from a previous call is cleared.
144 uintptr_t jitCallDescriptor
= MakeFrameDescriptorForJitCall(
145 jit::FrameType::TrampolineNative
, ArraySortData::ComparatorActualArgs
);
146 Label callDone
, jitCallFast
, jitCallSlow
;
147 masm
.bind(&jitCallFast
);
149 masm
.storePtr(ImmWord(jitCallDescriptor
),
150 Address(FramePointer
, DescriptorOffset
));
151 masm
.loadPtr(Address(FramePointer
, ComparatorOffset
), temp0
);
152 masm
.loadJitCodeRaw(temp0
, temp1
);
154 masm
.jump(&callDone
);
156 masm
.bind(&jitCallSlow
);
158 masm
.storePtr(ImmWord(jitCallDescriptor
),
159 Address(FramePointer
, DescriptorOffset
));
160 masm
.loadPtr(Address(FramePointer
, ComparatorOffset
), temp0
);
161 masm
.loadJitCodeRaw(temp0
, temp1
);
162 masm
.switchToObjectRealm(temp0
, temp2
);
164 // Handle arguments underflow.
165 Label noUnderflow
, restoreRealm
;
166 masm
.loadFunctionArgCount(temp0
, temp0
);
167 masm
.branch32(Assembler::BelowOrEqual
, temp0
,
168 Imm32(ArraySortData::ComparatorActualArgs
), &noUnderflow
);
171 bindLabelToOffset(&rectifier
, argumentsRectifierOffset_
);
172 masm
.call(&rectifier
);
173 masm
.jump(&restoreRealm
);
175 masm
.bind(&noUnderflow
);
178 masm
.bind(&restoreRealm
);
179 Address
calleeToken(FramePointer
,
180 TrampolineNativeFrameLayout::offsetOfCalleeToken());
181 masm
.loadFunctionFromCalleeToken(calleeToken
, temp0
);
182 masm
.switchToObjectRealm(temp0
, temp1
);
185 // Store the comparator's return value.
186 masm
.bind(&callDone
);
187 masm
.storeValue(JSReturnOperand
, Address(FramePointer
, RvalOffset
));
189 // Call ArraySortData::sortWithComparator.
190 using Fn2
= ArraySortResult (*)(ArraySortData
* data
);
191 masm
.moveStackPtrTo(temp2
);
192 masm
.loadJSContext(temp0
);
193 pushExitFrame(temp0
, temp1
);
194 masm
.setupAlignedABICall();
195 masm
.passABIArg(temp2
);
196 masm
.callWithABI
<Fn2
, ArraySortData::sortWithComparator
>(
197 ABIType::General
, CheckUnsafeCallWithABI::DontCheckHasExitFrame
);
199 // Check return value.
200 masm
.bind(&checkReturnValue
);
201 masm
.branch32(Assembler::Equal
, ReturnReg
,
202 Imm32(int32_t(ArraySortResult::Failure
)), masm
.failureLabel());
203 masm
.freeStack(ExitFrameLayout::SizeWithFooter());
204 masm
.branch32(Assembler::Equal
, ReturnReg
,
205 Imm32(int32_t(ArraySortResult::CallJSSameRealmNoRectifier
)),
207 masm
.branch32(Assembler::Equal
, ReturnReg
,
208 Imm32(int32_t(ArraySortResult::CallJS
)), &jitCallSlow
);
211 masm
.branch32(Assembler::Equal
, ReturnReg
,
212 Imm32(int32_t(ArraySortResult::Done
)), &ok
);
213 masm
.assumeUnreachable("Unexpected return value");
217 masm
.loadValue(Address(FramePointer
, RvalOffset
), JSReturnOperand
);
218 masm
.moveToStackPtr(FramePointer
);
219 masm
.pop(FramePointer
);
225 void JitRuntime::generateTrampolineNatives(
226 MacroAssembler
& masm
, TrampolineNativeJitEntryOffsets
& offsets
,
227 PerfSpewerRangeRecorder
& rangeRecorder
) {
228 offsets
[TrampolineNative::ArraySort
] = generateArraySortTrampoline(masm
);
229 rangeRecorder
.recordOffset("Trampoline: ArraySort");
232 bool jit::CallTrampolineNativeJitCode(JSContext
* cx
, TrampolineNative native
,
234 // Use the EnterJit trampoline to enter the native's trampoline code.
236 AutoCheckRecursionLimit
recursion(cx
);
237 if (!recursion
.check(cx
)) {
241 MOZ_ASSERT(!args
.isConstructing());
242 CalleeToken calleeToken
= CalleeToToken(&args
.callee().as
<JSFunction
>(),
243 /* constructing = */ false);
245 Value
* maxArgv
= args
.array() - 1; // -1 to include |this|
246 size_t maxArgc
= args
.length() + 1;
248 Rooted
<Value
> result(cx
, Int32Value(args
.length()));
250 AssertRealmUnchanged
aru(cx
);
251 ActivationEntryMonitor
entryMonitor(cx
, calleeToken
);
252 JitActivation
activation(cx
);
254 EnterJitCode enter
= cx
->runtime()->jitRuntime()->enterJit();
255 void* code
= *cx
->runtime()->jitRuntime()->trampolineNativeJitEntry(native
);
257 CALL_GENERATED_CODE(enter
, code
, maxArgc
, maxArgv
, /* osrFrame = */ nullptr,
258 calleeToken
, /* envChain = */ nullptr,
259 /* osrNumStackValues = */ 0, result
.address());
261 // Ensure the counter was reset to zero after exiting from JIT code.
262 MOZ_ASSERT(!cx
->isInUnsafeRegion());
264 // Release temporary buffer used for OSR into Ion.
265 cx
->runtime()->jitRuntime()->freeIonOsrTempData();
267 if (result
.isMagic()) {
268 MOZ_ASSERT(result
.isMagic(JS_ION_ERROR
));
272 args
.rval().set(result
);