]>
Commit | Line | Data |
---|---|---|
ba379fdc | 1 | /* |
b80e6193 | 2 | * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. |
ba379fdc A |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #ifndef LinkBuffer_h | |
27 | #define LinkBuffer_h | |
28 | ||
ba379fdc A |
29 | #if ENABLE(ASSEMBLER) |
30 | ||
14957cd0 A |
31 | #define DUMP_LINK_STATISTICS 0 |
32 | #define DUMP_CODE 0 | |
33 | ||
ba379fdc A |
34 | #include <MacroAssembler.h> |
35 | #include <wtf/Noncopyable.h> | |
36 | ||
37 | namespace JSC { | |
38 | ||
14957cd0 A |
39 | class JSGlobalData; |
40 | ||
ba379fdc A |
41 | // LinkBuffer: |
42 | // | |
43 | // This class assists in linking code generated by the macro assembler, once code generation | |
44 | // has been completed, and the code has been copied to is final location in memory. At this | |
45 | // time pointers to labels within the code may be resolved, and relative offsets to external | |
46 | // addresses may be fixed. | |
47 | // | |
48 | // Specifically: | |
49 | // * Jump objects may be linked to external targets, | |
50 | // * The address of Jump objects may taken, such that it can later be relinked. | |
51 | // * The return address of a Call may be acquired. | |
52 | // * The address of a Label pointing into the code may be resolved. | |
53 | // * The value referenced by a DataLabel may be set. | |
54 | // | |
14957cd0 A |
55 | class LinkBuffer { |
56 | WTF_MAKE_NONCOPYABLE(LinkBuffer); | |
ba379fdc | 57 | typedef MacroAssemblerCodeRef CodeRef; |
b80e6193 | 58 | typedef MacroAssemblerCodePtr CodePtr; |
ba379fdc A |
59 | typedef MacroAssembler::Label Label; |
60 | typedef MacroAssembler::Jump Jump; | |
61 | typedef MacroAssembler::JumpList JumpList; | |
62 | typedef MacroAssembler::Call Call; | |
14957cd0 | 63 | typedef MacroAssembler::DataLabelCompact DataLabelCompact; |
ba379fdc A |
64 | typedef MacroAssembler::DataLabel32 DataLabel32; |
65 | typedef MacroAssembler::DataLabelPtr DataLabelPtr; | |
b80e6193 A |
66 | #if ENABLE(BRANCH_COMPACTION) |
67 | typedef MacroAssembler::LinkRecord LinkRecord; | |
68 | typedef MacroAssembler::JumpLinkType JumpLinkType; | |
69 | #endif | |
ba379fdc A |
70 | |
71 | public: | |
14957cd0 | 72 | LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, PassRefPtr<ExecutablePool> executablePool) |
ba379fdc | 73 | : m_executablePool(executablePool) |
b80e6193 A |
74 | , m_size(0) |
75 | , m_code(0) | |
76 | , m_assembler(masm) | |
14957cd0 A |
77 | , m_globalData(&globalData) |
78 | #ifndef NDEBUG | |
79 | , m_completed(false) | |
80 | #endif | |
81 | { | |
82 | linkCode(); | |
83 | } | |
84 | ||
85 | LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, ExecutableAllocator& allocator) | |
86 | : m_executablePool(allocator.poolForSize(globalData, masm->m_assembler.codeSize())) | |
87 | , m_size(0) | |
88 | , m_code(0) | |
89 | , m_assembler(masm) | |
90 | , m_globalData(&globalData) | |
ba379fdc A |
91 | #ifndef NDEBUG |
92 | , m_completed(false) | |
93 | #endif | |
94 | { | |
14957cd0 | 95 | linkCode(); |
ba379fdc A |
96 | } |
97 | ||
98 | ~LinkBuffer() | |
99 | { | |
100 | ASSERT(m_completed); | |
101 | } | |
102 | ||
103 | // These methods are used to link or set values at code generation time. | |
104 | ||
105 | void link(Call call, FunctionPtr function) | |
106 | { | |
107 | ASSERT(call.isFlagSet(Call::Linkable)); | |
b80e6193 | 108 | call.m_jmp = applyOffset(call.m_jmp); |
ba379fdc A |
109 | MacroAssembler::linkCall(code(), call, function); |
110 | } | |
111 | ||
112 | void link(Jump jump, CodeLocationLabel label) | |
113 | { | |
b80e6193 | 114 | jump.m_jmp = applyOffset(jump.m_jmp); |
ba379fdc A |
115 | MacroAssembler::linkJump(code(), jump, label); |
116 | } | |
117 | ||
118 | void link(JumpList list, CodeLocationLabel label) | |
119 | { | |
120 | for (unsigned i = 0; i < list.m_jumps.size(); ++i) | |
b80e6193 | 121 | link(list.m_jumps[i], label); |
ba379fdc A |
122 | } |
123 | ||
124 | void patch(DataLabelPtr label, void* value) | |
125 | { | |
14957cd0 | 126 | AssemblerLabel target = applyOffset(label.m_label); |
b80e6193 | 127 | MacroAssembler::linkPointer(code(), target, value); |
ba379fdc A |
128 | } |
129 | ||
130 | void patch(DataLabelPtr label, CodeLocationLabel value) | |
131 | { | |
14957cd0 | 132 | AssemblerLabel target = applyOffset(label.m_label); |
b80e6193 | 133 | MacroAssembler::linkPointer(code(), target, value.executableAddress()); |
ba379fdc A |
134 | } |
135 | ||
136 | // These methods are used to obtain handles to allow the code to be relinked / repatched later. | |
137 | ||
138 | CodeLocationCall locationOf(Call call) | |
139 | { | |
140 | ASSERT(call.isFlagSet(Call::Linkable)); | |
141 | ASSERT(!call.isFlagSet(Call::Near)); | |
b80e6193 | 142 | return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp))); |
ba379fdc A |
143 | } |
144 | ||
145 | CodeLocationNearCall locationOfNearCall(Call call) | |
146 | { | |
147 | ASSERT(call.isFlagSet(Call::Linkable)); | |
148 | ASSERT(call.isFlagSet(Call::Near)); | |
b80e6193 | 149 | return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp))); |
ba379fdc A |
150 | } |
151 | ||
152 | CodeLocationLabel locationOf(Label label) | |
153 | { | |
b80e6193 | 154 | return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); |
ba379fdc A |
155 | } |
156 | ||
157 | CodeLocationDataLabelPtr locationOf(DataLabelPtr label) | |
158 | { | |
b80e6193 | 159 | return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); |
ba379fdc A |
160 | } |
161 | ||
162 | CodeLocationDataLabel32 locationOf(DataLabel32 label) | |
163 | { | |
b80e6193 | 164 | return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); |
ba379fdc | 165 | } |
14957cd0 A |
166 | |
167 | CodeLocationDataLabelCompact locationOf(DataLabelCompact label) | |
168 | { | |
169 | return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); | |
170 | } | |
ba379fdc A |
171 | |
172 | // This method obtains the return address of the call, given as an offset from | |
173 | // the start of the code. | |
174 | unsigned returnAddressOffset(Call call) | |
175 | { | |
b80e6193 | 176 | call.m_jmp = applyOffset(call.m_jmp); |
ba379fdc A |
177 | return MacroAssembler::getLinkerCallReturnOffset(call); |
178 | } | |
179 | ||
180 | // Upon completion of all patching either 'finalizeCode()' or 'finalizeCodeAddendum()' should be called | |
181 | // once to complete generation of the code. 'finalizeCode()' is suited to situations | |
182 | // where the executable pool must also be retained, the lighter-weight 'finalizeCodeAddendum()' is | |
183 | // suited to adding to an existing allocation. | |
184 | CodeRef finalizeCode() | |
185 | { | |
186 | performFinalization(); | |
187 | ||
188 | return CodeRef(m_code, m_executablePool, m_size); | |
189 | } | |
b80e6193 | 190 | |
ba379fdc A |
191 | CodeLocationLabel finalizeCodeAddendum() |
192 | { | |
193 | performFinalization(); | |
194 | ||
195 | return CodeLocationLabel(code()); | |
196 | } | |
197 | ||
b80e6193 A |
198 | CodePtr trampolineAt(Label label) |
199 | { | |
200 | return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); | |
201 | } | |
202 | ||
14957cd0 A |
203 | #ifndef NDEBUG |
204 | void* debugAddress() | |
205 | { | |
206 | return m_code; | |
207 | } | |
208 | #endif | |
209 | ||
ba379fdc | 210 | private: |
b80e6193 A |
211 | template <typename T> T applyOffset(T src) |
212 | { | |
213 | #if ENABLE(BRANCH_COMPACTION) | |
214 | src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); | |
215 | #endif | |
216 | return src; | |
217 | } | |
218 | ||
ba379fdc A |
219 | // Keep this private! - the underlying code should only be obtained externally via |
220 | // finalizeCode() or finalizeCodeAddendum(). | |
221 | void* code() | |
222 | { | |
223 | return m_code; | |
224 | } | |
225 | ||
14957cd0 | 226 | void linkCode() |
b80e6193 | 227 | { |
b80e6193 A |
228 | ASSERT(!m_code); |
229 | #if !ENABLE(BRANCH_COMPACTION) | |
14957cd0 A |
230 | m_code = m_assembler->m_assembler.executableCopy(*m_globalData, m_executablePool.get()); |
231 | m_size = m_assembler->m_assembler.codeSize(); | |
232 | ASSERT(m_code); | |
b80e6193 | 233 | #else |
14957cd0 A |
234 | size_t initialSize = m_assembler->m_assembler.codeSize(); |
235 | m_code = (uint8_t*)m_executablePool->alloc(*m_globalData, initialSize); | |
b80e6193 A |
236 | if (!m_code) |
237 | return; | |
14957cd0 | 238 | ExecutableAllocator::makeWritable(m_code, initialSize); |
b80e6193 A |
239 | uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); |
240 | uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); | |
b80e6193 A |
241 | int readPtr = 0; |
242 | int writePtr = 0; | |
243 | Vector<LinkRecord>& jumpsToLink = m_assembler->jumpsToLink(); | |
244 | unsigned jumpCount = jumpsToLink.size(); | |
245 | for (unsigned i = 0; i < jumpCount; ++i) { | |
246 | int offset = readPtr - writePtr; | |
247 | ASSERT(!(offset & 1)); | |
248 | ||
249 | // Copy the instructions from the last jump to the current one. | |
250 | size_t regionSize = jumpsToLink[i].from() - readPtr; | |
14957cd0 A |
251 | uint16_t* copySource = reinterpret_cast<uint16_t*>(inData + readPtr); |
252 | uint16_t* copyEnd = reinterpret_cast<uint16_t*>(inData + readPtr + regionSize); | |
253 | uint16_t* copyDst = reinterpret_cast<uint16_t*>(outData + writePtr); | |
254 | ASSERT(!(regionSize % 2)); | |
255 | ASSERT(!(readPtr % 2)); | |
256 | ASSERT(!(writePtr % 2)); | |
257 | while (copySource != copyEnd) | |
258 | *copyDst++ = *copySource++; | |
b80e6193 A |
259 | m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); |
260 | readPtr += regionSize; | |
261 | writePtr += regionSize; | |
262 | ||
263 | // Calculate absolute address of the jump target, in the case of backwards | |
264 | // branches we need to be precise, forward branches we are pessimistic | |
265 | const uint8_t* target; | |
266 | if (jumpsToLink[i].to() >= jumpsToLink[i].from()) | |
14957cd0 | 267 | target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far |
b80e6193 | 268 | else |
14957cd0 | 269 | target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); |
b80e6193 | 270 | |
14957cd0 | 271 | JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); |
b80e6193 A |
272 | // Compact branch if we can... |
273 | if (m_assembler->canCompact(jumpsToLink[i].type())) { | |
274 | // Step back in the write stream | |
275 | int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); | |
276 | if (delta) { | |
277 | writePtr -= delta; | |
278 | m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); | |
279 | } | |
280 | } | |
281 | jumpsToLink[i].setFrom(writePtr); | |
282 | } | |
283 | // Copy everything after the last jump | |
14957cd0 A |
284 | memcpy(outData + writePtr, inData + readPtr, initialSize - readPtr); |
285 | m_assembler->recordLinkOffsets(readPtr, initialSize, readPtr - writePtr); | |
b80e6193 | 286 | |
14957cd0 A |
287 | for (unsigned i = 0; i < jumpCount; ++i) { |
288 | uint8_t* location = outData + jumpsToLink[i].from(); | |
289 | uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); | |
290 | m_assembler->link(jumpsToLink[i], location, target); | |
b80e6193 A |
291 | } |
292 | ||
293 | jumpsToLink.clear(); | |
14957cd0 | 294 | m_size = writePtr + initialSize - readPtr; |
b80e6193 | 295 | m_executablePool->tryShrink(m_code, initialSize, m_size); |
14957cd0 A |
296 | |
297 | #if DUMP_LINK_STATISTICS | |
298 | dumpLinkStatistics(m_code, initialSize, m_size); | |
299 | #endif | |
300 | #if DUMP_CODE | |
301 | dumpCode(m_code, m_size); | |
302 | #endif | |
b80e6193 A |
303 | #endif |
304 | } | |
305 | ||
ba379fdc A |
306 | void performFinalization() |
307 | { | |
308 | #ifndef NDEBUG | |
309 | ASSERT(!m_completed); | |
310 | m_completed = true; | |
311 | #endif | |
312 | ||
313 | ExecutableAllocator::makeExecutable(code(), m_size); | |
314 | ExecutableAllocator::cacheFlush(code(), m_size); | |
315 | } | |
316 | ||
14957cd0 A |
317 | #if DUMP_LINK_STATISTICS |
318 | static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize) | |
319 | { | |
320 | static unsigned linkCount = 0; | |
321 | static unsigned totalInitialSize = 0; | |
322 | static unsigned totalFinalSize = 0; | |
323 | linkCount++; | |
324 | totalInitialSize += initialSize; | |
325 | totalFinalSize += finalSize; | |
326 | printf("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", | |
327 | code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize), | |
328 | static_cast<unsigned>(initialSize - finalSize), | |
329 | 100.0 * (initialSize - finalSize) / initialSize); | |
330 | printf("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", | |
331 | linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, | |
332 | 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); | |
333 | } | |
334 | #endif | |
335 | ||
336 | #if DUMP_CODE | |
337 | static void dumpCode(void* code, size_t size) | |
338 | { | |
339 | #if CPU(ARM_THUMB2) | |
340 | // Dump the generated code in an asm file format that can be assembled and then disassembled | |
341 | // for debugging purposes. For example, save this output as jit.s: | |
342 | // gcc -arch armv7 -c jit.s | |
343 | // otool -tv jit.o | |
344 | static unsigned codeCount = 0; | |
345 | unsigned short* tcode = static_cast<unsigned short*>(code); | |
346 | size_t tsize = size / sizeof(short); | |
347 | char nameBuf[128]; | |
348 | snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); | |
349 | printf("\t.syntax unified\n" | |
350 | "\t.section\t__TEXT,__text,regular,pure_instructions\n" | |
351 | "\t.globl\t%s\n" | |
352 | "\t.align 2\n" | |
353 | "\t.code 16\n" | |
354 | "\t.thumb_func\t%s\n" | |
355 | "# %p\n" | |
356 | "%s:\n", nameBuf, nameBuf, code, nameBuf); | |
357 | ||
358 | for (unsigned i = 0; i < tsize; i++) | |
359 | printf("\t.short\t0x%x\n", tcode[i]); | |
360 | #endif | |
361 | } | |
362 | #endif | |
363 | ||
ba379fdc | 364 | RefPtr<ExecutablePool> m_executablePool; |
ba379fdc | 365 | size_t m_size; |
b80e6193 A |
366 | void* m_code; |
367 | MacroAssembler* m_assembler; | |
14957cd0 | 368 | JSGlobalData* m_globalData; |
ba379fdc A |
369 | #ifndef NDEBUG |
370 | bool m_completed; | |
371 | #endif | |
372 | }; | |
373 | ||
374 | } // namespace JSC | |
375 | ||
376 | #endif // ENABLE(ASSEMBLER) | |
377 | ||
378 | #endif // LinkBuffer_h |