Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / jit / WarpBuilderShared.h
blob651164865dbdb3a3b63a02d5814adbea88b2cd93
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"
14 #include "js/Value.h"
16 namespace js {
18 class BytecodeLocation;
20 namespace jit {
22 class MBasicBlock;
23 class MCall;
24 class MConstant;
25 class MInstruction;
26 class MIRGenerator;
27 class TempAllocator;
28 class WarpSnapshot;
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_;
38 bool constructing_;
40 // True if the caller does not use the return value.
41 bool ignoresReturnValue_;
43 bool inlined_ = false;
44 bool setter_ = false;
46 public:
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
49 // specialized call.
50 enum class ArgFormat {
51 Standard,
52 Array,
53 FunApplyArgsObj,
56 private:
57 ArgFormat argFormat_ = ArgFormat::Standard;
58 mozilla::Maybe<ResumeMode> inliningMode_;
60 public:
61 CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue,
62 jsbytecode* pc = nullptr)
63 : args_(alloc),
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)) {
72 return false;
75 if (constructing()) {
76 setNewTarget(current->pop());
79 for (int32_t i = argc; i > 0; i--) {
80 args_.infallibleAppend(current->peek(-i));
82 current->popn(argc);
84 // Get |this| and |callee|
85 setThis(current->pop());
86 setCallee(current->pop());
88 return true;
91 void initForSpreadCall(MBasicBlock* current) {
92 MOZ_ASSERT(args_.empty());
94 if (constructing()) {
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());
112 setCallee(callee);
113 setThis(thisVal);
116 void initForProxyGet(MDefinition* callee, MDefinition* handler,
117 MDefinition* target, MDefinition* id,
118 MDefinition* receiver) {
119 MOZ_ASSERT(args_.empty());
120 setCallee(callee);
121 setThis(handler);
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,
130 MDefinition* rhs) {
131 MOZ_ASSERT(args_.empty());
132 markAsSetter();
133 setCallee(callee);
134 setThis(thisVal);
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_);
145 setCallee(callee);
146 setThis(thisVal);
148 MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs);
149 static_assert(
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_);
161 setCallee(callee);
162 setThis(thisVal);
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_);
174 setCallee(callee);
175 setThis(thisVal);
176 setNewTarget(newTarget);
178 return args_.reserve(numActuals);
181 void initForCloseIter(MDefinition* iter, MDefinition* callee) {
182 MOZ_ASSERT(args_.empty());
183 setCallee(callee);
184 setThis(iter);
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());
201 return true;
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());
218 return args_[i];
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());
228 args_[i] = def;
231 void removeArg(uint32_t i) { args_.erase(&args_[i]); }
233 MDefinition* thisArg() const {
234 MOZ_ASSERT(thisArg_);
235 return 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 {
271 MOZ_ASSERT(callee_);
272 return callee_;
275 void setCallee(MDefinition* callee) { callee_ = callee; }
277 template <typename Fun>
278 void forEachCallOperand(Fun& f) {
279 f(callee_);
280 f(thisArg_);
281 if (newTargetArg_) {
282 f(newTargetArg_);
284 for (uint32_t i = 0; i < argc(); i++) {
285 f(getArg(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)) {
294 return false;
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++) {
300 args_[i] = f(i);
302 return true;
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
316 // argument.
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;
329 if (isDOMCall) {
330 const Shape* shape = callInfo.thisArg()->toGuardShape()->shape();
331 MOZ_ASSERT(shape->getObjectClass()->isDOMClass());
332 if (shape->isNative()) {
333 objKind.emplace(DOMObjectKind::Native);
334 } else {
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());
348 MCall* call =
349 MCall::New(alloc, target, targetArgs + 1 + callInfo.constructing(),
350 callInfo.argc(), callInfo.constructing(),
351 callInfo.ignoresReturnValue(), isDOMCall, objKind);
352 if (!call) {
353 return nullptr;
356 if (callInfo.constructing()) {
357 // Note: setThis should have been done by the caller of makeCall.
358 if (needsThisCheck) {
359 call->setNeedsThisCheck();
362 // Pass |new.target|
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--) {
372 if (!undef) {
373 undef = addUndefined();
375 if (!alloc.ensureBallast()) {
376 return nullptr;
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));
387 if (isDOMCall) {
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());
396 if (target) {
397 // The callee must be a JSFunction so we don't need a Class check.
398 call->disableClassCheck();
401 return call;
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_;
412 protected:
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
425 // value type.
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);
436 public:
437 MBasicBlock* currentBlock() const { return current; }
438 WarpSnapshot& snapshot() const { return snapshot_; }
439 MIRGenerator& mirGen() { return mirGen_; }
440 TempAllocator& alloc() { return alloc_; }
443 } // namespace jit
444 } // namespace js
446 #endif