BaselineFrameInfo.h (13118B)
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_BaselineFrameInfo_h 8 #define jit_BaselineFrameInfo_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/Maybe.h" 12 13 #include <new> 14 15 #include "jit/BaselineFrame.h" 16 #include "jit/BaselineJIT.h" 17 #include "jit/FixedList.h" 18 #include "jit/MacroAssembler.h" 19 #include "jit/SharedICRegisters.h" 20 21 namespace js { 22 namespace jit { 23 24 struct BytecodeInfo; 25 class MacroAssembler; 26 27 // [SMDOC] Baseline FrameInfo overview. 28 // 29 // FrameInfo is used by BaselineCodeGen to track values stored in the frame. 30 // There are two implementations: 31 // 32 // InterpreterFrameInfo 33 // -------------------- 34 // The InterpreterFrameInfo class is used by the interpreter generator and is 35 // a very simple interface on top of the MacroAssembler, because the stack is 36 // always synced. 37 // 38 // CompilerFrameInfo 39 // ----------------- 40 // The CompilerFrameInfo class is more complicated because it maintains a 41 // virtual stack to optimize some common stack operations. Locals and arguments 42 // are always fully synced. Stack values can either be synced, stored as 43 // constant, stored in a Value register or refer to a local slot. Syncing a 44 // StackValue ensures it's stored on the stack, e.g. kind == Stack. 45 // 46 // To see how this works, consider the following statement: 47 // 48 // var y = x + 9; 49 // 50 // Here two values are pushed: StackValue(LocalSlot(0)) and 51 // StackValue(Int32Value(9)). Only when we reach the ADD op, code is generated 52 // to load the operands directly into the right operand registers and sync all 53 // other stack values. 54 // 55 // For stack values, the following invariants hold (and are checked between 56 // ops): 57 // 58 // (1) If a value is synced (kind == Stack), all values below it must also be 59 // synced. In other words, values with kind other than Stack can only appear 60 // on top of the abstract stack. 61 // 62 // (2) When we call a stub or IC, all values still on the stack must be synced. 63 64 // Represents a value pushed on the stack. Note that StackValue is not used for 65 // locals or arguments since these are always fully synced. 66 class StackValue { 67 public: 68 enum Kind { 69 Constant, 70 Register, 71 Stack, 72 LocalSlot, 73 ArgSlot, 74 ThisSlot, 75 #ifdef DEBUG 76 // In debug builds, assert Kind is initialized. 77 Uninitialized, 78 #endif 79 }; 80 81 private: 82 MOZ_INIT_OUTSIDE_CTOR Kind kind_; 83 84 MOZ_INIT_OUTSIDE_CTOR union Data { 85 JS::Value constant; 86 ValueOperand reg; 87 uint32_t localSlot; 88 uint32_t argSlot; 89 90 // |constant| has a non-trivial constructor and therefore MUST be 91 // placement-new'd into existence. 92 MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS 93 Data() {} 94 MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS 95 } data; 96 97 MOZ_INIT_OUTSIDE_CTOR JSValueType knownType_; 98 99 public: 100 StackValue() { reset(); } 101 102 Kind kind() const { return kind_; } 103 bool hasKnownType() const { return knownType_ != JSVAL_TYPE_UNKNOWN; } 104 bool hasKnownType(JSValueType type) const { 105 MOZ_ASSERT(type != JSVAL_TYPE_UNKNOWN); 106 return knownType_ == type; 107 } 108 JSValueType knownType() const { 109 MOZ_ASSERT(hasKnownType()); 110 return knownType_; 111 } 112 void reset() { 113 #ifdef DEBUG 114 kind_ = Uninitialized; 115 knownType_ = JSVAL_TYPE_UNKNOWN; 116 #endif 117 } 118 Value constant() const { 119 MOZ_ASSERT(kind_ == Constant); 120 return data.constant; 121 } 122 ValueOperand reg() const { 123 MOZ_ASSERT(kind_ == Register); 124 return data.reg; 125 } 126 uint32_t localSlot() const { 127 MOZ_ASSERT(kind_ == LocalSlot); 128 return data.localSlot; 129 } 130 uint32_t argSlot() const { 131 MOZ_ASSERT(kind_ == ArgSlot); 132 return data.argSlot; 133 } 134 135 void setConstant(const Value& v) { 136 kind_ = Constant; 137 new (&data.constant) Value(v); 138 knownType_ = v.isDouble() ? JSVAL_TYPE_DOUBLE : v.extractNonDoubleType(); 139 } 140 void setRegister(const ValueOperand& val, 141 JSValueType knownType = JSVAL_TYPE_UNKNOWN) { 142 kind_ = Register; 143 new (&data.reg) ValueOperand(val); 144 knownType_ = knownType; 145 } 146 void setLocalSlot(uint32_t slot) { 147 kind_ = LocalSlot; 148 new (&data.localSlot) uint32_t(slot); 149 knownType_ = JSVAL_TYPE_UNKNOWN; 150 } 151 void setArgSlot(uint32_t slot) { 152 kind_ = ArgSlot; 153 new (&data.argSlot) uint32_t(slot); 154 knownType_ = JSVAL_TYPE_UNKNOWN; 155 } 156 void setThis() { 157 kind_ = ThisSlot; 158 knownType_ = JSVAL_TYPE_UNKNOWN; 159 } 160 void setStack() { 161 kind_ = Stack; 162 knownType_ = JSVAL_TYPE_UNKNOWN; 163 } 164 }; 165 166 enum StackAdjustment { AdjustStack, DontAdjustStack }; 167 168 class FrameInfo { 169 protected: 170 MacroAssembler& masm; 171 172 public: 173 explicit FrameInfo(MacroAssembler& masm) : masm(masm) {} 174 175 Address addressOfLocal(size_t local) const { 176 return Address(FramePointer, BaselineFrame::reverseOffsetOfLocal(local)); 177 } 178 Address addressOfArg(size_t arg) const { 179 return Address(FramePointer, JitFrameLayout::offsetOfActualArg(arg)); 180 } 181 Address addressOfThis() const { 182 return Address(FramePointer, JitFrameLayout::offsetOfThis()); 183 } 184 Address addressOfDescriptor() const { 185 return Address(FramePointer, CommonFrameLayout::offsetOfDescriptor()); 186 } 187 Address addressOfCalleeToken() const { 188 return Address(FramePointer, JitFrameLayout::offsetOfCalleeToken()); 189 } 190 Address addressOfEnvironmentChain() const { 191 return Address(FramePointer, 192 BaselineFrame::reverseOffsetOfEnvironmentChain()); 193 } 194 Address addressOfICScript() const { 195 return Address(FramePointer, BaselineFrame::reverseOffsetOfICScript()); 196 } 197 Address addressOfFlags() const { 198 return Address(FramePointer, BaselineFrame::reverseOffsetOfFlags()); 199 } 200 Address addressOfReturnValue() const { 201 return Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue()); 202 } 203 Address addressOfArgsObj() const { 204 return Address(FramePointer, BaselineFrame::reverseOffsetOfArgsObj()); 205 } 206 Address addressOfScratchValue() const { 207 return Address(FramePointer, BaselineFrame::reverseOffsetOfScratchValue()); 208 } 209 Address addressOfScratchValueLow32() const { 210 return Address(FramePointer, 211 BaselineFrame::reverseOffsetOfScratchValueLow32()); 212 } 213 Address addressOfScratchValueHigh32() const { 214 return Address(FramePointer, 215 BaselineFrame::reverseOffsetOfScratchValueHigh32()); 216 } 217 #ifdef DEBUG 218 Address addressOfDebugFrameSize() const { 219 return Address(FramePointer, 220 BaselineFrame::reverseOffsetOfDebugFrameSize()); 221 } 222 #endif 223 Address addressOfInterpreterScript() const { 224 return Address(FramePointer, 225 BaselineFrame::reverseOffsetOfInterpreterScript()); 226 } 227 }; 228 229 class CompilerFrameInfo : public FrameInfo { 230 friend class BaselinePerfSpewer; 231 JSScript* script; 232 FixedList<StackValue> stack; 233 size_t spIndex; 234 235 public: 236 CompilerFrameInfo(JSScript* script, MacroAssembler& masm) 237 : FrameInfo(masm), script(script), spIndex(0) {} 238 [[nodiscard]] bool init(TempAllocator& alloc); 239 240 size_t nlocals() const { return script->nfixed(); } 241 size_t nargs() const { return script->function()->nargs(); } 242 243 private: 244 inline StackValue* rawPush() { 245 StackValue* val = &stack[spIndex++]; 246 val->reset(); 247 return val; 248 } 249 250 inline StackValue* peek(int32_t index) const { 251 MOZ_ASSERT(index < 0); 252 return const_cast<StackValue*>(&stack[spIndex + index]); 253 } 254 255 public: 256 inline size_t stackDepth() const { return spIndex; } 257 inline void setStackDepth(uint32_t newDepth) { 258 if (newDepth <= stackDepth()) { 259 spIndex = newDepth; 260 } else { 261 uint32_t diff = newDepth - stackDepth(); 262 for (uint32_t i = 0; i < diff; i++) { 263 StackValue* val = rawPush(); 264 val->setStack(); 265 } 266 267 MOZ_ASSERT(spIndex == newDepth); 268 } 269 } 270 271 void assertStackDepth(uint32_t depth) { MOZ_ASSERT(stackDepth() == depth); } 272 void incStackDepth(int32_t diff) { setStackDepth(stackDepth() + diff); } 273 bool hasKnownStackDepth(uint32_t depth) { return stackDepth() == depth; } 274 275 inline void pop(StackAdjustment adjust = AdjustStack); 276 inline void popn(uint32_t n, StackAdjustment adjust = AdjustStack); 277 inline void push(const Value& val) { 278 StackValue* sv = rawPush(); 279 sv->setConstant(val); 280 } 281 inline void push(const ValueOperand& val, 282 JSValueType knownType = JSVAL_TYPE_UNKNOWN) { 283 StackValue* sv = rawPush(); 284 sv->setRegister(val, knownType); 285 } 286 inline void pushLocal(uint32_t local) { 287 MOZ_ASSERT(local < nlocals()); 288 StackValue* sv = rawPush(); 289 sv->setLocalSlot(local); 290 } 291 inline void pushArg(uint32_t arg) { 292 StackValue* sv = rawPush(); 293 sv->setArgSlot(arg); 294 } 295 inline void pushThis() { 296 StackValue* sv = rawPush(); 297 sv->setThis(); 298 } 299 300 inline void pushScratchValue() { 301 masm.pushValue(addressOfScratchValue()); 302 StackValue* sv = rawPush(); 303 sv->setStack(); 304 } 305 306 Address addressOfLocal(size_t local) const { 307 MOZ_ASSERT(local < nlocals()); 308 return FrameInfo::addressOfLocal(local); 309 } 310 Address addressOfArg(size_t arg) const { 311 MOZ_ASSERT(arg < nargs()); 312 return FrameInfo::addressOfArg(arg); 313 } 314 315 Address addressOfStackValue(int32_t depth) const { 316 const StackValue* value = peek(depth); 317 MOZ_ASSERT(value->kind() == StackValue::Stack); 318 size_t slot = value - &stack[0]; 319 MOZ_ASSERT(slot < stackDepth()); 320 return Address(FramePointer, 321 BaselineFrame::reverseOffsetOfLocal(nlocals() + slot)); 322 } 323 324 void popValue(ValueOperand dest); 325 326 void sync(StackValue* val); 327 void syncStack(uint32_t uses); 328 uint32_t numUnsyncedSlots(); 329 void popRegsAndSync(uint32_t uses); 330 331 void assertSyncedStack() const { 332 MOZ_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack); 333 } 334 335 bool stackValueHasKnownType(int32_t depth, JSValueType type) const { 336 return peek(depth)->hasKnownType(type); 337 } 338 339 mozilla::Maybe<Value> knownStackValue(int32_t depth) const { 340 StackValue* val = peek(depth); 341 if (val->kind() == StackValue::Constant) { 342 return mozilla::Some(val->constant()); 343 } 344 return mozilla::Nothing(); 345 } 346 347 void storeStackValue(int32_t depth, const Address& dest, 348 const ValueOperand& scratch); 349 350 uint32_t frameSize() const { 351 return BaselineFrame::frameSizeForNumValueSlots(nlocals() + stackDepth()); 352 } 353 354 #ifdef DEBUG 355 // Assert the state is valid before excuting "pc". 356 void assertValidState(const BytecodeInfo& info); 357 #else 358 inline void assertValidState(const BytecodeInfo& info) {} 359 #endif 360 }; 361 362 class InterpreterFrameInfo : public FrameInfo { 363 public: 364 explicit InterpreterFrameInfo(MacroAssembler& masm) : FrameInfo(masm) {} 365 366 // These methods are no-ops in the interpreter, because we don't have a 367 // virtual stack there. 368 void syncStack(uint32_t uses) {} 369 void assertSyncedStack() const {} 370 void assertStackDepth(uint32_t depth) {} 371 void incStackDepth(int32_t diff) {} 372 bool hasKnownStackDepth(uint32_t depth) { return false; } 373 uint32_t numUnsyncedSlots() { return 0; } 374 375 bool stackValueHasKnownType(int32_t depth, JSValueType type) const { 376 return false; 377 } 378 379 mozilla::Maybe<Value> knownStackValue(int32_t depth) const { 380 return mozilla::Nothing(); 381 } 382 383 Address addressOfStackValue(int depth) const { 384 MOZ_ASSERT(depth < 0); 385 return Address(masm.getStackPointer(), 386 masm.framePushed() + size_t(-(depth + 1)) * sizeof(Value)); 387 } 388 389 BaseIndex addressOfStackValue(Register index, int32_t offset = 0) const { 390 return BaseIndex(masm.getStackPointer(), index, ValueScale, offset); 391 } 392 393 void popRegsAndSync(uint32_t uses); 394 395 inline void pop(); 396 397 inline void popn(uint32_t n); 398 399 void popn(Register reg) { 400 // sp := sp + reg * sizeof(Value) 401 Register spReg = AsRegister(masm.getStackPointer()); 402 masm.computeEffectiveAddress(BaseValueIndex(spReg, reg), spReg); 403 // On arm64, SP may be < PSP now (that's OK). 404 // eg testcase: tests/arguments/strict-args-generator-flushstack.js 405 } 406 407 void popValue(ValueOperand dest) { masm.popValue(dest); } 408 409 void push(const ValueOperand& val, 410 JSValueType knownType = JSVAL_TYPE_UNKNOWN) { 411 masm.pushValue(val); 412 } 413 void push(const Value& val) { masm.pushValue(val); } 414 415 void pushThis() { masm.pushValue(addressOfThis()); } 416 void pushScratchValue() { masm.pushValue(addressOfScratchValue()); } 417 418 void storeStackValue(int32_t depth, const Address& dest, 419 const ValueOperand& scratch) { 420 masm.loadValue(addressOfStackValue(depth), scratch); 421 masm.storeValue(scratch, dest); 422 } 423 424 void bumpInterpreterICEntry(); 425 426 Address addressOfInterpreterPC() const { 427 return Address(FramePointer, BaselineFrame::reverseOffsetOfInterpreterPC()); 428 } 429 Address addressOfInterpreterICEntry() const { 430 return Address(FramePointer, 431 BaselineFrame::reverseOffsetOfInterpreterICEntry()); 432 } 433 }; 434 435 } // namespace jit 436 } // namespace js 437 438 #endif /* jit_BaselineFrameInfo_h */