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