2 * Copyright (C) 2012-2015 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.
27 #include "LinkBuffer.h"
31 #include "CodeBlock.h"
33 #include "JSCInlines.h"
36 #include <wtf/CompilationThread.h>
40 bool shouldShowDisassemblyFor(CodeBlock
* codeBlock
)
42 if (JITCode::isOptimizingJIT(codeBlock
->jitType()) && Options::showDFGDisassembly())
44 return Options::showDisassembly();
47 LinkBuffer::CodeRef
LinkBuffer::finalizeCodeWithoutDisassembly()
49 performFinalization();
51 ASSERT(m_didAllocate
);
52 if (m_executableMemory
)
53 return CodeRef(m_executableMemory
);
55 return CodeRef::createSelfManagedCodeRef(MacroAssemblerCodePtr(m_code
));
58 LinkBuffer::CodeRef
LinkBuffer::finalizeCodeWithDisassembly(const char* format
, ...)
60 CodeRef result
= finalizeCodeWithoutDisassembly();
62 if (m_alreadyDisassembled
)
65 StringPrintStream out
;
66 out
.printf("Generated JIT code for ");
68 va_start(argList
, format
);
69 out
.vprintf(format
, argList
);
73 out
.printf(" Code at [%p, %p):\n", result
.code().executableAddress(), static_cast<char*>(result
.code().executableAddress()) + result
.size());
75 CString header
= out
.toCString();
77 if (Options::asyncDisassembly()) {
78 disassembleAsynchronously(header
, result
, m_size
, " ");
83 disassemble(result
.code(), m_size
, " ", WTF::dataFile());
88 #if ENABLE(BRANCH_COMPACTION)
89 static ALWAYS_INLINE
void recordLinkOffsets(AssemblerData
& assemblerData
, int32_t regionStart
, int32_t regionEnd
, int32_t offset
)
91 int32_t ptr
= regionStart
/ sizeof(int32_t);
92 const int32_t end
= regionEnd
/ sizeof(int32_t);
93 int32_t* offsets
= reinterpret_cast<int32_t*>(assemblerData
.buffer());
95 offsets
[ptr
++] = offset
;
98 template <typename InstructionType
>
99 void LinkBuffer::copyCompactAndLinkCode(MacroAssembler
& macroAssembler
, void* ownerUID
, JITCompilationEffort effort
)
101 m_initialSize
= macroAssembler
.m_assembler
.codeSize();
102 allocate(m_initialSize
, ownerUID
, effort
);
103 if (didFailToAllocate())
105 Vector
<LinkRecord
, 0, UnsafeVectorOverflow
>& jumpsToLink
= macroAssembler
.jumpsToLink();
106 m_assemblerStorage
= macroAssembler
.m_assembler
.buffer().releaseAssemblerData();
107 uint8_t* inData
= reinterpret_cast<uint8_t*>(m_assemblerStorage
.buffer());
108 uint8_t* outData
= reinterpret_cast<uint8_t*>(m_code
);
111 unsigned jumpCount
= jumpsToLink
.size();
112 for (unsigned i
= 0; i
< jumpCount
; ++i
) {
113 int offset
= readPtr
- writePtr
;
114 ASSERT(!(offset
& 1));
116 // Copy the instructions from the last jump to the current one.
117 size_t regionSize
= jumpsToLink
[i
].from() - readPtr
;
118 InstructionType
* copySource
= reinterpret_cast_ptr
<InstructionType
*>(inData
+ readPtr
);
119 InstructionType
* copyEnd
= reinterpret_cast_ptr
<InstructionType
*>(inData
+ readPtr
+ regionSize
);
120 InstructionType
* copyDst
= reinterpret_cast_ptr
<InstructionType
*>(outData
+ writePtr
);
121 ASSERT(!(regionSize
% 2));
122 ASSERT(!(readPtr
% 2));
123 ASSERT(!(writePtr
% 2));
124 while (copySource
!= copyEnd
)
125 *copyDst
++ = *copySource
++;
126 recordLinkOffsets(m_assemblerStorage
, readPtr
, jumpsToLink
[i
].from(), offset
);
127 readPtr
+= regionSize
;
128 writePtr
+= regionSize
;
130 // Calculate absolute address of the jump target, in the case of backwards
131 // branches we need to be precise, forward branches we are pessimistic
132 const uint8_t* target
;
133 if (jumpsToLink
[i
].to() >= jumpsToLink
[i
].from())
134 target
= outData
+ jumpsToLink
[i
].to() - offset
; // Compensate for what we have collapsed so far
136 target
= outData
+ jumpsToLink
[i
].to() - executableOffsetFor(jumpsToLink
[i
].to());
138 JumpLinkType jumpLinkType
= MacroAssembler::computeJumpType(jumpsToLink
[i
], outData
+ writePtr
, target
);
139 // Compact branch if we can...
140 if (MacroAssembler::canCompact(jumpsToLink
[i
].type())) {
141 // Step back in the write stream
142 int32_t delta
= MacroAssembler::jumpSizeDelta(jumpsToLink
[i
].type(), jumpLinkType
);
145 recordLinkOffsets(m_assemblerStorage
, jumpsToLink
[i
].from() - delta
, readPtr
, readPtr
- writePtr
);
148 jumpsToLink
[i
].setFrom(writePtr
);
150 // Copy everything after the last jump
151 memcpy(outData
+ writePtr
, inData
+ readPtr
, m_initialSize
- readPtr
);
152 recordLinkOffsets(m_assemblerStorage
, readPtr
, m_initialSize
, readPtr
- writePtr
);
154 for (unsigned i
= 0; i
< jumpCount
; ++i
) {
155 uint8_t* location
= outData
+ jumpsToLink
[i
].from();
156 uint8_t* target
= outData
+ jumpsToLink
[i
].to() - executableOffsetFor(jumpsToLink
[i
].to());
157 MacroAssembler::link(jumpsToLink
[i
], location
, target
);
161 shrink(writePtr
+ m_initialSize
- readPtr
);
163 #if DUMP_LINK_STATISTICS
164 dumpLinkStatistics(m_code
, m_initialSize
, m_size
);
167 dumpCode(m_code
, m_size
);
173 void LinkBuffer::linkCode(MacroAssembler
& macroAssembler
, void* ownerUID
, JITCompilationEffort effort
)
175 #if !ENABLE(BRANCH_COMPACTION)
176 #if defined(ASSEMBLER_HAS_CONSTANT_POOL) && ASSEMBLER_HAS_CONSTANT_POOL
177 macroAssembler
.m_assembler
.buffer().flushConstantPool(false);
179 AssemblerBuffer
& buffer
= macroAssembler
.m_assembler
.buffer();
180 allocate(buffer
.codeSize(), ownerUID
, effort
);
184 #if CPU(ARM_TRADITIONAL)
185 macroAssembler
.m_assembler
.prepareExecutableCopy(m_code
);
187 memcpy(m_code
, buffer
.data(), buffer
.codeSize());
189 macroAssembler
.m_assembler
.relocateJumps(buffer
.data(), m_code
);
191 #elif CPU(ARM_THUMB2)
192 copyCompactAndLinkCode
<uint16_t>(macroAssembler
, ownerUID
, effort
);
194 copyCompactAndLinkCode
<uint32_t>(macroAssembler
, ownerUID
, effort
);
198 void LinkBuffer::allocate(size_t initialSize
, void* ownerUID
, JITCompilationEffort effort
)
201 if (initialSize
> m_size
)
204 m_didAllocate
= true;
205 m_size
= initialSize
;
209 m_executableMemory
= m_vm
->executableAllocator
.allocate(*m_vm
, initialSize
, ownerUID
, effort
);
210 if (!m_executableMemory
)
212 ExecutableAllocator::makeWritable(m_executableMemory
->start(), m_executableMemory
->sizeInBytes());
213 m_code
= m_executableMemory
->start();
214 m_size
= initialSize
;
215 m_didAllocate
= true;
218 void LinkBuffer::shrink(size_t newSize
)
220 if (!m_executableMemory
)
223 m_executableMemory
->shrink(m_size
);
226 void LinkBuffer::performFinalization()
229 ASSERT(!isCompilationThread());
230 ASSERT(!m_completed
);
235 #if ENABLE(BRANCH_COMPACTION)
236 ExecutableAllocator::makeExecutable(code(), m_initialSize
);
238 ExecutableAllocator::makeExecutable(code(), m_size
);
240 MacroAssembler::cacheFlush(code(), m_size
);
243 #if DUMP_LINK_STATISTICS
244 void LinkBuffer::dumpLinkStatistics(void* code
, size_t initializeSize
, size_t finalSize
)
246 static unsigned linkCount
= 0;
247 static unsigned totalInitialSize
= 0;
248 static unsigned totalFinalSize
= 0;
250 totalInitialSize
+= initialSize
;
251 totalFinalSize
+= finalSize
;
252 dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n",
253 code
, static_cast<unsigned>(initialSize
), static_cast<unsigned>(finalSize
),
254 static_cast<unsigned>(initialSize
- finalSize
),
255 100.0 * (initialSize
- finalSize
) / initialSize
);
256 dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n",
257 linkCount
, totalInitialSize
, totalFinalSize
, totalInitialSize
- totalFinalSize
,
258 100.0 * (totalInitialSize
- totalFinalSize
) / totalInitialSize
);
263 void LinkBuffer::dumpCode(void* code
, size_t size
)
266 // Dump the generated code in an asm file format that can be assembled and then disassembled
267 // for debugging purposes. For example, save this output as jit.s:
268 // gcc -arch armv7 -c jit.s
270 static unsigned codeCount
= 0;
271 unsigned short* tcode
= static_cast<unsigned short*>(code
);
272 size_t tsize
= size
/ sizeof(short);
274 snprintf(nameBuf
, sizeof(nameBuf
), "_jsc_jit%u", codeCount
++);
275 dataLogF("\t.syntax unified\n"
276 "\t.section\t__TEXT,__text,regular,pure_instructions\n"
280 "\t.thumb_func\t%s\n"
282 "%s:\n", nameBuf
, nameBuf
, code
, nameBuf
);
284 for (unsigned i
= 0; i
< tsize
; i
++)
285 dataLogF("\t.short\t0x%x\n", tcode
[i
]);
286 #elif CPU(ARM_TRADITIONAL)
289 static unsigned codeCount
= 0;
290 unsigned int* tcode
= static_cast<unsigned int*>(code
);
291 size_t tsize
= size
/ sizeof(unsigned int);
293 snprintf(nameBuf
, sizeof(nameBuf
), "_jsc_jit%u", codeCount
++);
294 dataLogF("\t.globl\t%s\n"
299 "%s:\n", nameBuf
, code
, nameBuf
);
301 for (unsigned i
= 0; i
< tsize
; i
++)
302 dataLogF("\t.long\t0x%x\n", tcode
[i
]);
309 #endif // ENABLE(ASSEMBLER)