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