1/* 2 * Copyright (C) 2012, 2013, 2014 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 "CodeBlock.h" 32#include "JITCode.h" 33#include "JSCInlines.h" 34#include "Options.h" 35#include "VM.h" 36#include <wtf/CompilationThread.h> 37 38namespace JSC { 39 40bool shouldShowDisassemblyFor(CodeBlock* codeBlock) 41{ 42 if (JITCode::isOptimizingJIT(codeBlock->jitType()) && Options::showDFGDisassembly()) 43 return true; 44 return Options::showDisassembly(); 45} 46 47LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() 48{ 49 performFinalization(); 50 51 ASSERT(m_didAllocate); 52 if (m_executableMemory) 53 return CodeRef(m_executableMemory); 54 55 return CodeRef::createSelfManagedCodeRef(MacroAssemblerCodePtr(m_code)); 56} 57 58LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) 59{ 60 CodeRef result = finalizeCodeWithoutDisassembly(); 61 62#if ENABLE(DISASSEMBLER) 63 dataLogF("Generated JIT code for "); 64 va_list argList; 65 va_start(argList, format); 66 WTF::dataLogFV(format, argList); 67 va_end(argList); 68 dataLogF(":\n"); 69 70 dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size()); 71 disassemble(result.code(), m_size, " ", WTF::dataFile()); 72#else 73 UNUSED_PARAM(format); 74#endif // ENABLE(DISASSEMBLER) 75 76 return result; 77} 78 79#if ENABLE(BRANCH_COMPACTION) 80static ALWAYS_INLINE void recordLinkOffsets(AssemblerData& assemblerData, int32_t regionStart, int32_t regionEnd, int32_t offset) 81{ 82 int32_t ptr = regionStart / sizeof(int32_t); 83 const int32_t end = regionEnd / sizeof(int32_t); 84 int32_t* offsets = reinterpret_cast<int32_t*>(assemblerData.buffer()); 85 while (ptr < end) 86 offsets[ptr++] = offset; 87} 88 89template <typename InstructionType> 90void LinkBuffer::copyCompactAndLinkCode(MacroAssembler& macroAssembler, void* ownerUID, JITCompilationEffort effort) 91{ 92 m_initialSize = macroAssembler.m_assembler.codeSize(); 93 allocate(m_initialSize, ownerUID, effort); 94 if (didFailToAllocate()) 95 return; 96 Vector<LinkRecord, 0, UnsafeVectorOverflow>& jumpsToLink = macroAssembler.jumpsToLink(); 97 m_assemblerStorage = macroAssembler.m_assembler.buffer().releaseAssemblerData(); 98 uint8_t* inData = reinterpret_cast<uint8_t*>(m_assemblerStorage.buffer()); 99 uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); 100 int readPtr = 0; 101 int writePtr = 0; 102 unsigned jumpCount = jumpsToLink.size(); 103 for (unsigned i = 0; i < jumpCount; ++i) { 104 int offset = readPtr - writePtr; 105 ASSERT(!(offset & 1)); 106 107 // Copy the instructions from the last jump to the current one. 108 size_t regionSize = jumpsToLink[i].from() - readPtr; 109 InstructionType* copySource = reinterpret_cast_ptr<InstructionType*>(inData + readPtr); 110 InstructionType* copyEnd = reinterpret_cast_ptr<InstructionType*>(inData + readPtr + regionSize); 111 InstructionType* copyDst = reinterpret_cast_ptr<InstructionType*>(outData + writePtr); 112 ASSERT(!(regionSize % 2)); 113 ASSERT(!(readPtr % 2)); 114 ASSERT(!(writePtr % 2)); 115 while (copySource != copyEnd) 116 *copyDst++ = *copySource++; 117 recordLinkOffsets(m_assemblerStorage, readPtr, jumpsToLink[i].from(), offset); 118 readPtr += regionSize; 119 writePtr += regionSize; 120 121 // Calculate absolute address of the jump target, in the case of backwards 122 // branches we need to be precise, forward branches we are pessimistic 123 const uint8_t* target; 124 if (jumpsToLink[i].to() >= jumpsToLink[i].from()) 125 target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far 126 else 127 target = outData + jumpsToLink[i].to() - executableOffsetFor(jumpsToLink[i].to()); 128 129 JumpLinkType jumpLinkType = MacroAssembler::computeJumpType(jumpsToLink[i], outData + writePtr, target); 130 // Compact branch if we can... 131 if (MacroAssembler::canCompact(jumpsToLink[i].type())) { 132 // Step back in the write stream 133 int32_t delta = MacroAssembler::jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); 134 if (delta) { 135 writePtr -= delta; 136 recordLinkOffsets(m_assemblerStorage, jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); 137 } 138 } 139 jumpsToLink[i].setFrom(writePtr); 140 } 141 // Copy everything after the last jump 142 memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); 143 recordLinkOffsets(m_assemblerStorage, readPtr, m_initialSize, readPtr - writePtr); 144 145 for (unsigned i = 0; i < jumpCount; ++i) { 146 uint8_t* location = outData + jumpsToLink[i].from(); 147 uint8_t* target = outData + jumpsToLink[i].to() - executableOffsetFor(jumpsToLink[i].to()); 148 MacroAssembler::link(jumpsToLink[i], location, target); 149 } 150 151 jumpsToLink.clear(); 152 shrink(writePtr + m_initialSize - readPtr); 153 154#if DUMP_LINK_STATISTICS 155 dumpLinkStatistics(m_code, m_initialSize, m_size); 156#endif 157#if DUMP_CODE 158 dumpCode(m_code, m_size); 159#endif 160} 161#endif 162 163 164void LinkBuffer::linkCode(MacroAssembler& macroAssembler, void* ownerUID, JITCompilationEffort effort) 165{ 166#if !ENABLE(BRANCH_COMPACTION) 167#if defined(ASSEMBLER_HAS_CONSTANT_POOL) && ASSEMBLER_HAS_CONSTANT_POOL 168 macroAssembler.m_assembler.buffer().flushConstantPool(false); 169#endif 170 AssemblerBuffer& buffer = macroAssembler.m_assembler.buffer(); 171 allocate(buffer.codeSize(), ownerUID, effort); 172 if (!m_didAllocate) 173 return; 174 ASSERT(m_code); 175#if CPU(ARM_TRADITIONAL) 176 macroAssembler.m_assembler.prepareExecutableCopy(m_code); 177#endif 178 memcpy(m_code, buffer.data(), buffer.codeSize()); 179#if CPU(MIPS) 180 macroAssembler.m_assembler.relocateJumps(buffer.data(), m_code); 181#endif 182#elif CPU(ARM_THUMB2) 183 copyCompactAndLinkCode<uint16_t>(macroAssembler, ownerUID, effort); 184#elif CPU(ARM64) 185 copyCompactAndLinkCode<uint32_t>(macroAssembler, ownerUID, effort); 186#endif 187} 188 189void LinkBuffer::allocate(size_t initialSize, void* ownerUID, JITCompilationEffort effort) 190{ 191 if (m_code) { 192 if (initialSize > m_size) 193 return; 194 195 m_didAllocate = true; 196 m_size = initialSize; 197 return; 198 } 199 200 m_executableMemory = m_vm->executableAllocator.allocate(*m_vm, initialSize, ownerUID, effort); 201 if (!m_executableMemory) 202 return; 203 ExecutableAllocator::makeWritable(m_executableMemory->start(), m_executableMemory->sizeInBytes()); 204 m_code = m_executableMemory->start(); 205 m_size = initialSize; 206 m_didAllocate = true; 207} 208 209void LinkBuffer::shrink(size_t newSize) 210{ 211 if (!m_executableMemory) 212 return; 213 m_size = newSize; 214 m_executableMemory->shrink(m_size); 215} 216 217void LinkBuffer::performFinalization() 218{ 219#ifndef NDEBUG 220 ASSERT(!isCompilationThread()); 221 ASSERT(!m_completed); 222 ASSERT(isValid()); 223 m_completed = true; 224#endif 225 226#if ENABLE(BRANCH_COMPACTION) 227 ExecutableAllocator::makeExecutable(code(), m_initialSize); 228#else 229 ExecutableAllocator::makeExecutable(code(), m_size); 230#endif 231 MacroAssembler::cacheFlush(code(), m_size); 232} 233 234#if DUMP_LINK_STATISTICS 235void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) 236{ 237 static unsigned linkCount = 0; 238 static unsigned totalInitialSize = 0; 239 static unsigned totalFinalSize = 0; 240 linkCount++; 241 totalInitialSize += initialSize; 242 totalFinalSize += finalSize; 243 dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", 244 code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize), 245 static_cast<unsigned>(initialSize - finalSize), 246 100.0 * (initialSize - finalSize) / initialSize); 247 dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", 248 linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, 249 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); 250} 251#endif 252 253#if DUMP_CODE 254void LinkBuffer::dumpCode(void* code, size_t size) 255{ 256#if CPU(ARM_THUMB2) 257 // Dump the generated code in an asm file format that can be assembled and then disassembled 258 // for debugging purposes. For example, save this output as jit.s: 259 // gcc -arch armv7 -c jit.s 260 // otool -tv jit.o 261 static unsigned codeCount = 0; 262 unsigned short* tcode = static_cast<unsigned short*>(code); 263 size_t tsize = size / sizeof(short); 264 char nameBuf[128]; 265 snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); 266 dataLogF("\t.syntax unified\n" 267 "\t.section\t__TEXT,__text,regular,pure_instructions\n" 268 "\t.globl\t%s\n" 269 "\t.align 2\n" 270 "\t.code 16\n" 271 "\t.thumb_func\t%s\n" 272 "# %p\n" 273 "%s:\n", nameBuf, nameBuf, code, nameBuf); 274 275 for (unsigned i = 0; i < tsize; i++) 276 dataLogF("\t.short\t0x%x\n", tcode[i]); 277#elif CPU(ARM_TRADITIONAL) 278 // gcc -c jit.s 279 // objdump -D jit.o 280 static unsigned codeCount = 0; 281 unsigned int* tcode = static_cast<unsigned int*>(code); 282 size_t tsize = size / sizeof(unsigned int); 283 char nameBuf[128]; 284 snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); 285 dataLogF("\t.globl\t%s\n" 286 "\t.align 4\n" 287 "\t.code 32\n" 288 "\t.text\n" 289 "# %p\n" 290 "%s:\n", nameBuf, code, nameBuf); 291 292 for (unsigned i = 0; i < tsize; i++) 293 dataLogF("\t.long\t0x%x\n", tcode[i]); 294#endif 295} 296#endif 297 298} // namespace JSC 299 300#endif // ENABLE(ASSEMBLER) 301 302 303