this repo has no description
at trunk 479 lines 16 kB view raw
1/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */ 2#pragma once 3 4#include <cstring> 5 6#include "bytecode.h" 7#include "globals.h" 8#include "handles-decl.h" 9#include "objects.h" 10 11namespace py { 12 13// TryBlock contains the unmarshaled block stack information. 14// 15// Block stack entries are encoded and stored on the stack as a single 16// SmallInt using the following format: 17// 18// Name Size Description 19// ---------------------------------------------------- 20// (kSmallIntTag 1) 21// Kind 1 The kind of block this entry represents. 22// Handler 30 Where to jump to find the handler 23// Level 32 Value stack level to pop to 24class TryBlock { 25 public: 26 // cpython stores the opcode that pushed the block as the block kind, but only 27 // 4 opcodes actually push blocks. Store the same information with fewer bits. 28 enum Kind { 29 kExceptHandler, 30 kFinally, 31 }; 32 33 explicit TryBlock(RawObject value) { 34 DCHECK(value.isSmallInt(), "expected small integer"); 35 value_ = value.raw(); 36 } 37 38 TryBlock(Kind kind, word handler, word level) { 39 DCHECK((handler & ~kHandlerMask) == 0, "handler too big"); 40 DCHECK((level & ~kLevelMask) == 0, "level too big"); 41 value_ = static_cast<uword>(kind) << kKindOffset | 42 handler << kHandlerOffset | level << kLevelOffset; 43 } 44 45 RawObject asSmallInt() const; 46 47 Kind kind() const; 48 49 word handler() const; 50 51 word level() const; 52 53 static const int kKindOffset = RawObject::kSmallIntTagBits; 54 static const int kKindSize = 1; 55 static const uword kKindMask = (1 << kKindSize) - 1; 56 57 static const int kHandlerOffset = kKindOffset + kKindSize; // 9 58 static const int kHandlerSize = 30; 59 static const uword kHandlerMask = (uword{1} << kHandlerSize) - 1; 60 61 static const int kLevelOffset = kHandlerOffset + kHandlerSize; // 39 62 static const int kLevelSize = 32; 63 static const uword kLevelMask = (uword{1} << kLevelSize) - 1; 64 65 static const int kSize = kLevelOffset + kLevelSize; 66 67 static_assert(kSize <= kBitsPerWord, "TryBlock must fit into a word"); 68 69 private: 70 uword value_; 71}; 72 73// A stack frame. 74// 75// Prior to a function call, the stack will look like 76// 77// Function 78// Arg 0 79// ... 80// Arg N 81// <- Top of stack / lower memory addresses 82// 83// The function prologue is responsible for reserving space for local variables 84// and pushing other frame metadata needed by the interpreter onto the stack. 85// After the prologue, and immediately before the interpreter is re-invoked, 86// the stack looks like: 87// 88// Implicit Globals[1] 89// Function 90// Arg 0 <------------------------------------------------+ 91// ... | 92// Arg N | 93// Locals 0 | 94// ... | 95// Locals N | 96// +-------------------------------+ Frame (fixed size) | 97// |+----------------+ BlockStack | | 98// || Blockstack top | | | 99// || . | ^ | | 100// || . | | | | 101// || . entries | | growth | | 102// |+----------------+ | | 103// | Blockstack Depth/Return Mode | | 104// | Locals Offset ----------------|----------------------+ 105// | Virtual PC | 106// | Previous frame ptr |<-+ <--Frame pointer 107// +-------------------------------+ 108// . . 109// . | growth . 110// . Value stack | . 111// . v . 112// +...............................+ 113// 114// [1] Only available for non-optimized functions started via 115// `Thread::runClassFunction()` or `Thread::exec()`. for example, module- and 116// class-body function. 117// 118// 119// Implicit Globals 120// ================ 121// Python code started via `Thread::runClassFunction()` or `Thread::exec()` 122// which is used for things like module- and class-bodies or `eval()` may store 123// their local variables in arbitrary mapping objects. In this case the 124// functions will have the OPTIMIZED and NEWLOCALS flags cleared and the 125// bytecode will use STORE_NAME/LOAD_NAME rather than STORE_FAST/LOAD_FAST. 126// 127// We use the term implicit globals in accordance with the Python language 128// reference. Note that CPython code and APIs often use the term "locals" 129// instead. We do not use that term to avoid confusion with fast locals. 130// 131// In our system the implicit globals part of the frame only exists for 132// functions that use them. It may contain an arbitrary mapping or `None`. 133// `None` is a performance optimization in our system. It indicates that we 134// directly write into the globals / `function().moduleObject()` instead of 135// using the `implicitGlobals()` this way we can skip setting up a `ModuleProxy` 136// object for this case and avoid the extra indirection. 137class Frame { 138 public: 139 enum ReturnMode { 140 kNormal = 0, 141 kExitRecursiveInterpreter = 1 << 0, 142 kProfilerReturn = 1 << 1, 143 kJitReturn = 1 << 2, 144 }; 145 146 // Returns true if this frame is for a built-in or extension function. This 147 // means no bytecode exists and functions like virtualPC() or caches() must 148 // not be used. 149 bool isNative(); 150 151 // Function arguments, local variables, cell variables, and free variables 152 RawObject local(word idx); 153 void setLocal(word idx, RawObject value); 154 155 RawObject localWithReverseIndex(word reverse_idx); 156 void setLocalWithReverseIndex(word reverse_idx, RawObject value); 157 158 RawFunction function(); 159 160 word blockStackDepthReturnMode(); 161 void setBlockStackDepthReturnMode(word value); 162 163 bool blockStackEmpty(); 164 TryBlock blockStackPeek(); 165 TryBlock blockStackPop(); 166 void blockStackPush(TryBlock block); 167 168 void addReturnMode(word mode); 169 word returnMode(); 170 171 // Index in the bytecode array of the next instruction to be executed. 172 word virtualPC(); 173 void setVirtualPC(word pc); 174 175 word localsOffset(); 176 void setLocalsOffset(word locals_offset); 177 178 // Index in the bytecode array of the instruction currently being executed. 179 word currentPC(); 180 181 // The implicit globals namespace. This is only available when the 182 // code does not have OPTIMIZED and NEWLOCALS flags set. See the class 183 // comment for details. 184 RawObject implicitGlobals(); 185 186 RawMutableBytes bytecode(); 187 void setBytecode(RawMutableBytes bytecode); 188 189 RawObject caches(); 190 void setCaches(RawObject caches); 191 192 RawObject code(); 193 194 // A pointer to the previous frame or nullptr if this is the first frame 195 Frame* previousFrame(); 196 void setPreviousFrame(Frame* frame); 197 198 // Returns a pointer to the end of the frame including locals / parameters. 199 RawObject* frameEnd() { 200 // The locals() pointer points at the first local, so we need + 1 to skip 201 // the first local and another +1 to skip the function reference before. 202 return locals() + kFunctionOffsetFromLocals + 1; 203 } 204 205 bool isSentinel(); 206 207 // Versions of valueStackTop() and popValue() for a Frame that's had 208 // stashStackSize() called on it. 209 RawObject* stashedValueStackTop(); 210 word stashedStackSize(); 211 RawObject stashedPopValue(); 212 213 // Encode value stack size into the "previous frame" field. This 214 // representation is used for paused GeneratorFrame objects on the heap. 215 void stashStackSize(word size); 216 217 // Compute the total space required for a frame object 218 static word allocationSize(RawObject code); 219 220 // Returns nullptr if the frame is well formed, otherwise an error message. 221 const char* isInvalid(); 222 223 static const int kMaxBlockStackDepth = 20; 224 225 static const int kBytecodeOffset = 0; 226 static const int kCachesOffset = kBytecodeOffset + kPointerSize; 227 static const int kPreviousFrameOffset = kCachesOffset + kPointerSize; 228 static const int kVirtualPCOffset = kPreviousFrameOffset + kPointerSize; 229 static const int kLocalsOffsetOffset = kVirtualPCOffset + kPointerSize; 230 static const int kBlockStackDepthReturnModeOffset = 231 kLocalsOffsetOffset + kPointerSize; 232 static const int kBlockStackOffset = 233 kBlockStackDepthReturnModeOffset + kPointerSize; 234 static const int kSize = 235 kBlockStackOffset + (kMaxBlockStackDepth * kPointerSize); 236 237 static_assert(RawGeneratorFrame::kFrameSize == kSize, "frame size mismatch"); 238 // For stashed frames we save the stack size in previous frame offset. 239 static_assert(RawGeneratorFrame::kStackSizeFrameOffset == 240 kPreviousFrameOffset, 241 "previous frame / stack size mismatch"); 242 243 static const int kFunctionOffsetFromLocals = 0; 244 static const int kImplicitGlobalsOffsetFromLocals = 1; 245 246 // A large PC value represents finished generators. It must be an even number 247 // to fit the constraints of `setVirtualPC()`/`virtualPD()`. 248 static const word kFinishedGeneratorPC = RawSmallInt::kMaxValue - 1; 249 250 static const int kBlockStackDepthBits = 32; 251 static const word kBlockStackDepthMask = 252 (word{1} << kBlockStackDepthBits) - 1; 253 static const word kReturnModeOffset = 32; 254 255 // Returns a pointer to the "begin" of where the arguments + locals are stored 256 // on the stack. For example local 0 can be found at `locals()[-1]` local 1 257 // at `locals()[-2]`. 258 RawObject* locals(); 259 260 private: 261 uword address(); 262 RawObject at(int offset); 263 void atPut(int offset, RawObject value); 264 265 DISALLOW_COPY_AND_ASSIGN(Frame); 266}; 267 268class FrameVisitor { 269 public: 270 virtual bool visit(Frame* frame) = 0; 271 virtual ~FrameVisitor() = default; 272}; 273 274class Arguments { 275 public: 276 Arguments() = default; 277 Arguments(Frame* frame) : locals_(frame->locals()) {} 278 279 RawObject get(word n) const { return *(locals_ - n - 1); } 280 281 protected: 282 RawObject* locals_; 283}; 284 285RawObject frameLocals(Thread* thread, Frame* frame); 286 287inline bool Frame::isNative() { 288 return !code().isCode() || RawCode::cast(code()).isNative(); 289} 290 291inline uword Frame::address() { return reinterpret_cast<uword>(this); } 292 293inline RawObject Frame::at(int offset) { 294 return *reinterpret_cast<RawObject*>(address() + offset); 295} 296 297inline void Frame::atPut(int offset, RawObject value) { 298 *reinterpret_cast<RawObject*>(address() + offset) = value; 299} 300 301inline word Frame::blockStackDepthReturnMode() { 302 return RawSmallInt::cast(at(kBlockStackDepthReturnModeOffset)) 303 .asReinterpretedWord(); 304} 305 306inline void Frame::setBlockStackDepthReturnMode(word value) { 307 atPut(kBlockStackDepthReturnModeOffset, 308 RawSmallInt::fromReinterpretedWord(value)); 309} 310 311inline bool Frame::blockStackEmpty() { 312 return (blockStackDepthReturnMode() & kBlockStackDepthMask) == 0; 313} 314 315inline TryBlock Frame::blockStackPeek() { 316 word depth = blockStackDepthReturnMode() & kBlockStackDepthMask; 317 DCHECK(depth > 0, "cannot peek into empty blockstack"); 318 return TryBlock(at(kBlockStackOffset + depth - kPointerSize)); 319} 320 321inline TryBlock Frame::blockStackPop() { 322 word new_depth_return_mode = blockStackDepthReturnMode() - kPointerSize; 323 word new_depth = new_depth_return_mode & kBlockStackDepthMask; 324 DCHECK(new_depth >= 0, "block stack underflow"); 325 TryBlock result(at(kBlockStackOffset + new_depth)); 326 setBlockStackDepthReturnMode(new_depth_return_mode); 327 return result; 328} 329 330inline void Frame::blockStackPush(TryBlock block) { 331 word depth_return_mode = blockStackDepthReturnMode(); 332 word depth = depth_return_mode & kBlockStackDepthMask; 333 DCHECK(depth < kMaxBlockStackDepth * kPointerSize, "block stack overflow"); 334 atPut(kBlockStackOffset + depth, block.asSmallInt()); 335 setBlockStackDepthReturnMode(depth_return_mode + kPointerSize); 336} 337 338inline void Frame::addReturnMode(word mode) { 339 DCHECK(!isNative(), "Cannot set return mode on native frames"); 340 word blockstack_depth_return_mode = blockStackDepthReturnMode(); 341 setBlockStackDepthReturnMode(blockstack_depth_return_mode | 342 (mode << kReturnModeOffset)); 343} 344 345inline word Frame::returnMode() { 346 return static_cast<ReturnMode>(blockStackDepthReturnMode() >> 347 kReturnModeOffset); 348} 349 350inline RawFunction Frame::function() { 351 DCHECK(previousFrame() != nullptr, "must not be called on initial frame"); 352 return RawFunction::cast(*(locals() + kFunctionOffsetFromLocals)); 353} 354 355inline word Frame::virtualPC() { 356 return RawSmallInt::cast(at(kVirtualPCOffset)).asReinterpretedWord(); 357} 358 359inline void Frame::setVirtualPC(word pc) { 360 // We re-interpret the PC value as a small int. This works because it must 361 // be an even number and naturally has the lowest bit cleared. 362 atPut(kVirtualPCOffset, RawSmallInt::fromReinterpretedWord(pc)); 363} 364 365inline word Frame::localsOffset() { 366 return RawSmallInt::cast(at(kLocalsOffsetOffset)).asReinterpretedWord(); 367} 368 369inline void Frame::setLocalsOffset(word locals_offset) { 370 atPut(kLocalsOffsetOffset, RawSmallInt::fromReinterpretedWord(locals_offset)); 371} 372 373inline word Frame::currentPC() { 374 return RawSmallInt::cast(at(kVirtualPCOffset)).asReinterpretedWord() - 375 kCodeUnitSize; 376} 377 378inline RawObject Frame::implicitGlobals() { 379 DCHECK(previousFrame() != nullptr, "must not be called on initial frame"); 380 DCHECK(!function().hasOptimizedOrNewlocals(), 381 "implicit globals not available"); 382 // Thread::exec() and Thread::runClassFunction() place implicit globals there. 383 return *(locals() + kImplicitGlobalsOffsetFromLocals); 384} 385 386inline RawObject Frame::code() { return function().code(); } 387 388inline RawObject* Frame::locals() { 389 return reinterpret_cast<RawObject*>(reinterpret_cast<char*>(this) + 390 localsOffset()); 391} 392 393inline RawObject Frame::local(word idx) { 394 DCHECK_INDEX(idx, function().totalLocals()); 395 return *(locals() - idx - 1); 396} 397 398inline RawObject Frame::localWithReverseIndex(word reverse_idx) { 399 DCHECK_INDEX(reverse_idx, function().totalLocals()); 400 RawObject* locals_end = reinterpret_cast<RawObject*>(address() + kSize); 401 return locals_end[reverse_idx]; 402} 403 404inline void Frame::setLocal(word idx, RawObject value) { 405 DCHECK_INDEX(idx, function().totalLocals()); 406 *(locals() - idx - 1) = value; 407} 408 409inline void Frame::setLocalWithReverseIndex(word reverse_idx, RawObject value) { 410 DCHECK_INDEX(reverse_idx, function().totalLocals()); 411 RawObject* locals_end = reinterpret_cast<RawObject*>(address() + kSize); 412 locals_end[reverse_idx] = value; 413} 414 415inline RawObject Frame::caches() { return at(kCachesOffset); } 416 417inline void Frame::setCaches(RawObject caches) { atPut(kCachesOffset, caches); } 418 419inline RawMutableBytes Frame::bytecode() { 420 return RawMutableBytes::cast(at(kBytecodeOffset)); 421} 422 423inline void Frame::setBytecode(RawMutableBytes bytecode) { 424 atPut(kBytecodeOffset, bytecode); 425} 426 427inline Frame* Frame::previousFrame() { 428 RawObject frame = at(kPreviousFrameOffset); 429 return static_cast<Frame*>(RawSmallInt::cast(frame).asAlignedCPtr()); 430} 431 432inline void Frame::setPreviousFrame(Frame* frame) { 433 atPut(kPreviousFrameOffset, 434 RawSmallInt::fromAlignedCPtr(reinterpret_cast<void*>(frame))); 435} 436 437inline bool Frame::isSentinel() { 438 // This is the same as `previousFrame() == nullptr` but will not fail 439 // assertion checks if the field is not a SmallInt. 440 return at(kPreviousFrameOffset) == RawSmallInt::fromWord(0); 441} 442 443inline RawObject* Frame::stashedValueStackTop() { 444 word depth = RawSmallInt::cast(at(kPreviousFrameOffset)).value(); 445 return reinterpret_cast<RawObject*>(this) - depth; 446} 447 448inline RawObject Frame::stashedPopValue() { 449 RawObject result = *stashedValueStackTop(); 450 // valueStackTop() contains the stack depth as a RawSmallInt rather than a 451 // pointer, so decrement it by 1. 452 word depth = RawSmallInt::cast(at(kPreviousFrameOffset)).value(); 453 atPut(kPreviousFrameOffset, RawSmallInt::fromWord(depth - 1)); 454 return result; 455} 456 457inline void Frame::stashStackSize(word size) { 458 atPut(kPreviousFrameOffset, RawSmallInt::fromWord(size)); 459} 460 461inline RawObject TryBlock::asSmallInt() const { 462 RawObject obj{value_}; 463 DCHECK(obj.isSmallInt(), "expected small integer"); 464 return obj; 465} 466 467inline TryBlock::Kind TryBlock::kind() const { 468 return static_cast<Kind>((value_ >> kKindOffset) & kKindMask); 469} 470 471inline word TryBlock::handler() const { 472 return (value_ >> kHandlerOffset) & kHandlerMask; 473} 474 475inline word TryBlock::level() const { 476 return (value_ >> kLevelOffset) & kLevelMask; 477} 478 479} // namespace py