]>
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 | ||
6fe7ccc8 A |
34 | #define GLOBAL_THUNK_ID reinterpret_cast<void*>(static_cast<intptr_t>(-1)) |
35 | #define REGEXP_CODE_ID reinterpret_cast<void*>(static_cast<intptr_t>(-2)) | |
36 | ||
37 | #include "JITCompilationEffort.h" | |
38 | #include "MacroAssembler.h" | |
39 | #include <wtf/DataLog.h> | |
ba379fdc A |
40 | #include <wtf/Noncopyable.h> |
41 | ||
42 | namespace JSC { | |
43 | ||
14957cd0 A |
44 | class JSGlobalData; |
45 | ||
ba379fdc A |
46 | // LinkBuffer: |
47 | // | |
48 | // This class assists in linking code generated by the macro assembler, once code generation | |
49 | // has been completed, and the code has been copied to is final location in memory. At this | |
50 | // time pointers to labels within the code may be resolved, and relative offsets to external | |
51 | // addresses may be fixed. | |
52 | // | |
53 | // Specifically: | |
54 | // * Jump objects may be linked to external targets, | |
55 | // * The address of Jump objects may taken, such that it can later be relinked. | |
56 | // * The return address of a Call may be acquired. | |
57 | // * The address of a Label pointing into the code may be resolved. | |
58 | // * The value referenced by a DataLabel may be set. | |
59 | // | |
14957cd0 A |
60 | class LinkBuffer { |
61 | WTF_MAKE_NONCOPYABLE(LinkBuffer); | |
ba379fdc | 62 | typedef MacroAssemblerCodeRef CodeRef; |
b80e6193 | 63 | typedef MacroAssemblerCodePtr CodePtr; |
ba379fdc A |
64 | typedef MacroAssembler::Label Label; |
65 | typedef MacroAssembler::Jump Jump; | |
6fe7ccc8 | 66 | typedef MacroAssembler::PatchableJump PatchableJump; |
ba379fdc A |
67 | typedef MacroAssembler::JumpList JumpList; |
68 | typedef MacroAssembler::Call Call; | |
14957cd0 | 69 | typedef MacroAssembler::DataLabelCompact DataLabelCompact; |
ba379fdc A |
70 | typedef MacroAssembler::DataLabel32 DataLabel32; |
71 | typedef MacroAssembler::DataLabelPtr DataLabelPtr; | |
b80e6193 A |
72 | #if ENABLE(BRANCH_COMPACTION) |
73 | typedef MacroAssembler::LinkRecord LinkRecord; | |
74 | typedef MacroAssembler::JumpLinkType JumpLinkType; | |
75 | #endif | |
ba379fdc A |
76 | |
77 | public: | |
6fe7ccc8 A |
78 | LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) |
79 | : m_size(0) | |
80 | #if ENABLE(BRANCH_COMPACTION) | |
81 | , m_initialSize(0) | |
14957cd0 | 82 | #endif |
14957cd0 A |
83 | , m_code(0) |
84 | , m_assembler(masm) | |
85 | , m_globalData(&globalData) | |
ba379fdc A |
86 | #ifndef NDEBUG |
87 | , m_completed(false) | |
6fe7ccc8 | 88 | , m_effort(effort) |
ba379fdc A |
89 | #endif |
90 | { | |
6fe7ccc8 | 91 | linkCode(ownerUID, effort); |
ba379fdc A |
92 | } |
93 | ||
94 | ~LinkBuffer() | |
95 | { | |
6fe7ccc8 A |
96 | ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); |
97 | } | |
98 | ||
99 | bool didFailToAllocate() const | |
100 | { | |
101 | return !m_executableMemory; | |
ba379fdc A |
102 | } |
103 | ||
6fe7ccc8 A |
104 | bool isValid() const |
105 | { | |
106 | return !didFailToAllocate(); | |
107 | } | |
108 | ||
ba379fdc A |
109 | // These methods are used to link or set values at code generation time. |
110 | ||
111 | void link(Call call, FunctionPtr function) | |
112 | { | |
113 | ASSERT(call.isFlagSet(Call::Linkable)); | |
6fe7ccc8 | 114 | call.m_label = applyOffset(call.m_label); |
ba379fdc A |
115 | MacroAssembler::linkCall(code(), call, function); |
116 | } | |
117 | ||
118 | void link(Jump jump, CodeLocationLabel label) | |
119 | { | |
6fe7ccc8 | 120 | jump.m_label = applyOffset(jump.m_label); |
ba379fdc A |
121 | MacroAssembler::linkJump(code(), jump, label); |
122 | } | |
123 | ||
124 | void link(JumpList list, CodeLocationLabel label) | |
125 | { | |
126 | for (unsigned i = 0; i < list.m_jumps.size(); ++i) | |
b80e6193 | 127 | link(list.m_jumps[i], label); |
ba379fdc A |
128 | } |
129 | ||
130 | void patch(DataLabelPtr label, void* value) | |
131 | { | |
14957cd0 | 132 | AssemblerLabel target = applyOffset(label.m_label); |
b80e6193 | 133 | MacroAssembler::linkPointer(code(), target, value); |
ba379fdc A |
134 | } |
135 | ||
136 | void patch(DataLabelPtr label, CodeLocationLabel value) | |
137 | { | |
14957cd0 | 138 | AssemblerLabel target = applyOffset(label.m_label); |
b80e6193 | 139 | MacroAssembler::linkPointer(code(), target, value.executableAddress()); |
ba379fdc A |
140 | } |
141 | ||
142 | // These methods are used to obtain handles to allow the code to be relinked / repatched later. | |
143 | ||
144 | CodeLocationCall locationOf(Call call) | |
145 | { | |
146 | ASSERT(call.isFlagSet(Call::Linkable)); | |
147 | ASSERT(!call.isFlagSet(Call::Near)); | |
6fe7ccc8 | 148 | return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); |
ba379fdc A |
149 | } |
150 | ||
151 | CodeLocationNearCall locationOfNearCall(Call call) | |
152 | { | |
153 | ASSERT(call.isFlagSet(Call::Linkable)); | |
154 | ASSERT(call.isFlagSet(Call::Near)); | |
6fe7ccc8 A |
155 | return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); |
156 | } | |
157 | ||
158 | CodeLocationLabel locationOf(PatchableJump jump) | |
159 | { | |
160 | return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump.m_jump.m_label))); | |
ba379fdc A |
161 | } |
162 | ||
163 | CodeLocationLabel locationOf(Label label) | |
164 | { | |
b80e6193 | 165 | return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); |
ba379fdc A |
166 | } |
167 | ||
168 | CodeLocationDataLabelPtr locationOf(DataLabelPtr label) | |
169 | { | |
b80e6193 | 170 | return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); |
ba379fdc A |
171 | } |
172 | ||
173 | CodeLocationDataLabel32 locationOf(DataLabel32 label) | |
174 | { | |
b80e6193 | 175 | return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); |
ba379fdc | 176 | } |
14957cd0 A |
177 | |
178 | CodeLocationDataLabelCompact locationOf(DataLabelCompact label) | |
179 | { | |
180 | return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); | |
181 | } | |
ba379fdc A |
182 | |
183 | // This method obtains the return address of the call, given as an offset from | |
184 | // the start of the code. | |
185 | unsigned returnAddressOffset(Call call) | |
186 | { | |
6fe7ccc8 | 187 | call.m_label = applyOffset(call.m_label); |
ba379fdc A |
188 | return MacroAssembler::getLinkerCallReturnOffset(call); |
189 | } | |
190 | ||
6fe7ccc8 | 191 | uint32_t offsetOf(Label label) |
ba379fdc | 192 | { |
6fe7ccc8 | 193 | return applyOffset(label.m_label).m_offset; |
ba379fdc | 194 | } |
b80e6193 | 195 | |
6fe7ccc8 A |
196 | // Upon completion of all patching 'finalizeCode()' should be called once to complete generation of the code. |
197 | CodeRef finalizeCode() | |
ba379fdc A |
198 | { |
199 | performFinalization(); | |
200 | ||
6fe7ccc8 | 201 | return CodeRef(m_executableMemory); |
ba379fdc A |
202 | } |
203 | ||
b80e6193 A |
204 | CodePtr trampolineAt(Label label) |
205 | { | |
206 | return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); | |
207 | } | |
208 | ||
14957cd0 A |
209 | void* debugAddress() |
210 | { | |
211 | return m_code; | |
212 | } | |
6fe7ccc8 A |
213 | |
214 | size_t debugSize() | |
215 | { | |
216 | return m_size; | |
217 | } | |
14957cd0 | 218 | |
ba379fdc | 219 | private: |
b80e6193 A |
220 | template <typename T> T applyOffset(T src) |
221 | { | |
222 | #if ENABLE(BRANCH_COMPACTION) | |
223 | src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); | |
224 | #endif | |
225 | return src; | |
226 | } | |
227 | ||
6fe7ccc8 | 228 | // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). |
ba379fdc A |
229 | void* code() |
230 | { | |
231 | return m_code; | |
232 | } | |
233 | ||
6fe7ccc8 | 234 | void linkCode(void* ownerUID, JITCompilationEffort effort) |
b80e6193 | 235 | { |
b80e6193 A |
236 | ASSERT(!m_code); |
237 | #if !ENABLE(BRANCH_COMPACTION) | |
6fe7ccc8 A |
238 | m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); |
239 | if (!m_executableMemory) | |
240 | return; | |
241 | m_code = m_executableMemory->start(); | |
14957cd0 A |
242 | m_size = m_assembler->m_assembler.codeSize(); |
243 | ASSERT(m_code); | |
b80e6193 | 244 | #else |
6fe7ccc8 A |
245 | m_initialSize = m_assembler->m_assembler.codeSize(); |
246 | m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); | |
247 | if (!m_executableMemory) | |
b80e6193 | 248 | return; |
6fe7ccc8 A |
249 | m_code = (uint8_t*)m_executableMemory->start(); |
250 | ASSERT(m_code); | |
251 | ExecutableAllocator::makeWritable(m_code, m_initialSize); | |
b80e6193 A |
252 | uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); |
253 | uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); | |
b80e6193 A |
254 | int readPtr = 0; |
255 | int writePtr = 0; | |
256 | Vector<LinkRecord>& jumpsToLink = m_assembler->jumpsToLink(); | |
257 | unsigned jumpCount = jumpsToLink.size(); | |
258 | for (unsigned i = 0; i < jumpCount; ++i) { | |
259 | int offset = readPtr - writePtr; | |
260 | ASSERT(!(offset & 1)); | |
261 | ||
262 | // Copy the instructions from the last jump to the current one. | |
263 | size_t regionSize = jumpsToLink[i].from() - readPtr; | |
6fe7ccc8 A |
264 | uint16_t* copySource = reinterpret_cast_ptr<uint16_t*>(inData + readPtr); |
265 | uint16_t* copyEnd = reinterpret_cast_ptr<uint16_t*>(inData + readPtr + regionSize); | |
266 | uint16_t* copyDst = reinterpret_cast_ptr<uint16_t*>(outData + writePtr); | |
14957cd0 A |
267 | ASSERT(!(regionSize % 2)); |
268 | ASSERT(!(readPtr % 2)); | |
269 | ASSERT(!(writePtr % 2)); | |
270 | while (copySource != copyEnd) | |
271 | *copyDst++ = *copySource++; | |
b80e6193 A |
272 | m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); |
273 | readPtr += regionSize; | |
274 | writePtr += regionSize; | |
275 | ||
276 | // Calculate absolute address of the jump target, in the case of backwards | |
277 | // branches we need to be precise, forward branches we are pessimistic | |
278 | const uint8_t* target; | |
279 | if (jumpsToLink[i].to() >= jumpsToLink[i].from()) | |
14957cd0 | 280 | target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far |
b80e6193 | 281 | else |
14957cd0 | 282 | target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); |
b80e6193 | 283 | |
14957cd0 | 284 | JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); |
b80e6193 A |
285 | // Compact branch if we can... |
286 | if (m_assembler->canCompact(jumpsToLink[i].type())) { | |
287 | // Step back in the write stream | |
288 | int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); | |
289 | if (delta) { | |
290 | writePtr -= delta; | |
291 | m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); | |
292 | } | |
293 | } | |
294 | jumpsToLink[i].setFrom(writePtr); | |
295 | } | |
296 | // Copy everything after the last jump | |
6fe7ccc8 A |
297 | memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); |
298 | m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); | |
b80e6193 | 299 | |
14957cd0 A |
300 | for (unsigned i = 0; i < jumpCount; ++i) { |
301 | uint8_t* location = outData + jumpsToLink[i].from(); | |
302 | uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); | |
303 | m_assembler->link(jumpsToLink[i], location, target); | |
b80e6193 A |
304 | } |
305 | ||
306 | jumpsToLink.clear(); | |
6fe7ccc8 A |
307 | m_size = writePtr + m_initialSize - readPtr; |
308 | m_executableMemory->shrink(m_size); | |
14957cd0 A |
309 | |
310 | #if DUMP_LINK_STATISTICS | |
6fe7ccc8 | 311 | dumpLinkStatistics(m_code, m_initialSize, m_size); |
14957cd0 A |
312 | #endif |
313 | #if DUMP_CODE | |
314 | dumpCode(m_code, m_size); | |
315 | #endif | |
b80e6193 A |
316 | #endif |
317 | } | |
318 | ||
ba379fdc A |
319 | void performFinalization() |
320 | { | |
321 | #ifndef NDEBUG | |
322 | ASSERT(!m_completed); | |
6fe7ccc8 | 323 | ASSERT(isValid()); |
ba379fdc A |
324 | m_completed = true; |
325 | #endif | |
326 | ||
6fe7ccc8 A |
327 | #if ENABLE(BRANCH_COMPACTION) |
328 | ExecutableAllocator::makeExecutable(code(), m_initialSize); | |
329 | #else | |
ba379fdc | 330 | ExecutableAllocator::makeExecutable(code(), m_size); |
6fe7ccc8 A |
331 | #endif |
332 | MacroAssembler::cacheFlush(code(), m_size); | |
ba379fdc A |
333 | } |
334 | ||
14957cd0 A |
335 | #if DUMP_LINK_STATISTICS |
336 | static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize) | |
337 | { | |
338 | static unsigned linkCount = 0; | |
339 | static unsigned totalInitialSize = 0; | |
340 | static unsigned totalFinalSize = 0; | |
341 | linkCount++; | |
342 | totalInitialSize += initialSize; | |
343 | totalFinalSize += finalSize; | |
6fe7ccc8 A |
344 | dataLog("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", |
345 | code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize), | |
346 | static_cast<unsigned>(initialSize - finalSize), | |
347 | 100.0 * (initialSize - finalSize) / initialSize); | |
348 | dataLog("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", | |
349 | linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, | |
350 | 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); | |
14957cd0 A |
351 | } |
352 | #endif | |
353 | ||
354 | #if DUMP_CODE | |
355 | static void dumpCode(void* code, size_t size) | |
356 | { | |
357 | #if CPU(ARM_THUMB2) | |
358 | // Dump the generated code in an asm file format that can be assembled and then disassembled | |
359 | // for debugging purposes. For example, save this output as jit.s: | |
360 | // gcc -arch armv7 -c jit.s | |
361 | // otool -tv jit.o | |
362 | static unsigned codeCount = 0; | |
363 | unsigned short* tcode = static_cast<unsigned short*>(code); | |
364 | size_t tsize = size / sizeof(short); | |
365 | char nameBuf[128]; | |
366 | snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); | |
6fe7ccc8 A |
367 | dataLog("\t.syntax unified\n" |
368 | "\t.section\t__TEXT,__text,regular,pure_instructions\n" | |
369 | "\t.globl\t%s\n" | |
370 | "\t.align 2\n" | |
371 | "\t.code 16\n" | |
372 | "\t.thumb_func\t%s\n" | |
373 | "# %p\n" | |
374 | "%s:\n", nameBuf, nameBuf, code, nameBuf); | |
14957cd0 A |
375 | |
376 | for (unsigned i = 0; i < tsize; i++) | |
6fe7ccc8 A |
377 | dataLog("\t.short\t0x%x\n", tcode[i]); |
378 | #elif CPU(ARM_TRADITIONAL) | |
379 | // gcc -c jit.s | |
380 | // objdump -D jit.o | |
381 | static unsigned codeCount = 0; | |
382 | unsigned int* tcode = static_cast<unsigned int*>(code); | |
383 | size_t tsize = size / sizeof(unsigned int); | |
384 | char nameBuf[128]; | |
385 | snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); | |
386 | dataLog("\t.globl\t%s\n" | |
387 | "\t.align 4\n" | |
388 | "\t.code 32\n" | |
389 | "\t.text\n" | |
390 | "# %p\n" | |
391 | "%s:\n", nameBuf, code, nameBuf); | |
392 | ||
393 | for (unsigned i = 0; i < tsize; i++) | |
394 | dataLog("\t.long\t0x%x\n", tcode[i]); | |
14957cd0 A |
395 | #endif |
396 | } | |
397 | #endif | |
398 | ||
6fe7ccc8 | 399 | RefPtr<ExecutableMemoryHandle> m_executableMemory; |
ba379fdc | 400 | size_t m_size; |
6fe7ccc8 A |
401 | #if ENABLE(BRANCH_COMPACTION) |
402 | size_t m_initialSize; | |
403 | #endif | |
b80e6193 A |
404 | void* m_code; |
405 | MacroAssembler* m_assembler; | |
14957cd0 | 406 | JSGlobalData* m_globalData; |
ba379fdc A |
407 | #ifndef NDEBUG |
408 | bool m_completed; | |
6fe7ccc8 | 409 | JITCompilationEffort m_effort; |
ba379fdc A |
410 | #endif |
411 | }; | |
412 | ||
413 | } // namespace JSC | |
414 | ||
415 | #endif // ENABLE(ASSEMBLER) | |
416 | ||
417 | #endif // LinkBuffer_h |