1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
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#include "config.h"
27#include "LinkBuffer.h"
28
29#if ENABLE(ASSEMBLER)
30
31#include "Options.h"
32
33namespace JSC {
34
35LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly()
36{
37    performFinalization();
38
39    return CodeRef(m_executableMemory);
40}
41
42LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...)
43{
44    ASSERT(Options::showDisassembly() || Options::showDFGDisassembly());
45
46    CodeRef result = finalizeCodeWithoutDisassembly();
47
48    dataLogF("Generated JIT code for ");
49    va_list argList;
50    va_start(argList, format);
51    WTF::dataLogFV(format, argList);
52    va_end(argList);
53    dataLogF(":\n");
54
55    dataLogF("    Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size());
56    disassemble(result.code(), m_size, "    ", WTF::dataFile());
57
58    return result;
59}
60
61void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort)
62{
63    ASSERT(!m_code);
64#if !ENABLE(BRANCH_COMPACTION)
65    m_executableMemory = m_assembler->m_assembler.executableCopy(*m_vm, ownerUID, effort);
66    if (!m_executableMemory)
67        return;
68    m_code = m_executableMemory->start();
69    m_size = m_assembler->m_assembler.codeSize();
70    ASSERT(m_code);
71#else
72    m_initialSize = m_assembler->m_assembler.codeSize();
73    m_executableMemory = m_vm->executableAllocator.allocate(*m_vm, m_initialSize, ownerUID, effort);
74    if (!m_executableMemory)
75        return;
76    m_code = (uint8_t*)m_executableMemory->start();
77    ASSERT(m_code);
78    ExecutableAllocator::makeWritable(m_code, m_initialSize);
79    uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode();
80    uint8_t* outData = reinterpret_cast<uint8_t*>(m_code);
81    int readPtr = 0;
82    int writePtr = 0;
83    Vector<LinkRecord, 0, UnsafeVectorOverflow>& jumpsToLink = m_assembler->jumpsToLink();
84    unsigned jumpCount = jumpsToLink.size();
85    for (unsigned i = 0; i < jumpCount; ++i) {
86        int offset = readPtr - writePtr;
87        ASSERT(!(offset & 1));
88
89        // Copy the instructions from the last jump to the current one.
90        size_t regionSize = jumpsToLink[i].from() - readPtr;
91        uint16_t* copySource = reinterpret_cast_ptr<uint16_t*>(inData + readPtr);
92        uint16_t* copyEnd = reinterpret_cast_ptr<uint16_t*>(inData + readPtr + regionSize);
93        uint16_t* copyDst = reinterpret_cast_ptr<uint16_t*>(outData + writePtr);
94        ASSERT(!(regionSize % 2));
95        ASSERT(!(readPtr % 2));
96        ASSERT(!(writePtr % 2));
97        while (copySource != copyEnd)
98            *copyDst++ = *copySource++;
99        m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset);
100        readPtr += regionSize;
101        writePtr += regionSize;
102
103        // Calculate absolute address of the jump target, in the case of backwards
104        // branches we need to be precise, forward branches we are pessimistic
105        const uint8_t* target;
106        if (jumpsToLink[i].to() >= jumpsToLink[i].from())
107            target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far
108        else
109            target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to());
110
111        JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target);
112        // Compact branch if we can...
113        if (m_assembler->canCompact(jumpsToLink[i].type())) {
114            // Step back in the write stream
115            int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType);
116            if (delta) {
117                writePtr -= delta;
118                m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr);
119            }
120        }
121        jumpsToLink[i].setFrom(writePtr);
122    }
123    // Copy everything after the last jump
124    memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr);
125    m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr);
126
127    for (unsigned i = 0; i < jumpCount; ++i) {
128        uint8_t* location = outData + jumpsToLink[i].from();
129        uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to());
130        m_assembler->link(jumpsToLink[i], location, target);
131    }
132
133    jumpsToLink.clear();
134    m_size = writePtr + m_initialSize - readPtr;
135    m_executableMemory->shrink(m_size);
136
137#if DUMP_LINK_STATISTICS
138    dumpLinkStatistics(m_code, m_initialSize, m_size);
139#endif
140#if DUMP_CODE
141    dumpCode(m_code, m_size);
142#endif
143#endif
144}
145
146void LinkBuffer::performFinalization()
147{
148#ifndef NDEBUG
149    ASSERT(!m_completed);
150    ASSERT(isValid());
151    m_completed = true;
152#endif
153
154#if ENABLE(BRANCH_COMPACTION)
155    ExecutableAllocator::makeExecutable(code(), m_initialSize);
156#else
157    ExecutableAllocator::makeExecutable(code(), m_size);
158#endif
159    MacroAssembler::cacheFlush(code(), m_size);
160}
161
162#if DUMP_LINK_STATISTICS
163void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize)
164{
165    static unsigned linkCount = 0;
166    static unsigned totalInitialSize = 0;
167    static unsigned totalFinalSize = 0;
168    linkCount++;
169    totalInitialSize += initialSize;
170    totalFinalSize += finalSize;
171    dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n",
172            code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize),
173            static_cast<unsigned>(initialSize - finalSize),
174            100.0 * (initialSize - finalSize) / initialSize);
175    dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n",
176            linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize,
177            100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize);
178}
179#endif
180
181#if DUMP_CODE
182void LinkBuffer::dumpCode(void* code, size_t size)
183{
184#if CPU(ARM_THUMB2)
185    // Dump the generated code in an asm file format that can be assembled and then disassembled
186    // for debugging purposes. For example, save this output as jit.s:
187    //   gcc -arch armv7 -c jit.s
188    //   otool -tv jit.o
189    static unsigned codeCount = 0;
190    unsigned short* tcode = static_cast<unsigned short*>(code);
191    size_t tsize = size / sizeof(short);
192    char nameBuf[128];
193    snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++);
194    dataLogF("\t.syntax unified\n"
195            "\t.section\t__TEXT,__text,regular,pure_instructions\n"
196            "\t.globl\t%s\n"
197            "\t.align 2\n"
198            "\t.code 16\n"
199            "\t.thumb_func\t%s\n"
200            "# %p\n"
201            "%s:\n", nameBuf, nameBuf, code, nameBuf);
202
203    for (unsigned i = 0; i < tsize; i++)
204        dataLogF("\t.short\t0x%x\n", tcode[i]);
205#elif CPU(ARM_TRADITIONAL)
206    //   gcc -c jit.s
207    //   objdump -D jit.o
208    static unsigned codeCount = 0;
209    unsigned int* tcode = static_cast<unsigned int*>(code);
210    size_t tsize = size / sizeof(unsigned int);
211    char nameBuf[128];
212    snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++);
213    dataLogF("\t.globl\t%s\n"
214            "\t.align 4\n"
215            "\t.code 32\n"
216            "\t.text\n"
217            "# %p\n"
218            "%s:\n", nameBuf, code, nameBuf);
219
220    for (unsigned i = 0; i < tsize; i++)
221        dataLogF("\t.long\t0x%x\n", tcode[i]);
222#endif
223}
224#endif
225
226} // namespace JSC
227
228#endif // ENABLE(ASSEMBLER)
229
230
231