• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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