|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef jit_shared_IonAssemblerBuffer_h |
|
8 #define jit_shared_IonAssemblerBuffer_h |
|
9 |
|
10 // needed for the definition of Label :( |
|
11 #include "jit/shared/Assembler-shared.h" |
|
12 |
|
13 namespace js { |
|
14 namespace jit { |
|
15 |
|
16 // This should theoretically reside inside of AssemblerBuffer, but that won't be nice |
|
17 // AssemblerBuffer is templated, BufferOffset would be indirectly. |
|
18 // A BufferOffset is the offset into a buffer, expressed in bytes of instructions. |
|
19 |
|
20 class BufferOffset |
|
21 { |
|
22 int offset; |
|
23 public: |
|
24 friend BufferOffset nextOffset(); |
|
25 explicit BufferOffset(int offset_) : offset(offset_) {} |
|
26 // Return the offset as a raw integer. |
|
27 int getOffset() const { return offset; } |
|
28 |
|
29 // A BOffImm is a Branch Offset Immediate. It is an architecture-specific |
|
30 // structure that holds the immediate for a pc relative branch. |
|
31 // diffB takes the label for the destination of the branch, and encodes |
|
32 // the immediate for the branch. This will need to be fixed up later, since |
|
33 // A pool may be inserted between the branch and its destination |
|
34 template <class BOffImm> |
|
35 BOffImm diffB(BufferOffset other) const { |
|
36 return BOffImm(offset - other.offset); |
|
37 } |
|
38 |
|
39 template <class BOffImm> |
|
40 BOffImm diffB(Label *other) const { |
|
41 JS_ASSERT(other->bound()); |
|
42 return BOffImm(offset - other->offset()); |
|
43 } |
|
44 |
|
45 explicit BufferOffset(Label *l) : offset(l->offset()) { |
|
46 } |
|
47 explicit BufferOffset(RepatchLabel *l) : offset(l->offset()) { |
|
48 } |
|
49 |
|
50 BufferOffset() : offset(INT_MIN) {} |
|
51 bool assigned() const { return offset != INT_MIN; }; |
|
52 }; |
|
53 |
|
54 template<int SliceSize> |
|
55 struct BufferSlice { |
|
56 protected: |
|
57 BufferSlice<SliceSize> *prev; |
|
58 BufferSlice<SliceSize> *next; |
|
59 // How much data has been added to the current node. |
|
60 uint32_t nodeSize; |
|
61 public: |
|
62 BufferSlice *getNext() { return this->next; } |
|
63 BufferSlice *getPrev() { return this->prev; } |
|
64 void setNext(BufferSlice<SliceSize> *next_) { |
|
65 JS_ASSERT(this->next == nullptr); |
|
66 JS_ASSERT(next_->prev == nullptr); |
|
67 this->next = next_; |
|
68 next_->prev = this; |
|
69 } |
|
70 |
|
71 mozilla::Array<uint8_t, SliceSize> instructions; |
|
72 unsigned int size() { |
|
73 return nodeSize; |
|
74 } |
|
75 BufferSlice() : prev(nullptr), next(nullptr), nodeSize(0) {} |
|
76 void putBlob(uint32_t instSize, uint8_t* inst) { |
|
77 if (inst != nullptr) |
|
78 memcpy(&instructions[size()], inst, instSize); |
|
79 nodeSize += instSize; |
|
80 } |
|
81 }; |
|
82 |
|
83 template<int SliceSize, class Inst> |
|
84 struct AssemblerBuffer |
|
85 { |
|
86 public: |
|
87 AssemblerBuffer() : head(nullptr), tail(nullptr), m_oom(false), m_bail(false), bufferSize(0), LifoAlloc_(8192) {} |
|
88 protected: |
|
89 typedef BufferSlice<SliceSize> Slice; |
|
90 typedef AssemblerBuffer<SliceSize, Inst> AssemblerBuffer_; |
|
91 Slice *head; |
|
92 Slice *tail; |
|
93 public: |
|
94 bool m_oom; |
|
95 bool m_bail; |
|
96 // How much data has been added to the buffer thusfar. |
|
97 uint32_t bufferSize; |
|
98 uint32_t lastInstSize; |
|
99 bool isAligned(int alignment) const { |
|
100 // make sure the requested alignment is a power of two. |
|
101 JS_ASSERT((alignment & (alignment-1)) == 0); |
|
102 return !(size() & (alignment - 1)); |
|
103 } |
|
104 virtual Slice *newSlice(LifoAlloc &a) { |
|
105 Slice *tmp = static_cast<Slice*>(a.alloc(sizeof(Slice))); |
|
106 if (!tmp) { |
|
107 m_oom = true; |
|
108 return nullptr; |
|
109 } |
|
110 new (tmp) Slice; |
|
111 return tmp; |
|
112 } |
|
113 bool ensureSpace(int size) { |
|
114 if (tail != nullptr && tail->size()+size <= SliceSize) |
|
115 return true; |
|
116 Slice *tmp = newSlice(LifoAlloc_); |
|
117 if (tmp == nullptr) |
|
118 return false; |
|
119 if (tail != nullptr) { |
|
120 bufferSize += tail->size(); |
|
121 tail->setNext(tmp); |
|
122 } |
|
123 tail = tmp; |
|
124 if (head == nullptr) { |
|
125 finger = tmp; |
|
126 finger_offset = 0; |
|
127 head = tmp; |
|
128 } |
|
129 return true; |
|
130 } |
|
131 |
|
132 BufferOffset putByte(uint8_t value) { |
|
133 return putBlob(sizeof(value), (uint8_t*)&value); |
|
134 } |
|
135 |
|
136 BufferOffset putShort(uint16_t value) { |
|
137 return putBlob(sizeof(value), (uint8_t*)&value); |
|
138 } |
|
139 |
|
140 BufferOffset putInt(uint32_t value) { |
|
141 return putBlob(sizeof(value), (uint8_t*)&value); |
|
142 } |
|
143 BufferOffset putBlob(uint32_t instSize, uint8_t *inst) { |
|
144 if (!ensureSpace(instSize)) |
|
145 return BufferOffset(); |
|
146 BufferOffset ret = nextOffset(); |
|
147 tail->putBlob(instSize, inst); |
|
148 return ret; |
|
149 } |
|
150 unsigned int size() const { |
|
151 int executableSize; |
|
152 if (tail != nullptr) |
|
153 executableSize = bufferSize + tail->size(); |
|
154 else |
|
155 executableSize = bufferSize; |
|
156 return executableSize; |
|
157 } |
|
158 unsigned int uncheckedSize() const { |
|
159 return size(); |
|
160 } |
|
161 bool oom() const { |
|
162 return m_oom || m_bail; |
|
163 } |
|
164 bool bail() const { |
|
165 return m_bail; |
|
166 } |
|
167 void fail_oom() { |
|
168 m_oom = true; |
|
169 } |
|
170 void fail_bail() { |
|
171 m_bail = true; |
|
172 } |
|
173 // finger for speeding up accesses |
|
174 Slice *finger; |
|
175 unsigned int finger_offset; |
|
176 Inst *getInst(BufferOffset off) { |
|
177 int local_off = off.getOffset(); |
|
178 // don't update the structure's finger in place, so there is the option |
|
179 // to not update it. |
|
180 Slice *cur = nullptr; |
|
181 int cur_off; |
|
182 // get the offset that we'd be dealing with by walking through backwards |
|
183 int end_off = bufferSize - local_off; |
|
184 // If end_off is negative, then it is in the last chunk, and there is no |
|
185 // real work to be done. |
|
186 if (end_off <= 0) { |
|
187 return (Inst*)&tail->instructions[-end_off]; |
|
188 } |
|
189 bool used_finger = false; |
|
190 int finger_off = abs((int)(local_off - finger_offset)); |
|
191 if (finger_off < Min(local_off, end_off)) { |
|
192 // The finger offset is minimal, use the finger. |
|
193 cur = finger; |
|
194 cur_off = finger_offset; |
|
195 used_finger = true; |
|
196 } else if (local_off < end_off) { |
|
197 // it is closest to the start |
|
198 cur = head; |
|
199 cur_off = 0; |
|
200 } else { |
|
201 // it is closest to the end |
|
202 cur = tail; |
|
203 cur_off = bufferSize; |
|
204 } |
|
205 int count = 0; |
|
206 if (local_off < cur_off) { |
|
207 for (; cur != nullptr; cur = cur->getPrev(), cur_off -= cur->size()) { |
|
208 if (local_off >= cur_off) { |
|
209 local_off -= cur_off; |
|
210 break; |
|
211 } |
|
212 count++; |
|
213 } |
|
214 JS_ASSERT(cur != nullptr); |
|
215 } else { |
|
216 for (; cur != nullptr; cur = cur->getNext()) { |
|
217 int cur_size = cur->size(); |
|
218 if (local_off < cur_off + cur_size) { |
|
219 local_off -= cur_off; |
|
220 break; |
|
221 } |
|
222 cur_off += cur_size; |
|
223 count++; |
|
224 } |
|
225 JS_ASSERT(cur != nullptr); |
|
226 } |
|
227 if (count > 2 || used_finger) { |
|
228 finger = cur; |
|
229 finger_offset = cur_off; |
|
230 } |
|
231 // the offset within this node should not be larger than the node itself. |
|
232 JS_ASSERT(local_off < (int)cur->size()); |
|
233 return (Inst*)&cur->instructions[local_off]; |
|
234 } |
|
235 BufferOffset nextOffset() const { |
|
236 if (tail != nullptr) |
|
237 return BufferOffset(bufferSize + tail->size()); |
|
238 else |
|
239 return BufferOffset(bufferSize); |
|
240 } |
|
241 BufferOffset prevOffset() const { |
|
242 MOZ_ASSUME_UNREACHABLE("Don't current record lastInstSize"); |
|
243 } |
|
244 |
|
245 // Break the instruction stream so we can go back and edit it at this point |
|
246 void perforate() { |
|
247 Slice *tmp = newSlice(LifoAlloc_); |
|
248 if (!tmp) |
|
249 m_oom = true; |
|
250 bufferSize += tail->size(); |
|
251 tail->setNext(tmp); |
|
252 tail = tmp; |
|
253 } |
|
254 |
|
255 class AssemblerBufferInstIterator { |
|
256 private: |
|
257 BufferOffset bo; |
|
258 AssemblerBuffer_ *m_buffer; |
|
259 public: |
|
260 AssemblerBufferInstIterator(BufferOffset off, AssemblerBuffer_ *buff) : bo(off), m_buffer(buff) {} |
|
261 Inst *next() { |
|
262 Inst *i = m_buffer->getInst(bo); |
|
263 bo = BufferOffset(bo.getOffset()+i->size()); |
|
264 return cur(); |
|
265 }; |
|
266 Inst *cur() { |
|
267 return m_buffer->getInst(bo); |
|
268 } |
|
269 }; |
|
270 public: |
|
271 LifoAlloc LifoAlloc_; |
|
272 }; |
|
273 |
|
274 } // ion |
|
275 } // js |
|
276 #endif /* jit_shared_IonAssemblerBuffer_h */ |