Bug 1874684 - Part 31: Correctly reject invalid durations in some RoundDuration calls...
[gecko.git] / js / src / jit / WarpBuilder.h
blob587a6c4837849889a103e5ebd5759f0a57c99ec4
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_WarpBuilder_h
8 #define jit_WarpBuilder_h
10 #include <initializer_list>
12 #include "ds/InlineTable.h"
13 #include "jit/JitContext.h"
14 #include "jit/MIR.h"
15 #include "jit/WarpBuilderShared.h"
16 #include "jit/WarpSnapshot.h"
17 #include "vm/Opcodes.h"
19 namespace js {
20 namespace jit {
22 // JSOps not yet supported by WarpBuilder. See warning at the end of the list.
23 #define WARP_UNSUPPORTED_OPCODE_LIST(_) \
24 /* Intentionally not implemented */ \
25 _(ForceInterpreter) \
26 /* With */ \
27 _(EnterWith) \
28 _(LeaveWith) \
29 /* Eval */ \
30 _(Eval) \
31 _(StrictEval) \
32 _(SpreadEval) \
33 _(StrictSpreadEval) \
34 /* Super */ \
35 _(SetPropSuper) \
36 _(SetElemSuper) \
37 _(StrictSetPropSuper) \
38 _(StrictSetElemSuper) \
39 /* Compound assignment */ \
40 _(GetBoundName) \
41 /* Generators / Async (bug 1317690) */ \
42 _(IsGenClosing) \
43 _(Resume) \
44 /* Misc */ \
45 _(DelName) \
46 _(SetIntrinsic) \
47 /* Private Fields */ \
48 _(GetAliasedDebugVar) \
49 /* Non-syntactic scope */ \
50 _(NonSyntacticGlobalThis) \
51 /* Records and Tuples */ \
52 IF_RECORD_TUPLE(_(InitRecord)) \
53 IF_RECORD_TUPLE(_(AddRecordProperty)) \
54 IF_RECORD_TUPLE(_(AddRecordSpread)) \
55 IF_RECORD_TUPLE(_(FinishRecord)) \
56 IF_RECORD_TUPLE(_(InitTuple)) \
57 IF_RECORD_TUPLE(_(AddTupleElement)) \
58 IF_RECORD_TUPLE(_(FinishTuple)) \
59 // === !! WARNING WARNING WARNING !! ===
60 // Do you really want to sacrifice performance by not implementing this
61 // operation in the optimizing compiler?
63 class MIRGenerator;
64 class MIRGraph;
65 class WarpSnapshot;
67 enum class CacheKind : uint8_t;
69 // [SMDOC] Control Flow handling in WarpBuilder.
71 // WarpBuilder traverses the script's bytecode and compiles each instruction to
72 // corresponding MIR instructions. Handling control flow bytecode ops requires
73 // some special machinery:
75 // Forward branches
76 // ----------------
77 // Most branches in the bytecode are forward branches to a JSOp::JumpTarget
78 // instruction that we have not inspected yet. We compile them in two phases:
80 // 1) When compiling the source instruction: the MBasicBlock is terminated
81 // with a control instruction that has a nullptr successor block. We also add
82 // a PendingEdge instance to the PendingEdges list for the target bytecode
83 // location.
85 // 2) When finally compiling the JSOp::JumpTarget: WarpBuilder::build_JumpTarget
86 // creates the target block and uses the list of PendingEdges to 'link' the
87 // blocks.
89 // Loops
90 // -----
91 // Loops may be nested within other loops, so each WarpBuilder has a LoopState
92 // stack. This is used to link the backedge to the loop's header block.
94 // Unreachable/dead code
95 // ---------------------
96 // Some bytecode instructions never fall through to the next instruction, for
97 // example JSOp::Return, JSOp::Goto, or JSOp::Throw. Code after such
98 // instructions is guaranteed to be dead so WarpBuilder skips it until it gets
99 // to a jump target instruction with pending edges.
101 // Note: The frontend may generate unnecessary JSOp::JumpTarget instructions we
102 // can ignore when they have no incoming pending edges.
104 // Try-catch
105 // ---------
106 // WarpBuilder supports scripts with try-catch by only compiling the try-block
107 // and bailing out (to the Baseline Interpreter) from the exception handler
108 // whenever we need to execute the catch-block.
110 // Because we don't compile the catch-block and the code after the try-catch may
111 // only be reachable via the catch-block, Baseline's BytecodeAnalysis ensures
112 // Baseline does not attempt OSR into Warp at loops that are only reachable via
113 // catch/finally blocks.
115 // Finally-blocks are compiled by WarpBuilder, but when we have to enter a
116 // finally-block from the exception handler, we bail out to the Baseline
117 // Interpreter.
119 // PendingEdge is used whenever a block is terminated with a forward branch in
120 // the bytecode. When we reach the jump target we use this information to link
121 // the block to the jump target's block.
122 class PendingEdge {
123 MBasicBlock* block_;
124 uint32_t successor_;
125 uint8_t numToPop_;
127 public:
128 PendingEdge(MBasicBlock* block, uint32_t successor, uint32_t numToPop)
129 : block_(block), successor_(successor), numToPop_(numToPop) {
130 MOZ_ASSERT(numToPop_ == numToPop, "value must fit in field");
133 MBasicBlock* block() const { return block_; }
134 uint32_t successor() const { return successor_; }
135 uint8_t numToPop() const { return numToPop_; }
138 // PendingEdgesMap maps a bytecode instruction to a Vector of PendingEdges
139 // targeting it. We use InlineMap<> for this because most of the time there are
140 // only a few pending edges but there can be many when switch-statements are
141 // involved.
142 using PendingEdges = Vector<PendingEdge, 2, SystemAllocPolicy>;
143 using PendingEdgesMap =
144 InlineMap<jsbytecode*, PendingEdges, 8, PointerHasher<jsbytecode*>,
145 SystemAllocPolicy>;
147 // LoopState stores information about a loop that's being compiled to MIR.
148 class LoopState {
149 MBasicBlock* header_ = nullptr;
151 public:
152 explicit LoopState(MBasicBlock* header) : header_(header) {}
154 MBasicBlock* header() const { return header_; }
156 using LoopStateStack = Vector<LoopState, 4, JitAllocPolicy>;
158 // Data that is shared across all WarpBuilders for a given compilation.
159 class MOZ_STACK_CLASS WarpCompilation {
160 // The total loop depth, including loops in the caller while
161 // compiling inlined functions.
162 uint32_t loopDepth_ = 0;
164 // Loop phis for iterators that need to be kept alive.
165 PhiVector iterators_;
167 public:
168 explicit WarpCompilation(TempAllocator& alloc) : iterators_(alloc) {}
170 uint32_t loopDepth() const { return loopDepth_; }
171 void incLoopDepth() { loopDepth_++; }
172 void decLoopDepth() {
173 MOZ_ASSERT(loopDepth() > 0);
174 loopDepth_--;
177 PhiVector* iterators() { return &iterators_; }
180 // WarpBuilder builds a MIR graph from WarpSnapshot. Unlike WarpOracle,
181 // WarpBuilder can run off-thread.
182 class MOZ_STACK_CLASS WarpBuilder : public WarpBuilderShared {
183 WarpCompilation* warpCompilation_;
184 MIRGraph& graph_;
185 const CompileInfo& info_;
186 const WarpScriptSnapshot* scriptSnapshot_;
187 JSScript* script_;
189 // Pointer to a WarpOpSnapshot or nullptr if we reached the end of the list.
190 // Because bytecode is compiled from first to last instruction (and
191 // WarpOpSnapshot is sorted the same way), the iterator always moves forward.
192 const WarpOpSnapshot* opSnapshotIter_ = nullptr;
194 // Note: loopStack_ is builder-specific. loopStack_.length is the
195 // depth relative to the current script. The overall loop depth is
196 // stored in the WarpCompilation.
197 LoopStateStack loopStack_;
198 PendingEdgesMap pendingEdges_;
200 // These are only initialized when building an inlined script.
201 WarpBuilder* callerBuilder_ = nullptr;
202 MResumePoint* callerResumePoint_ = nullptr;
203 CallInfo* inlineCallInfo_ = nullptr;
205 WarpCompilation* warpCompilation() const { return warpCompilation_; }
206 MIRGraph& graph() { return graph_; }
207 const WarpScriptSnapshot* scriptSnapshot() const { return scriptSnapshot_; }
209 uint32_t loopDepth() const { return warpCompilation_->loopDepth(); }
210 void incLoopDepth() { warpCompilation_->incLoopDepth(); }
211 void decLoopDepth() { warpCompilation_->decLoopDepth(); }
212 PhiVector* iterators() { return warpCompilation_->iterators(); }
214 WarpBuilder* callerBuilder() const { return callerBuilder_; }
215 MResumePoint* callerResumePoint() const { return callerResumePoint_; }
217 BytecodeSite* newBytecodeSite(BytecodeLocation loc);
219 const WarpOpSnapshot* getOpSnapshotImpl(BytecodeLocation loc,
220 WarpOpSnapshot::Kind kind);
222 template <typename T>
223 const T* getOpSnapshot(BytecodeLocation loc) {
224 const WarpOpSnapshot* snapshot = getOpSnapshotImpl(loc, T::ThisKind);
225 return snapshot ? snapshot->as<T>() : nullptr;
228 void initBlock(MBasicBlock* block);
229 [[nodiscard]] bool startNewEntryBlock(size_t stackDepth,
230 BytecodeLocation loc);
231 [[nodiscard]] bool startNewBlock(MBasicBlock* predecessor,
232 BytecodeLocation loc, size_t numToPop = 0);
233 [[nodiscard]] bool startNewLoopHeaderBlock(BytecodeLocation loopHead);
234 [[nodiscard]] bool startNewOsrPreHeaderBlock(BytecodeLocation loopHead);
236 bool hasTerminatedBlock() const { return current == nullptr; }
237 void setTerminatedBlock() { current = nullptr; }
239 [[nodiscard]] bool addPendingEdge(BytecodeLocation target, MBasicBlock* block,
240 uint32_t successor, uint32_t numToPop = 0);
241 [[nodiscard]] bool buildForwardGoto(BytecodeLocation target);
242 [[nodiscard]] bool buildBackedge();
243 [[nodiscard]] bool buildTestBackedge(BytecodeLocation loc);
245 [[nodiscard]] bool addIteratorLoopPhis(BytecodeLocation loopHead);
247 [[nodiscard]] bool buildPrologue();
248 [[nodiscard]] bool buildBody();
250 [[nodiscard]] bool buildInlinePrologue();
252 [[nodiscard]] bool buildIC(BytecodeLocation loc, CacheKind kind,
253 std::initializer_list<MDefinition*> inputs);
254 [[nodiscard]] bool buildBailoutForColdIC(BytecodeLocation loc,
255 CacheKind kind);
257 [[nodiscard]] bool buildEnvironmentChain();
258 MInstruction* buildNamedLambdaEnv(MDefinition* callee, MDefinition* env,
259 NamedLambdaObject* templateObj);
260 MInstruction* buildCallObject(MDefinition* callee, MDefinition* env,
261 CallObject* templateObj);
262 MInstruction* buildLoadSlot(MDefinition* obj, uint32_t numFixedSlots,
263 uint32_t slot);
265 MConstant* globalLexicalEnvConstant();
266 MDefinition* getCallee();
268 [[nodiscard]] bool buildUnaryOp(BytecodeLocation loc);
269 [[nodiscard]] bool buildBinaryOp(BytecodeLocation loc);
270 [[nodiscard]] bool buildCompareOp(BytecodeLocation loc);
271 [[nodiscard]] bool buildTestOp(BytecodeLocation loc);
272 [[nodiscard]] bool buildCallOp(BytecodeLocation loc);
274 [[nodiscard]] bool buildInitPropGetterSetterOp(BytecodeLocation loc);
275 [[nodiscard]] bool buildInitElemGetterSetterOp(BytecodeLocation loc);
277 [[nodiscard]] bool buildSuspend(BytecodeLocation loc, MDefinition* gen,
278 MDefinition* retVal);
280 void buildCheckLexicalOp(BytecodeLocation loc);
282 bool usesEnvironmentChain() const;
283 MDefinition* walkEnvironmentChain(uint32_t numHops);
285 void buildCreateThis(CallInfo& callInfo);
287 [[nodiscard]] bool transpileCall(BytecodeLocation loc,
288 const WarpCacheIR* cacheIRSnapshot,
289 CallInfo* callInfo);
291 [[nodiscard]] bool buildInlinedCall(BytecodeLocation loc,
292 const WarpInlinedCall* snapshot,
293 CallInfo& callInfo);
295 MDefinition* patchInlinedReturns(CompileInfo* calleeCompileInfo,
296 CallInfo& callInfo, MIRGraphReturns& exits,
297 MBasicBlock* returnBlock);
298 MDefinition* patchInlinedReturn(CompileInfo* calleeCompileInfo,
299 CallInfo& callInfo, MBasicBlock* exit,
300 MBasicBlock* returnBlock);
302 #define BUILD_OP(OP, ...) [[nodiscard]] bool build_##OP(BytecodeLocation loc);
303 FOR_EACH_OPCODE(BUILD_OP)
304 #undef BUILD_OP
306 public:
307 WarpBuilder(WarpSnapshot& snapshot, MIRGenerator& mirGen,
308 WarpCompilation* warpCompilation);
309 WarpBuilder(WarpBuilder* caller, WarpScriptSnapshot* snapshot,
310 CompileInfo& compileInfo, CallInfo* inlineCallInfo,
311 MResumePoint* callerResumePoint);
313 [[nodiscard]] bool build();
314 [[nodiscard]] bool buildInline();
316 const CompileInfo& info() const { return info_; }
317 CallInfo* inlineCallInfo() const { return inlineCallInfo_; }
320 } // namespace jit
321 } // namespace js
323 #endif /* jit_WarpBuilder_h */