1 // Copyright 2014 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_COMPILER_FRAME_H_ 6 #define V8_COMPILER_FRAME_H_ 7 8 #include "src/base/bits.h" 9 #include "src/codegen/aligned-slot-allocator.h" 10 #include "src/execution/frame-constants.h" 11 #include "src/utils/bit-vector.h" 12 13 namespace v8 { 14 namespace internal { 15 namespace compiler { 16 17 class CallDescriptor; 18 19 // Collects the spill slot and other frame slot requirements for a compiled 20 // function. Frames are usually populated by the register allocator and are used 21 // by Linkage to generate code for the prologue and epilogue to compiled 22 // code. Frame objects must be considered immutable once they've been 23 // instantiated and the basic information about the frame has been collected 24 // into them. Mutable state associated with the frame is stored separately in 25 // FrameAccessState. 26 // 27 // Frames are divided up into four regions. 28 // - The first is the fixed header, which always has a constant size and can be 29 // predicted before code generation begins depending on the type of code being 30 // generated. 31 // - The second is the region for spill slots, which is immediately below the 32 // fixed header and grows as the register allocator needs to spill to the 33 // stack and asks the frame for more space. 34 // - The third region, which contains the callee-saved registers must be 35 // reserved after register allocation, since its size can only be precisely 36 // determined after register allocation once the number of used callee-saved 37 // register is certain. 38 // - The fourth region is a scratch area for return values from other functions 39 // called, if multiple returns cannot all be passed in registers. This region 40 // Must be last in a stack frame, so that it is positioned immediately below 41 // the stack frame of a callee to store to. 42 // 43 // The frame region immediately below the fixed header contains spill slots 44 // starting at slot 4 for JSFunctions. The callee-saved frame region below that 45 // starts at 4+spill_slot_count_. Callee stack slots correspond to 46 // parameters that are accessible through negative slot ids. 47 // 48 // Every slot of a caller or callee frame is accessible by the register 49 // allocator and gap resolver with a SpillSlotOperand containing its 50 // corresponding slot id. 51 // 52 // Below an example JSFunction Frame with slot ids, frame regions and contents: 53 // 54 // slot JS frame 55 // +-----------------+-------------------------------- 56 // -n-1 | parameter n | ^ 57 // |- - - - - - - - -| | 58 // -n | parameter n-1 | Caller 59 // ... | ... | frame slots 60 // -2 | parameter 1 | (slot < 0) 61 // |- - - - - - - - -| | 62 // -1 | parameter 0 | v 63 // -----+-----------------+-------------------------------- 64 // 0 | return addr | ^ ^ 65 // |- - - - - - - - -| | | 66 // 1 | saved frame ptr | Fixed | 67 // |- - - - - - - - -| Header <-- frame ptr | 68 // 2 |Context/Frm. Type| | | 69 // |- - - - - - - - -| | | 70 // 3 | [JSFunction] | v | 71 // +-----------------+---- | 72 // 4 | spill 1 | ^ Callee 73 // |- - - - - - - - -| | frame slots 74 // ... | ... | Spill slots (slot >= 0) 75 // |- - - - - - - - -| | | 76 // m+3 | spill m | v | 77 // +-----------------+---- | 78 // m+4 | callee-saved 1 | ^ | 79 // |- - - - - - - - -| | | 80 // | ... | Callee-saved | 81 // |- - - - - - - - -| | | 82 // m+r+3 | callee-saved r | v | 83 // +-----------------+---- | 84 // m+r+4 | return 0 | ^ | 85 // |- - - - - - - - -| | | 86 // | ... | Return | 87 // |- - - - - - - - -| | | 88 // | return q-1 | v v 89 // -----+-----------------+----- <-- stack ptr ------------- 90 // 91 class V8_EXPORT_PRIVATE Frame : public ZoneObject { 92 public: 93 explicit Frame(int fixed_frame_size_in_slots); 94 Frame(const Frame&) = delete; 95 Frame& operator=(const Frame&) = delete; 96 GetTotalFrameSlotCount()97 inline int GetTotalFrameSlotCount() const { 98 return slot_allocator_.Size() + return_slot_count_; 99 } GetFixedSlotCount()100 inline int GetFixedSlotCount() const { return fixed_slot_count_; } GetSpillSlotCount()101 inline int GetSpillSlotCount() const { return spill_slot_count_; } GetReturnSlotCount()102 inline int GetReturnSlotCount() const { return return_slot_count_; } 103 SetAllocatedRegisters(BitVector * regs)104 void SetAllocatedRegisters(BitVector* regs) { 105 DCHECK_NULL(allocated_registers_); 106 allocated_registers_ = regs; 107 } 108 SetAllocatedDoubleRegisters(BitVector * regs)109 void SetAllocatedDoubleRegisters(BitVector* regs) { 110 DCHECK_NULL(allocated_double_registers_); 111 allocated_double_registers_ = regs; 112 } 113 DidAllocateDoubleRegisters()114 bool DidAllocateDoubleRegisters() const { 115 return !allocated_double_registers_->IsEmpty(); 116 } 117 118 void AlignSavedCalleeRegisterSlots(int alignment = kDoubleSize) { 119 DCHECK(!frame_aligned_); 120 #if DEBUG 121 spill_slots_finished_ = true; 122 #endif 123 DCHECK(base::bits::IsPowerOfTwo(alignment)); 124 DCHECK_LE(alignment, kSimd128Size); 125 int alignment_in_slots = AlignedSlotAllocator::NumSlotsForWidth(alignment); 126 int padding = slot_allocator_.Align(alignment_in_slots); 127 spill_slot_count_ += padding; 128 } 129 AllocateSavedCalleeRegisterSlots(int count)130 void AllocateSavedCalleeRegisterSlots(int count) { 131 DCHECK(!frame_aligned_); 132 #if DEBUG 133 spill_slots_finished_ = true; 134 #endif 135 slot_allocator_.AllocateUnaligned(count); 136 } 137 138 int AllocateSpillSlot(int width, int alignment = 0) { 139 DCHECK_EQ(GetTotalFrameSlotCount(), 140 fixed_slot_count_ + spill_slot_count_ + return_slot_count_); 141 // Never allocate spill slots after the callee-saved slots are defined. 142 DCHECK(!spill_slots_finished_); 143 DCHECK(!frame_aligned_); 144 int actual_width = std::max({width, AlignedSlotAllocator::kSlotSize}); 145 int actual_alignment = 146 std::max({alignment, AlignedSlotAllocator::kSlotSize}); 147 int slots = AlignedSlotAllocator::NumSlotsForWidth(actual_width); 148 int old_end = slot_allocator_.Size(); 149 int slot; 150 if (actual_width == actual_alignment) { 151 // Simple allocation, alignment equal to width. 152 slot = slot_allocator_.Allocate(slots); 153 } else { 154 // Complex allocation, alignment different from width. 155 if (actual_alignment > AlignedSlotAllocator::kSlotSize) { 156 // Alignment required. 157 int alignment_in_slots = 158 AlignedSlotAllocator::NumSlotsForWidth(actual_alignment); 159 slot_allocator_.Align(alignment_in_slots); 160 } 161 slot = slot_allocator_.AllocateUnaligned(slots); 162 } 163 int end = slot_allocator_.Size(); 164 165 spill_slot_count_ += end - old_end; 166 return slot + slots - 1; 167 } 168 EnsureReturnSlots(int count)169 void EnsureReturnSlots(int count) { 170 DCHECK(!frame_aligned_); 171 return_slot_count_ = std::max(return_slot_count_, count); 172 } 173 174 void AlignFrame(int alignment = kDoubleSize); 175 ReserveSpillSlots(size_t slot_count)176 int ReserveSpillSlots(size_t slot_count) { 177 DCHECK_EQ(0, spill_slot_count_); 178 DCHECK(!frame_aligned_); 179 spill_slot_count_ += static_cast<int>(slot_count); 180 slot_allocator_.AllocateUnaligned(static_cast<int>(slot_count)); 181 return slot_allocator_.Size() - 1; 182 } 183 184 private: 185 int fixed_slot_count_; 186 int spill_slot_count_ = 0; 187 // Account for return slots separately. Conceptually, they follow all 188 // allocated spill slots. 189 int return_slot_count_ = 0; 190 AlignedSlotAllocator slot_allocator_; 191 BitVector* allocated_registers_; 192 BitVector* allocated_double_registers_; 193 #if DEBUG 194 bool spill_slots_finished_ = false; 195 bool frame_aligned_ = false; 196 #endif 197 }; 198 199 // Represents an offset from either the stack pointer or frame pointer. 200 class FrameOffset { 201 public: from_stack_pointer()202 inline bool from_stack_pointer() { return (offset_ & 1) == kFromSp; } from_frame_pointer()203 inline bool from_frame_pointer() { return (offset_ & 1) == kFromFp; } offset()204 inline int offset() { return offset_ & ~1; } 205 FromStackPointer(int offset)206 inline static FrameOffset FromStackPointer(int offset) { 207 DCHECK_EQ(0, offset & 1); 208 return FrameOffset(offset | kFromSp); 209 } 210 FromFramePointer(int offset)211 inline static FrameOffset FromFramePointer(int offset) { 212 DCHECK_EQ(0, offset & 1); 213 return FrameOffset(offset | kFromFp); 214 } 215 216 private: FrameOffset(int offset)217 explicit FrameOffset(int offset) : offset_(offset) {} 218 219 int offset_; // Encodes SP or FP in the low order bit. 220 221 static const int kFromSp = 1; 222 static const int kFromFp = 0; 223 }; 224 225 // Encapsulates the mutable state maintained during code generation about the 226 // current function's frame. 227 class FrameAccessState : public ZoneObject { 228 public: FrameAccessState(const Frame * const frame)229 explicit FrameAccessState(const Frame* const frame) 230 : frame_(frame), 231 access_frame_with_fp_(false), 232 sp_delta_(0), 233 has_frame_(false) {} 234 frame()235 const Frame* frame() const { return frame_; } 236 V8_EXPORT_PRIVATE void MarkHasFrame(bool state); 237 sp_delta()238 int sp_delta() const { return sp_delta_; } ClearSPDelta()239 void ClearSPDelta() { sp_delta_ = 0; } IncreaseSPDelta(int amount)240 void IncreaseSPDelta(int amount) { sp_delta_ += amount; } 241 access_frame_with_fp()242 bool access_frame_with_fp() const { return access_frame_with_fp_; } 243 244 // Regardless of how we access slots on the stack - using sp or fp - do we 245 // have a frame, at the current stage in code generation. has_frame()246 bool has_frame() const { return has_frame_; } 247 248 void SetFrameAccessToDefault(); SetFrameAccessToFP()249 void SetFrameAccessToFP() { access_frame_with_fp_ = true; } SetFrameAccessToSP()250 void SetFrameAccessToSP() { access_frame_with_fp_ = false; } 251 GetSPToFPSlotCount()252 int GetSPToFPSlotCount() const { 253 int frame_slot_count = 254 (has_frame() ? frame()->GetTotalFrameSlotCount() : kElidedFrameSlots) - 255 StandardFrameConstants::kFixedSlotCountAboveFp; 256 return frame_slot_count + sp_delta(); 257 } GetSPToFPOffset()258 int GetSPToFPOffset() const { 259 return GetSPToFPSlotCount() * kSystemPointerSize; 260 } 261 262 // Get the frame offset for a given spill slot. The location depends on the 263 // calling convention and the specific frame layout, and may thus be 264 // architecture-specific. Negative spill slots indicate arguments on the 265 // caller's frame. 266 FrameOffset GetFrameOffset(int spill_slot) const; 267 268 private: 269 const Frame* const frame_; 270 bool access_frame_with_fp_; 271 int sp_delta_; 272 bool has_frame_; 273 }; 274 } // namespace compiler 275 } // namespace internal 276 } // namespace v8 277 278 #endif // V8_COMPILER_FRAME_H_ 279