Bug 1890513: Directly invoke variadic native functions. r=jandem
[gecko.git] / js / src / jit / TrampolineNatives.cpp
blob0bde6d9985c9e216960f079ecdc1d5a31a446420
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"
10 #include "jit/Ion.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"
21 using namespace js;
22 using namespace js::jit;
24 #define ADD_NATIVE(native) \
25 const JSJitInfo js::jit::JitInfo_##native{ \
26 {nullptr}, \
27 {uint16_t(TrampolineNative::native)}, \
28 {0}, \
29 JSJitInfo::TrampolineNative};
30 TRAMPOLINE_NATIVE_LIST(ADD_NATIVE)
31 #undef 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.
37 return;
39 void** entry = cx->runtime()->jitRuntime()->trampolineNativeJitEntry(native);
40 MOZ_ASSERT(entry);
41 MOZ_ASSERT(*entry);
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
54 // * CalleeToken
55 // * Descriptor
56 // * Return Address
57 // * Saved frame pointer <= FramePointer
58 // [ArraySortData]
59 // * ...
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();
92 #endif
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
111 // call_comparator:
112 // call comparator
113 // call ArraySortData::sortWithComparator
114 // checkReturnValue:
115 // check return value, jump to call_comparator if needed
116 // return rval
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);
153 masm.callJit(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);
170 Label rectifier;
171 bindLabelToOffset(&rectifier, argumentsRectifierOffset_);
172 masm.call(&rectifier);
173 masm.jump(&restoreRealm);
175 masm.bind(&noUnderflow);
176 masm.callJit(temp1);
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)),
206 &jitCallFast);
207 masm.branch32(Assembler::Equal, ReturnReg,
208 Imm32(int32_t(ArraySortResult::CallJS)), &jitCallSlow);
209 #ifdef DEBUG
210 Label ok;
211 masm.branch32(Assembler::Equal, ReturnReg,
212 Imm32(int32_t(ArraySortResult::Done)), &ok);
213 masm.assumeUnreachable("Unexpected return value");
214 masm.bind(&ok);
215 #endif
217 masm.loadValue(Address(FramePointer, RvalOffset), JSReturnOperand);
218 masm.moveToStackPtr(FramePointer);
219 masm.pop(FramePointer);
220 masm.ret();
222 return offset;
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,
233 CallArgs& args) {
234 // Use the EnterJit trampoline to enter the native's trampoline code.
236 AutoCheckRecursionLimit recursion(cx);
237 if (!recursion.check(cx)) {
238 return false;
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));
269 return false;
272 args.rval().set(result);
273 return true;