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