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 #ifndef jit_WarpBuilderShared_h
8 #define jit_WarpBuilderShared_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
13 #include "jit/MIRGraph.h"
18 class BytecodeLocation
;
29 class WrappedFunction
;
31 // Helper class to manage call state.
32 class MOZ_STACK_CLASS CallInfo
{
33 MDefinition
* callee_
= nullptr;
34 MDefinition
* thisArg_
= nullptr;
35 MDefinition
* newTargetArg_
= nullptr;
36 MDefinitionVector args_
;
40 // True if the caller does not use the return value.
41 bool ignoresReturnValue_
;
43 bool inlined_
= false;
47 // For normal calls and FunCall we can shuffle around definitions in
48 // the CallInfo and use a normal MCall. For others, we need to use a
50 enum class ArgFormat
{
57 ArgFormat argFormat_
= ArgFormat::Standard
;
58 mozilla::Maybe
<ResumeMode
> inliningMode_
;
61 CallInfo(TempAllocator
& alloc
, bool constructing
, bool ignoresReturnValue
,
62 jsbytecode
* pc
= nullptr)
64 constructing_(constructing
),
65 ignoresReturnValue_(ignoresReturnValue
) {}
67 [[nodiscard
]] bool init(MBasicBlock
* current
, uint32_t argc
) {
68 MOZ_ASSERT(args_
.empty());
70 // Get the arguments in the right order
71 if (!args_
.reserve(argc
)) {
76 setNewTarget(current
->pop());
79 for (int32_t i
= argc
; i
> 0; i
--) {
80 args_
.infallibleAppend(current
->peek(-i
));
84 // Get |this| and |callee|
85 setThis(current
->pop());
86 setCallee(current
->pop());
91 void initForSpreadCall(MBasicBlock
* current
) {
92 MOZ_ASSERT(args_
.empty());
95 setNewTarget(current
->pop());
98 // Spread calls have one argument, an Array object containing the args.
99 static_assert(decltype(args_
)::InlineLength
>= 1,
100 "Appending one argument should be infallible");
101 MOZ_ALWAYS_TRUE(args_
.append(current
->pop()));
103 // Get |this| and |callee|
104 setThis(current
->pop());
105 setCallee(current
->pop());
107 argFormat_
= ArgFormat::Array
;
110 void initForGetterCall(MDefinition
* callee
, MDefinition
* thisVal
) {
111 MOZ_ASSERT(args_
.empty());
116 void initForProxyGet(MDefinition
* callee
, MDefinition
* handler
,
117 MDefinition
* target
, MDefinition
* id
,
118 MDefinition
* receiver
) {
119 MOZ_ASSERT(args_
.empty());
122 static_assert(decltype(args_
)::InlineLength
>= 3,
123 "Appending three arguments should be infallible");
124 MOZ_ALWAYS_TRUE(args_
.append(target
));
125 MOZ_ALWAYS_TRUE(args_
.append(id
));
126 MOZ_ALWAYS_TRUE(args_
.append(receiver
));
129 void initForSetterCall(MDefinition
* callee
, MDefinition
* thisVal
,
131 MOZ_ASSERT(args_
.empty());
135 static_assert(decltype(args_
)::InlineLength
>= 1,
136 "Appending one argument should be infallible");
137 MOZ_ALWAYS_TRUE(args_
.append(rhs
));
140 void initForApplyInlinedArgs(MDefinition
* callee
, MDefinition
* thisVal
,
141 uint32_t numActuals
) {
142 MOZ_ASSERT(args_
.empty());
143 MOZ_ASSERT(!constructing_
);
148 MOZ_ASSERT(numActuals
<= ArgumentsObject::MaxInlinedArgs
);
150 ArgumentsObject::MaxInlinedArgs
<= decltype(args_
)::InlineLength
,
151 "Actual arguments can be infallibly stored inline");
152 MOZ_ALWAYS_TRUE(args_
.reserve(numActuals
));
155 [[nodiscard
]] bool initForApplyArray(MDefinition
* callee
,
156 MDefinition
* thisVal
,
157 uint32_t numActuals
) {
158 MOZ_ASSERT(args_
.empty());
159 MOZ_ASSERT(!constructing_
);
164 return args_
.reserve(numActuals
);
167 [[nodiscard
]] bool initForConstructArray(MDefinition
* callee
,
168 MDefinition
* thisVal
,
169 MDefinition
* newTarget
,
170 uint32_t numActuals
) {
171 MOZ_ASSERT(args_
.empty());
172 MOZ_ASSERT(constructing_
);
176 setNewTarget(newTarget
);
178 return args_
.reserve(numActuals
);
181 void initForCloseIter(MDefinition
* iter
, MDefinition
* callee
) {
182 MOZ_ASSERT(args_
.empty());
187 void popCallStack(MBasicBlock
* current
) { current
->popn(numFormals()); }
189 [[nodiscard
]] bool pushCallStack(MBasicBlock
* current
) {
190 current
->push(callee());
191 current
->push(thisArg());
193 for (uint32_t i
= 0; i
< argc(); i
++) {
194 current
->push(getArg(i
));
197 if (constructing()) {
198 current
->push(getNewTarget());
204 uint32_t argc() const { return args_
.length(); }
205 uint32_t numFormals() const { return argc() + 2 + constructing(); }
207 [[nodiscard
]] bool setArgs(const MDefinitionVector
& args
) {
208 MOZ_ASSERT(args_
.empty());
209 return args_
.appendAll(args
);
212 MDefinitionVector
& argv() { return args_
; }
214 const MDefinitionVector
& argv() const { return args_
; }
216 MDefinition
* getArg(uint32_t i
) const {
217 MOZ_ASSERT(i
< argc());
221 void initArg(uint32_t i
, MDefinition
* def
) {
222 MOZ_ASSERT(i
== argc());
223 args_
.infallibleAppend(def
);
226 void setArg(uint32_t i
, MDefinition
* def
) {
227 MOZ_ASSERT(i
< argc());
231 void removeArg(uint32_t i
) { args_
.erase(&args_
[i
]); }
233 MDefinition
* thisArg() const {
234 MOZ_ASSERT(thisArg_
);
238 void setThis(MDefinition
* thisArg
) { thisArg_
= thisArg
; }
240 bool constructing() const { return constructing_
; }
242 bool ignoresReturnValue() const { return ignoresReturnValue_
; }
244 void setNewTarget(MDefinition
* newTarget
) {
245 MOZ_ASSERT(constructing());
246 newTargetArg_
= newTarget
;
248 MDefinition
* getNewTarget() const {
249 MOZ_ASSERT(newTargetArg_
);
250 return newTargetArg_
;
253 bool isSetter() const { return setter_
; }
254 void markAsSetter() { setter_
= true; }
256 bool isInlined() const { return inlined_
; }
257 void markAsInlined() { inlined_
= true; }
259 ResumeMode
inliningResumeMode() const {
260 MOZ_ASSERT(isInlined());
261 return *inliningMode_
;
264 void setInliningResumeMode(ResumeMode mode
) {
265 MOZ_ASSERT(isInlined());
266 MOZ_ASSERT(inliningMode_
.isNothing());
267 inliningMode_
.emplace(mode
);
270 MDefinition
* callee() const {
275 void setCallee(MDefinition
* callee
) { callee_
= callee
; }
277 template <typename Fun
>
278 void forEachCallOperand(Fun
& f
) {
284 for (uint32_t i
= 0; i
< argc(); i
++) {
289 // Prepend `numArgs` arguments. Calls `f(i)` for each new argument.
290 template <typename Fun
>
291 [[nodiscard
]] bool prependArgs(size_t numArgs
, const Fun
& f
) {
292 size_t numArgsBefore
= args_
.length();
293 if (!args_
.growBy(numArgs
)) {
296 for (size_t i
= numArgsBefore
; i
> 0; i
--) {
297 args_
[numArgs
+ i
- 1] = args_
[i
- 1];
299 for (size_t i
= 0; i
< numArgs
; i
++) {
305 void setImplicitlyUsedUnchecked() {
306 auto setFlag
= [](MDefinition
* def
) { def
->setImplicitlyUsedUnchecked(); };
307 forEachCallOperand(setFlag
);
310 ArgFormat
argFormat() const { return argFormat_
; }
311 void setArgFormat(ArgFormat argFormat
) { argFormat_
= argFormat
; }
313 MDefinition
* arrayArg() const {
314 MOZ_ASSERT(argFormat_
== ArgFormat::Array
);
315 // The array argument for a spread call or FunApply is always the last
317 return getArg(argc() - 1);
321 template <typename Undef
>
322 MCall
* MakeCall(TempAllocator
& alloc
, Undef addUndefined
, CallInfo
& callInfo
,
323 bool needsThisCheck
, WrappedFunction
* target
, bool isDOMCall
) {
324 MOZ_ASSERT(callInfo
.argFormat() == CallInfo::ArgFormat::Standard
);
325 MOZ_ASSERT_IF(needsThisCheck
, !target
);
326 MOZ_ASSERT_IF(isDOMCall
, target
->jitInfo()->type() == JSJitInfo::Method
);
328 mozilla::Maybe
<DOMObjectKind
> objKind
;
330 const Shape
* shape
= callInfo
.thisArg()->toGuardShape()->shape();
331 MOZ_ASSERT(shape
->getObjectClass()->isDOMClass());
332 if (shape
->isNative()) {
333 objKind
.emplace(DOMObjectKind::Native
);
335 MOZ_ASSERT(shape
->isProxy());
336 objKind
.emplace(DOMObjectKind::Proxy
);
340 uint32_t targetArgs
= callInfo
.argc();
342 // Collect number of missing arguments provided that the target is
343 // scripted. Native functions are passed an explicit 'argc' parameter.
344 if (target
&& target
->hasJitEntry()) {
345 targetArgs
= std::max
<uint32_t>(target
->nargs(), callInfo
.argc());
349 MCall::New(alloc
, target
, targetArgs
+ 1 + callInfo
.constructing(),
350 callInfo
.argc(), callInfo
.constructing(),
351 callInfo
.ignoresReturnValue(), isDOMCall
, objKind
);
356 if (callInfo
.constructing()) {
357 // Note: setThis should have been done by the caller of makeCall.
358 if (needsThisCheck
) {
359 call
->setNeedsThisCheck();
363 call
->addArg(targetArgs
+ 1, callInfo
.getNewTarget());
366 // Explicitly pad any missing arguments with |undefined|.
367 // This permits skipping the argumentsRectifier.
368 MOZ_ASSERT_IF(target
&& targetArgs
> callInfo
.argc(), target
->hasJitEntry());
370 MConstant
* undef
= nullptr;
371 for (uint32_t i
= targetArgs
; i
> callInfo
.argc(); i
--) {
373 undef
= addUndefined();
375 if (!alloc
.ensureBallast()) {
378 call
->addArg(i
, undef
);
381 // Add explicit arguments.
382 // Skip addArg(0) because it is reserved for |this|.
383 for (int32_t i
= callInfo
.argc() - 1; i
>= 0; i
--) {
384 call
->addArg(i
+ 1, callInfo
.getArg(i
));
388 // Now that we've told it about all the args, compute whether it's movable
389 call
->computeMovable();
392 // Pass |this| and callee.
393 call
->addArg(0, callInfo
.thisArg());
394 call
->initCallee(callInfo
.callee());
397 // The callee must be a JSFunction so we don't need a Class check.
398 call
->disableClassCheck();
404 // Base class for code sharing between WarpBuilder and WarpCacheIRTranspiler.
405 // Because this code is used by WarpCacheIRTranspiler we should
406 // generally assume that we only have access to the current basic block.
407 class WarpBuilderShared
{
408 WarpSnapshot
& snapshot_
;
409 MIRGenerator
& mirGen_
;
410 TempAllocator
& alloc_
;
413 MBasicBlock
* current
;
415 WarpBuilderShared(WarpSnapshot
& snapshot
, MIRGenerator
& mirGen
,
416 MBasicBlock
* current_
);
418 [[nodiscard
]] bool resumeAfter(MInstruction
* ins
, BytecodeLocation loc
);
420 MConstant
* constant(const JS::Value
& v
);
421 void pushConstant(const JS::Value
& v
);
423 // Note: unboxObjectInfallible defaults to adding a non-movable MUnbox to
424 // ensure we don't hoist the infallible unbox before a branch checking the
426 enum class IsMovable
: bool { No
, Yes
};
427 MDefinition
* unboxObjectInfallible(MDefinition
* def
,
428 IsMovable movable
= IsMovable::No
);
430 MCall
* makeCall(CallInfo
& callInfo
, bool needsThisCheck
,
431 WrappedFunction
* target
= nullptr, bool isDOMCall
= false);
432 MInstruction
* makeSpreadCall(CallInfo
& callInfo
, bool needsThisCheck
,
433 bool isSameRealm
= false,
434 WrappedFunction
* target
= nullptr);
437 MBasicBlock
* currentBlock() const { return current
; }
438 WarpSnapshot
& snapshot() const { return snapshot_
; }
439 MIRGenerator
& mirGen() { return mirGen_
; }
440 TempAllocator
& alloc() { return alloc_
; }