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 13namespace v8 { 14namespace internal { 15namespace compiler { 16 17class 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// 91class 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 97 inline int GetTotalFrameSlotCount() const { 98 return slot_allocator_.Size() + return_slot_count_; 99 } 100 inline int GetFixedSlotCount() const { return fixed_slot_count_; } 101 inline int GetSpillSlotCount() const { return spill_slot_count_; } 102 inline int GetReturnSlotCount() const { return return_slot_count_; } 103 104 void SetAllocatedRegisters(BitVector* regs) { 105 DCHECK_NULL(allocated_registers_); 106 allocated_registers_ = regs; 107 } 108 109 void SetAllocatedDoubleRegisters(BitVector* regs) { 110 DCHECK_NULL(allocated_double_registers_); 111 allocated_double_registers_ = regs; 112 } 113 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 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 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 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. 200class FrameOffset { 201 public: 202 inline bool from_stack_pointer() { return (offset_ & 1) == kFromSp; } 203 inline bool from_frame_pointer() { return (offset_ & 1) == kFromFp; } 204 inline int offset() { return offset_ & ~1; } 205 206 inline static FrameOffset FromStackPointer(int offset) { 207 DCHECK_EQ(0, offset & 1); 208 return FrameOffset(offset | kFromSp); 209 } 210 211 inline static FrameOffset FromFramePointer(int offset) { 212 DCHECK_EQ(0, offset & 1); 213 return FrameOffset(offset | kFromFp); 214 } 215 216 private: 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. 227class FrameAccessState : public ZoneObject { 228 public: 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 235 const Frame* frame() const { return frame_; } 236 V8_EXPORT_PRIVATE void MarkHasFrame(bool state); 237 238 int sp_delta() const { return sp_delta_; } 239 void ClearSPDelta() { sp_delta_ = 0; } 240 void IncreaseSPDelta(int amount) { sp_delta_ += amount; } 241 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. 246 bool has_frame() const { return has_frame_; } 247 248 void SetFrameAccessToDefault(); 249 void SetFrameAccessToFP() { access_frame_with_fp_ = true; } 250 void SetFrameAccessToSP() { access_frame_with_fp_ = false; } 251 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 } 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