CompileInfo.h (14802B)
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_CompileInfo_h 8 #define jit_CompileInfo_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT 11 #include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some 12 13 #include <algorithm> // std::max 14 #include <stdint.h> // uint32_t 15 16 #include "jit/CompileWrappers.h" // CompileRuntime 17 #include "jit/JitFrames.h" // MinJITStackSize 18 #include "jit/shared/Assembler-shared.h" 19 #include "js/TypeDecls.h" // jsbytecode 20 #include "vm/BindingKind.h" // BindingLocation 21 #include "vm/JSAtomState.h" // JSAtomState 22 #include "vm/JSFunction.h" // JSFunction 23 #include "vm/JSScript.h" // JSScript 24 #include "vm/Opcodes.h" // JSOp 25 #include "vm/Scope.h" // BindingIter 26 27 namespace js { 28 29 class ModuleObject; 30 31 namespace jit { 32 33 class InlineScriptTree; 34 35 inline unsigned StartArgSlot(JSScript* script) { 36 // Reserved slots: 37 // Slot 0: Environment chain. 38 // Slot 1: Return value. 39 40 // When needed: 41 // Slot 2: Argumentsobject. 42 43 // Note: when updating this, please also update the assert in 44 // SnapshotWriter::startFrame 45 return 2 + (script->needsArgsObj() ? 1 : 0); 46 } 47 48 inline unsigned CountArgSlots(JSScript* script, JSFunction* fun) { 49 // Slot x + 0: This value. 50 // Slot x + 1: Argument 1. 51 // ... 52 // Slot x + n: Argument n. 53 54 // Note: when updating this, please also update the assert in 55 // SnapshotWriter::startFrame 56 return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0); 57 } 58 59 inline unsigned CountArgSlots(JSScript* script, bool hasFun, 60 uint32_t funArgCount) { 61 // Same as the previous function, for use when the JSFunction is not 62 // available. 63 return StartArgSlot(script) + (hasFun ? funArgCount + 1 : 0); 64 } 65 66 // Contains information about the compilation source for IR being generated. 67 class CompileInfo { 68 public: 69 CompileInfo(CompileRuntime* runtime, JSScript* script, jsbytecode* osrPc, 70 bool scriptNeedsArgsObj, InlineScriptTree* inlineScriptTree) 71 : script_(script), 72 fun_(script->function()), 73 osrPc_(osrPc), 74 scriptNeedsArgsObj_(scriptNeedsArgsObj), 75 hadEagerTruncationBailout_(script->hadEagerTruncationBailout()), 76 hadSpeculativePhiBailout_(script->hadSpeculativePhiBailout()), 77 hadLICMInvalidation_(script->hadLICMInvalidation()), 78 hadReorderingBailout_(script->hadReorderingBailout()), 79 hadBoundsCheckBailout_(script->failedBoundsCheck()), 80 hadUnboxFoldingBailout_(script->hadUnboxFoldingBailout()), 81 mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()), 82 anyFormalIsForwarded_(script->anyFormalIsForwarded()), 83 isDerivedClassConstructor_(script->isDerivedClassConstructor()), 84 inlineScriptTree_(inlineScriptTree), 85 hasSeenObjectEmulateUndefinedFuseIntact_( 86 runtime->hasSeenObjectEmulateUndefinedFuseIntact()), 87 hasSeenArrayExceedsInt32LengthFuseIntact_( 88 runtime->hasSeenArrayExceedsInt32LengthFuseIntact()) { 89 MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead); 90 91 nimplicit_ = StartArgSlot(script) /* env chain and argument obj */ 92 + (fun_ ? 1 : 0); /* this */ 93 nargs_ = fun_ ? fun_->nargs() : 0; 94 nlocals_ = script->nfixed(); 95 96 // An extra slot is needed for global scopes because InitGLexical (stack 97 // depth 1) is compiled as a SetProp (stack depth 2) on the global lexical 98 // scope. 99 uint32_t extra = script->isGlobalCode() ? 1 : 0; 100 nstack_ = std::max<unsigned>(script->nslots() - script->nfixed(), 101 MinJITStackSize) + 102 extra; 103 nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_; 104 105 // For derived class constructors, find and cache the frame slot for 106 // the .this binding. This slot is assumed to be always 107 // observable. See isObservableFrameSlot. 108 if (script->isDerivedClassConstructor()) { 109 MOZ_ASSERT(script->functionHasThisBinding()); 110 for (BindingIter bi(script); bi; bi++) { 111 if (bi.name() != runtime->names().dot_this_) { 112 continue; 113 } 114 BindingLocation loc = bi.location(); 115 if (loc.kind() == BindingLocation::Kind::Frame) { 116 thisSlotForDerivedClassConstructor_ = 117 mozilla::Some(localSlot(loc.slot())); 118 break; 119 } 120 } 121 } 122 123 // If the script uses an environment in body, the environment chain 124 // will need to be observable. 125 needsBodyEnvironmentObject_ = script->needsBodyEnvironment(); 126 funNeedsSomeEnvironmentObject_ = 127 fun_ ? fun_->needsSomeEnvironmentObject() : false; 128 } 129 130 explicit CompileInfo(unsigned nlocals) 131 : script_(nullptr), 132 fun_(nullptr), 133 osrPc_(nullptr), 134 scriptNeedsArgsObj_(false), 135 hadEagerTruncationBailout_(false), 136 hadSpeculativePhiBailout_(false), 137 hadLICMInvalidation_(false), 138 hadReorderingBailout_(false), 139 hadBoundsCheckBailout_(false), 140 hadUnboxFoldingBailout_(false), 141 branchHintingEnabled_(false), 142 mayReadFrameArgsDirectly_(false), 143 anyFormalIsForwarded_(false), 144 inlineScriptTree_(nullptr), 145 needsBodyEnvironmentObject_(false), 146 funNeedsSomeEnvironmentObject_(false), 147 hasSeenObjectEmulateUndefinedFuseIntact_(false), 148 hasSeenArrayExceedsInt32LengthFuseIntact_(false) { 149 nimplicit_ = 0; 150 nargs_ = 0; 151 nlocals_ = nlocals; 152 nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */ 153 nslots_ = nlocals_ + nstack_; 154 } 155 156 JSScript* script() const { return script_; } 157 bool compilingWasm() const { return script() == nullptr; } 158 ModuleObject* module() const { return script_->module(); } 159 jsbytecode* osrPc() const { return osrPc_; } 160 InlineScriptTree* inlineScriptTree() const { return inlineScriptTree_; } 161 162 // It's not safe to access the JSFunction off main thread. 163 bool hasFunMaybeLazy() const { return fun_; } 164 ImmGCPtr funMaybeLazy() const { return ImmGCPtr(fun_); } 165 166 const char* filename() const { return script_->filename(); } 167 168 unsigned lineno() const { return script_->lineno(); } 169 170 // Total number of slots: args, locals, and stack. 171 unsigned nslots() const { return nslots_; } 172 173 // Number of slots needed for env chain, return value, 174 // maybe argumentsobject and this value. 175 unsigned nimplicit() const { return nimplicit_; } 176 // Number of arguments (without counting this value). 177 unsigned nargs() const { return nargs_; } 178 // Number of slots needed for all local variables. This includes "fixed 179 // vars" (see above) and also block-scoped locals. 180 unsigned nlocals() const { return nlocals_; } 181 unsigned ninvoke() const { return nslots_ - nstack_; } 182 183 uint32_t environmentChainSlot() const { 184 MOZ_ASSERT(script()); 185 return 0; 186 } 187 uint32_t returnValueSlot() const { 188 MOZ_ASSERT(script()); 189 return 1; 190 } 191 uint32_t argsObjSlot() const { 192 MOZ_ASSERT(needsArgsObj()); 193 return 2; 194 } 195 uint32_t thisSlot() const { 196 MOZ_ASSERT(hasFunMaybeLazy()); 197 MOZ_ASSERT(nimplicit_ > 0); 198 return nimplicit_ - 1; 199 } 200 uint32_t firstArgSlot() const { return nimplicit_; } 201 uint32_t argSlotUnchecked(uint32_t i) const { 202 // During initialization, some routines need to get at arg 203 // slots regardless of how regular argument access is done. 204 MOZ_ASSERT(i < nargs_); 205 return nimplicit_ + i; 206 } 207 uint32_t argSlot(uint32_t i) const { 208 // This should only be accessed when compiling functions for 209 // which argument accesses don't need to go through the 210 // argument object. 211 MOZ_ASSERT(!argsObjAliasesFormals()); 212 return argSlotUnchecked(i); 213 } 214 uint32_t firstLocalSlot() const { return nimplicit_ + nargs_; } 215 uint32_t localSlot(uint32_t i) const { return firstLocalSlot() + i; } 216 uint32_t firstStackSlot() const { return firstLocalSlot() + nlocals(); } 217 uint32_t stackSlot(uint32_t i) const { return firstStackSlot() + i; } 218 219 uint32_t totalSlots() const { 220 MOZ_ASSERT(script() && hasFunMaybeLazy()); 221 return nimplicit() + nargs() + nlocals(); 222 } 223 224 bool hasMappedArgsObj() const { return script()->hasMappedArgsObj(); } 225 bool needsArgsObj() const { return scriptNeedsArgsObj_; } 226 bool argsObjAliasesFormals() const { 227 return scriptNeedsArgsObj_ && script()->hasMappedArgsObj(); 228 } 229 230 bool needsBodyEnvironmentObject() const { 231 return needsBodyEnvironmentObject_; 232 } 233 234 enum class SlotObservableKind { 235 // This slot must be preserved because it's observable outside SSA uses. 236 // It can't be recovered before or during bailout. 237 ObservableNotRecoverable, 238 239 // This slot must be preserved because it's observable, but it can be 240 // recovered. 241 ObservableRecoverable, 242 243 // This slot is not observable outside SSA uses. 244 NotObservable, 245 }; 246 247 inline SlotObservableKind getSlotObservableKind(uint32_t slot) const { 248 // Locals and expression stack slots. 249 if (slot >= firstLocalSlot()) { 250 // The |this| slot for a derived class constructor is a local slot. 251 // It should never be optimized out, as a Debugger might need to perform 252 // TDZ checks on it via, e.g., an exceptionUnwind handler. The TDZ check 253 // is required for correctness if the handler decides to continue 254 // execution. 255 if (thisSlotForDerivedClassConstructor_ && 256 *thisSlotForDerivedClassConstructor_ == slot) { 257 return SlotObservableKind::ObservableNotRecoverable; 258 } 259 return SlotObservableKind::NotObservable; 260 } 261 262 // Formal argument slots. 263 if (slot >= firstArgSlot()) { 264 MOZ_ASSERT(hasFunMaybeLazy()); 265 MOZ_ASSERT(slot - firstArgSlot() < nargs()); 266 267 // Preserve formal arguments if they might be read when creating a rest or 268 // arguments object. In non-strict scripts, Function.arguments can create 269 // an arguments object dynamically so we always preserve the arguments. 270 if (mayReadFrameArgsDirectly_ || !script()->strict()) { 271 return SlotObservableKind::ObservableRecoverable; 272 } 273 return SlotObservableKind::NotObservable; 274 } 275 276 // |this| slot is observable but it can be recovered. 277 if (hasFunMaybeLazy() && slot == thisSlot()) { 278 return SlotObservableKind::ObservableRecoverable; 279 } 280 281 // Environment chain slot. 282 if (slot == environmentChainSlot()) { 283 // If environments can be added in the body (after the prologue) we need 284 // to preserve the environment chain slot. It can't be recovered. 285 if (needsBodyEnvironmentObject()) { 286 return SlotObservableKind::ObservableNotRecoverable; 287 } 288 // If the function may need an arguments object, also preserve the 289 // environment chain because it may be needed to reconstruct the arguments 290 // object during bailout. 291 if (funNeedsSomeEnvironmentObject_ || needsArgsObj()) { 292 return SlotObservableKind::ObservableRecoverable; 293 } 294 return SlotObservableKind::NotObservable; 295 } 296 297 // The arguments object is observable. If it does not escape, it can 298 // be recovered. 299 if (needsArgsObj() && slot == argsObjSlot()) { 300 MOZ_ASSERT(hasFunMaybeLazy()); 301 return SlotObservableKind::ObservableRecoverable; 302 } 303 304 MOZ_ASSERT(slot == returnValueSlot()); 305 return SlotObservableKind::NotObservable; 306 } 307 308 // Returns true if a slot can be observed out-side the current frame while 309 // the frame is active on the stack. This implies that these definitions 310 // would have to be executed and that they cannot be removed even if they 311 // are unused. 312 inline bool isObservableSlot(uint32_t slot) const { 313 SlotObservableKind kind = getSlotObservableKind(slot); 314 return (kind == SlotObservableKind::ObservableNotRecoverable || 315 kind == SlotObservableKind::ObservableRecoverable); 316 } 317 318 // Returns true if a slot can be recovered before or during a bailout. A 319 // definition which can be observed and recovered, implies that this 320 // definition can be optimized away as long as we can compute its values. 321 bool isRecoverableOperand(uint32_t slot) const { 322 SlotObservableKind kind = getSlotObservableKind(slot); 323 return (kind == SlotObservableKind::ObservableRecoverable || 324 kind == SlotObservableKind::NotObservable); 325 } 326 327 // Check previous bailout states to prevent doing the same bailout in the 328 // next compilation. 329 bool hadEagerTruncationBailout() const { return hadEagerTruncationBailout_; } 330 bool hadSpeculativePhiBailout() const { return hadSpeculativePhiBailout_; } 331 bool hadLICMInvalidation() const { return hadLICMInvalidation_; } 332 bool hadReorderingBailout() const { return hadReorderingBailout_; } 333 bool hadBoundsCheckBailout() const { return hadBoundsCheckBailout_; } 334 bool hadUnboxFoldingBailout() const { return hadUnboxFoldingBailout_; } 335 336 bool branchHintingEnabled() const { 337 return compilingWasm() && branchHintingEnabled_; 338 } 339 340 void setBranchHinting(bool value) { branchHintingEnabled_ = value; } 341 342 bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; } 343 bool anyFormalIsForwarded() const { return anyFormalIsForwarded_; } 344 345 bool isDerivedClassConstructor() const { return isDerivedClassConstructor_; } 346 347 bool hasSeenObjectEmulateUndefinedFuseIntact() const { 348 return hasSeenObjectEmulateUndefinedFuseIntact_; 349 } 350 351 bool hasSeenArrayExceedsInt32LengthFuseIntact() const { 352 return hasSeenArrayExceedsInt32LengthFuseIntact_; 353 } 354 355 private: 356 unsigned nimplicit_; 357 unsigned nargs_; 358 unsigned nlocals_; 359 unsigned nstack_; 360 unsigned nslots_; 361 mozilla::Maybe<unsigned> thisSlotForDerivedClassConstructor_; 362 JSScript* script_; 363 JSFunction* fun_; 364 jsbytecode* osrPc_; 365 366 bool scriptNeedsArgsObj_; 367 368 // Record the state of previous bailouts in order to prevent compiling the 369 // same function identically the next time. 370 bool hadEagerTruncationBailout_; 371 bool hadSpeculativePhiBailout_; 372 bool hadLICMInvalidation_; 373 bool hadReorderingBailout_; 374 bool hadBoundsCheckBailout_; 375 bool hadUnboxFoldingBailout_; 376 377 bool branchHintingEnabled_; 378 379 bool mayReadFrameArgsDirectly_; 380 bool anyFormalIsForwarded_; 381 382 bool isDerivedClassConstructor_; 383 384 InlineScriptTree* inlineScriptTree_; 385 386 // Whether a script needs environments within its body. This informs us 387 // that the environment chain is not easy to reconstruct. 388 bool needsBodyEnvironmentObject_; 389 bool funNeedsSomeEnvironmentObject_; 390 391 bool hasSeenObjectEmulateUndefinedFuseIntact_; 392 bool hasSeenArrayExceedsInt32LengthFuseIntact_; 393 }; 394 395 } // namespace jit 396 } // namespace js 397 398 #endif /* jit_CompileInfo_h */