2 * Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
31 #define DUMP_LINK_STATISTICS 0
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))
37 #include "JITCompilationEffort.h"
38 #include "MacroAssembler.h"
39 #include <wtf/DataLog.h>
40 #include <wtf/Noncopyable.h>
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.
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.
61 WTF_MAKE_NONCOPYABLE(LinkBuffer
);
62 typedef MacroAssemblerCodeRef CodeRef
;
63 typedef MacroAssemblerCodePtr CodePtr
;
64 typedef MacroAssembler::Label Label
;
65 typedef MacroAssembler::Jump Jump
;
66 typedef MacroAssembler::PatchableJump PatchableJump
;
67 typedef MacroAssembler::JumpList JumpList
;
68 typedef MacroAssembler::Call Call
;
69 typedef MacroAssembler::DataLabelCompact DataLabelCompact
;
70 typedef MacroAssembler::DataLabel32 DataLabel32
;
71 typedef MacroAssembler::DataLabelPtr DataLabelPtr
;
72 #if ENABLE(BRANCH_COMPACTION)
73 typedef MacroAssembler::LinkRecord LinkRecord
;
74 typedef MacroAssembler::JumpLinkType JumpLinkType
;
78 LinkBuffer(JSGlobalData
& globalData
, MacroAssembler
* masm
, void* ownerUID
, JITCompilationEffort effort
= JITCompilationMustSucceed
)
80 #if ENABLE(BRANCH_COMPACTION)
85 , m_globalData(&globalData
)
91 linkCode(ownerUID
, effort
);
96 ASSERT(m_completed
|| (!m_executableMemory
&& m_effort
== JITCompilationCanFail
));
99 bool didFailToAllocate() const
101 return !m_executableMemory
;
106 return !didFailToAllocate();
109 // These methods are used to link or set values at code generation time.
111 void link(Call call
, FunctionPtr function
)
113 ASSERT(call
.isFlagSet(Call::Linkable
));
114 call
.m_label
= applyOffset(call
.m_label
);
115 MacroAssembler::linkCall(code(), call
, function
);
118 void link(Jump jump
, CodeLocationLabel label
)
120 jump
.m_label
= applyOffset(jump
.m_label
);
121 MacroAssembler::linkJump(code(), jump
, label
);
124 void link(JumpList list
, CodeLocationLabel label
)
126 for (unsigned i
= 0; i
< list
.m_jumps
.size(); ++i
)
127 link(list
.m_jumps
[i
], label
);
130 void patch(DataLabelPtr label
, void* value
)
132 AssemblerLabel target
= applyOffset(label
.m_label
);
133 MacroAssembler::linkPointer(code(), target
, value
);
136 void patch(DataLabelPtr label
, CodeLocationLabel value
)
138 AssemblerLabel target
= applyOffset(label
.m_label
);
139 MacroAssembler::linkPointer(code(), target
, value
.executableAddress());
142 // These methods are used to obtain handles to allow the code to be relinked / repatched later.
144 CodeLocationCall
locationOf(Call call
)
146 ASSERT(call
.isFlagSet(Call::Linkable
));
147 ASSERT(!call
.isFlagSet(Call::Near
));
148 return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call
.m_label
)));
151 CodeLocationNearCall
locationOfNearCall(Call call
)
153 ASSERT(call
.isFlagSet(Call::Linkable
));
154 ASSERT(call
.isFlagSet(Call::Near
));
155 return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call
.m_label
)));
158 CodeLocationLabel
locationOf(PatchableJump jump
)
160 return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump
.m_jump
.m_label
)));
163 CodeLocationLabel
locationOf(Label label
)
165 return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label
.m_label
)));
168 CodeLocationDataLabelPtr
locationOf(DataLabelPtr label
)
170 return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label
.m_label
)));
173 CodeLocationDataLabel32
locationOf(DataLabel32 label
)
175 return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label
.m_label
)));
178 CodeLocationDataLabelCompact
locationOf(DataLabelCompact label
)
180 return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label
.m_label
)));
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
)
187 call
.m_label
= applyOffset(call
.m_label
);
188 return MacroAssembler::getLinkerCallReturnOffset(call
);
191 uint32_t offsetOf(Label label
)
193 return applyOffset(label
.m_label
).m_offset
;
196 // Upon completion of all patching 'finalizeCode()' should be called once to complete generation of the code.
197 CodeRef
finalizeCode()
199 performFinalization();
201 return CodeRef(m_executableMemory
);
204 CodePtr
trampolineAt(Label label
)
206 return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label
.m_label
)));
220 template <typename T
> T
applyOffset(T src
)
222 #if ENABLE(BRANCH_COMPACTION)
223 src
.m_offset
-= m_assembler
->executableOffsetFor(src
.m_offset
);
228 // Keep this private! - the underlying code should only be obtained externally via finalizeCode().
234 void linkCode(void* ownerUID
, JITCompilationEffort effort
)
237 #if !ENABLE(BRANCH_COMPACTION)
238 m_executableMemory
= m_assembler
->m_assembler
.executableCopy(*m_globalData
, ownerUID
, effort
);
239 if (!m_executableMemory
)
241 m_code
= m_executableMemory
->start();
242 m_size
= m_assembler
->m_assembler
.codeSize();
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
)
249 m_code
= (uint8_t*)m_executableMemory
->start();
251 ExecutableAllocator::makeWritable(m_code
, m_initialSize
);
252 uint8_t* inData
= (uint8_t*)m_assembler
->unlinkedCode();
253 uint8_t* outData
= reinterpret_cast<uint8_t*>(m_code
);
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));
262 // Copy the instructions from the last jump to the current one.
263 size_t regionSize
= jumpsToLink
[i
].from() - readPtr
;
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
);
267 ASSERT(!(regionSize
% 2));
268 ASSERT(!(readPtr
% 2));
269 ASSERT(!(writePtr
% 2));
270 while (copySource
!= copyEnd
)
271 *copyDst
++ = *copySource
++;
272 m_assembler
->recordLinkOffsets(readPtr
, jumpsToLink
[i
].from(), offset
);
273 readPtr
+= regionSize
;
274 writePtr
+= regionSize
;
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())
280 target
= outData
+ jumpsToLink
[i
].to() - offset
; // Compensate for what we have collapsed so far
282 target
= outData
+ jumpsToLink
[i
].to() - m_assembler
->executableOffsetFor(jumpsToLink
[i
].to());
284 JumpLinkType jumpLinkType
= m_assembler
->computeJumpType(jumpsToLink
[i
], outData
+ writePtr
, target
);
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
);
291 m_assembler
->recordLinkOffsets(jumpsToLink
[i
].from() - delta
, readPtr
, readPtr
- writePtr
);
294 jumpsToLink
[i
].setFrom(writePtr
);
296 // Copy everything after the last jump
297 memcpy(outData
+ writePtr
, inData
+ readPtr
, m_initialSize
- readPtr
);
298 m_assembler
->recordLinkOffsets(readPtr
, m_initialSize
, readPtr
- writePtr
);
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
);
307 m_size
= writePtr
+ m_initialSize
- readPtr
;
308 m_executableMemory
->shrink(m_size
);
310 #if DUMP_LINK_STATISTICS
311 dumpLinkStatistics(m_code
, m_initialSize
, m_size
);
314 dumpCode(m_code
, m_size
);
319 void performFinalization()
322 ASSERT(!m_completed
);
327 #if ENABLE(BRANCH_COMPACTION)
328 ExecutableAllocator::makeExecutable(code(), m_initialSize
);
330 ExecutableAllocator::makeExecutable(code(), m_size
);
332 MacroAssembler::cacheFlush(code(), m_size
);
335 #if DUMP_LINK_STATISTICS
336 static void dumpLinkStatistics(void* code
, size_t initialSize
, size_t finalSize
)
338 static unsigned linkCount
= 0;
339 static unsigned totalInitialSize
= 0;
340 static unsigned totalFinalSize
= 0;
342 totalInitialSize
+= initialSize
;
343 totalFinalSize
+= finalSize
;
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
);
355 static void dumpCode(void* code
, size_t size
)
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
362 static unsigned codeCount
= 0;
363 unsigned short* tcode
= static_cast<unsigned short*>(code
);
364 size_t tsize
= size
/ sizeof(short);
366 snprintf(nameBuf
, sizeof(nameBuf
), "_jsc_jit%u", codeCount
++);
367 dataLog("\t.syntax unified\n"
368 "\t.section\t__TEXT,__text,regular,pure_instructions\n"
372 "\t.thumb_func\t%s\n"
374 "%s:\n", nameBuf
, nameBuf
, code
, nameBuf
);
376 for (unsigned i
= 0; i
< tsize
; i
++)
377 dataLog("\t.short\t0x%x\n", tcode
[i
]);
378 #elif CPU(ARM_TRADITIONAL)
381 static unsigned codeCount
= 0;
382 unsigned int* tcode
= static_cast<unsigned int*>(code
);
383 size_t tsize
= size
/ sizeof(unsigned int);
385 snprintf(nameBuf
, sizeof(nameBuf
), "_jsc_jit%u", codeCount
++);
386 dataLog("\t.globl\t%s\n"
391 "%s:\n", nameBuf
, code
, nameBuf
);
393 for (unsigned i
= 0; i
< tsize
; i
++)
394 dataLog("\t.long\t0x%x\n", tcode
[i
]);
399 RefPtr
<ExecutableMemoryHandle
> m_executableMemory
;
401 #if ENABLE(BRANCH_COMPACTION)
402 size_t m_initialSize
;
405 MacroAssembler
* m_assembler
;
406 JSGlobalData
* m_globalData
;
409 JITCompilationEffort m_effort
;
415 #endif // ENABLE(ASSEMBLER)
417 #endif // LinkBuffer_h