1 /* 2 * Copyright (C) 2009, 2010 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 #ifndef LinkBuffer_h 27 #define LinkBuffer_h 28 29 #if ENABLE(ASSEMBLER) 30 31 #define DUMP_LINK_STATISTICS 0 32 #define DUMP_CODE 0 33 34 #include <MacroAssembler.h> 35 #include <wtf/Noncopyable.h> 36 37 namespace JSC { 38 39 // LinkBuffer: 40 // 41 // This class assists in linking code generated by the macro assembler, once code generation 42 // has been completed, and the code has been copied to is final location in memory. At this 43 // time pointers to labels within the code may be resolved, and relative offsets to external 44 // addresses may be fixed. 45 // 46 // Specifically: 47 // * Jump objects may be linked to external targets, 48 // * The address of Jump objects may taken, such that it can later be relinked. 49 // * The return address of a Call may be acquired. 50 // * The address of a Label pointing into the code may be resolved. 51 // * The value referenced by a DataLabel may be set. 52 // 53 class LinkBuffer { 54 WTF_MAKE_NONCOPYABLE(LinkBuffer); 55 typedef MacroAssemblerCodeRef CodeRef; 56 typedef MacroAssemblerCodePtr CodePtr; 57 typedef MacroAssembler::Label Label; 58 typedef MacroAssembler::Jump Jump; 59 typedef MacroAssembler::JumpList JumpList; 60 typedef MacroAssembler::Call Call; 61 typedef MacroAssembler::DataLabel32 DataLabel32; 62 typedef MacroAssembler::DataLabelPtr DataLabelPtr; 63 typedef MacroAssembler::JmpDst JmpDst; 64 #if ENABLE(BRANCH_COMPACTION) 65 typedef MacroAssembler::LinkRecord LinkRecord; 66 typedef MacroAssembler::JumpLinkType JumpLinkType; 67 #endif 68 69 public: 70 // Note: Initialization sequence is significant, since executablePool is a PassRefPtr. 71 // First, executablePool is copied into m_executablePool, then the initialization of 72 // m_code uses m_executablePool, *not* executablePool, since this is no longer valid. 73 // The linkOffset parameter should only be non-null when recompiling for exception info LinkBuffer(MacroAssembler * masm,PassRefPtr<ExecutablePool> executablePool,void * linkOffset)74 LinkBuffer(MacroAssembler* masm, PassRefPtr<ExecutablePool> executablePool, void* linkOffset) 75 : m_executablePool(executablePool) 76 , m_size(0) 77 , m_code(0) 78 , m_assembler(masm) 79 #ifndef NDEBUG 80 , m_completed(false) 81 #endif 82 { 83 linkCode(linkOffset); 84 } 85 ~LinkBuffer()86 ~LinkBuffer() 87 { 88 ASSERT(m_completed); 89 } 90 91 // These methods are used to link or set values at code generation time. 92 link(Call call,FunctionPtr function)93 void link(Call call, FunctionPtr function) 94 { 95 ASSERT(call.isFlagSet(Call::Linkable)); 96 call.m_jmp = applyOffset(call.m_jmp); 97 MacroAssembler::linkCall(code(), call, function); 98 } 99 link(Jump jump,CodeLocationLabel label)100 void link(Jump jump, CodeLocationLabel label) 101 { 102 jump.m_jmp = applyOffset(jump.m_jmp); 103 MacroAssembler::linkJump(code(), jump, label); 104 } 105 link(JumpList list,CodeLocationLabel label)106 void link(JumpList list, CodeLocationLabel label) 107 { 108 for (unsigned i = 0; i < list.m_jumps.size(); ++i) 109 link(list.m_jumps[i], label); 110 } 111 patch(DataLabelPtr label,void * value)112 void patch(DataLabelPtr label, void* value) 113 { 114 JmpDst target = applyOffset(label.m_label); 115 MacroAssembler::linkPointer(code(), target, value); 116 } 117 patch(DataLabelPtr label,CodeLocationLabel value)118 void patch(DataLabelPtr label, CodeLocationLabel value) 119 { 120 JmpDst target = applyOffset(label.m_label); 121 MacroAssembler::linkPointer(code(), target, value.executableAddress()); 122 } 123 124 // These methods are used to obtain handles to allow the code to be relinked / repatched later. 125 locationOf(Call call)126 CodeLocationCall locationOf(Call call) 127 { 128 ASSERT(call.isFlagSet(Call::Linkable)); 129 ASSERT(!call.isFlagSet(Call::Near)); 130 return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp))); 131 } 132 locationOfNearCall(Call call)133 CodeLocationNearCall locationOfNearCall(Call call) 134 { 135 ASSERT(call.isFlagSet(Call::Linkable)); 136 ASSERT(call.isFlagSet(Call::Near)); 137 return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp))); 138 } 139 locationOf(Label label)140 CodeLocationLabel locationOf(Label label) 141 { 142 return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); 143 } 144 locationOf(DataLabelPtr label)145 CodeLocationDataLabelPtr locationOf(DataLabelPtr label) 146 { 147 return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); 148 } 149 locationOf(DataLabel32 label)150 CodeLocationDataLabel32 locationOf(DataLabel32 label) 151 { 152 return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); 153 } 154 155 // This method obtains the return address of the call, given as an offset from 156 // the start of the code. returnAddressOffset(Call call)157 unsigned returnAddressOffset(Call call) 158 { 159 call.m_jmp = applyOffset(call.m_jmp); 160 return MacroAssembler::getLinkerCallReturnOffset(call); 161 } 162 163 // Upon completion of all patching either 'finalizeCode()' or 'finalizeCodeAddendum()' should be called 164 // once to complete generation of the code. 'finalizeCode()' is suited to situations 165 // where the executable pool must also be retained, the lighter-weight 'finalizeCodeAddendum()' is 166 // suited to adding to an existing allocation. finalizeCode()167 CodeRef finalizeCode() 168 { 169 performFinalization(); 170 171 return CodeRef(m_code, m_executablePool, m_size); 172 } 173 finalizeCodeAddendum()174 CodeLocationLabel finalizeCodeAddendum() 175 { 176 performFinalization(); 177 178 return CodeLocationLabel(code()); 179 } 180 trampolineAt(Label label)181 CodePtr trampolineAt(Label label) 182 { 183 return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); 184 } 185 186 #ifndef NDEBUG debugAddress()187 void* debugAddress() 188 { 189 return m_code; 190 } 191 #endif 192 193 private: applyOffset(T src)194 template <typename T> T applyOffset(T src) 195 { 196 #if ENABLE(BRANCH_COMPACTION) 197 src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); 198 #endif 199 return src; 200 } 201 202 // Keep this private! - the underlying code should only be obtained externally via 203 // finalizeCode() or finalizeCodeAddendum(). code()204 void* code() 205 { 206 return m_code; 207 } 208 linkCode(void * linkOffset)209 void linkCode(void* linkOffset) 210 { 211 UNUSED_PARAM(linkOffset); 212 ASSERT(!m_code); 213 #if !ENABLE(BRANCH_COMPACTION) 214 m_code = m_assembler->m_assembler.executableCopy(m_executablePool.get()); 215 m_size = m_assembler->size(); 216 #else 217 size_t initialSize = m_assembler->size(); 218 m_code = (uint8_t*)m_executablePool->alloc(initialSize); 219 if (!m_code) 220 return; 221 ExecutableAllocator::makeWritable(m_code, m_assembler->size()); 222 uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); 223 uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); 224 const uint8_t* linkBase = linkOffset ? reinterpret_cast<uint8_t*>(linkOffset) : outData; 225 int readPtr = 0; 226 int writePtr = 0; 227 Vector<LinkRecord>& jumpsToLink = m_assembler->jumpsToLink(); 228 unsigned jumpCount = jumpsToLink.size(); 229 for (unsigned i = 0; i < jumpCount; ++i) { 230 int offset = readPtr - writePtr; 231 ASSERT(!(offset & 1)); 232 233 // Copy the instructions from the last jump to the current one. 234 size_t regionSize = jumpsToLink[i].from() - readPtr; 235 memcpy(outData + writePtr, inData + readPtr, regionSize); 236 m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); 237 readPtr += regionSize; 238 writePtr += regionSize; 239 240 // Calculate absolute address of the jump target, in the case of backwards 241 // branches we need to be precise, forward branches we are pessimistic 242 const uint8_t* target; 243 if (jumpsToLink[i].to() >= jumpsToLink[i].from()) 244 target = linkBase + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far 245 else 246 target = linkBase + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); 247 248 JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], linkBase + writePtr, target); 249 // Compact branch if we can... 250 if (m_assembler->canCompact(jumpsToLink[i].type())) { 251 // Step back in the write stream 252 int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); 253 if (delta) { 254 writePtr -= delta; 255 m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); 256 } 257 } 258 jumpsToLink[i].setFrom(writePtr); 259 } 260 // Copy everything after the last jump 261 memcpy(outData + writePtr, inData + readPtr, m_assembler->size() - readPtr); 262 m_assembler->recordLinkOffsets(readPtr, m_assembler->size(), readPtr - writePtr); 263 264 // Actually link everything (don't link if we've be given a linkoffset as it's a 265 // waste of time: linkOffset is used for recompiling to get exception info) 266 if (!linkOffset) { 267 for (unsigned i = 0; i < jumpCount; ++i) { 268 uint8_t* location = outData + jumpsToLink[i].from(); 269 uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); 270 m_assembler->link(jumpsToLink[i], location, target); 271 } 272 } 273 274 jumpsToLink.clear(); 275 m_size = writePtr + m_assembler->size() - readPtr; 276 m_executablePool->tryShrink(m_code, initialSize, m_size); 277 278 #if DUMP_LINK_STATISTICS 279 dumpLinkStatistics(m_code, initialSize, m_size); 280 #endif 281 #if DUMP_CODE 282 dumpCode(m_code, m_size); 283 #endif 284 #endif 285 } 286 performFinalization()287 void performFinalization() 288 { 289 #ifndef NDEBUG 290 ASSERT(!m_completed); 291 m_completed = true; 292 #endif 293 294 ExecutableAllocator::makeExecutable(code(), m_size); 295 ExecutableAllocator::cacheFlush(code(), m_size); 296 } 297 298 #if DUMP_LINK_STATISTICS dumpLinkStatistics(void * code,size_t initialSize,size_t finalSize)299 static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize) 300 { 301 static unsigned linkCount = 0; 302 static unsigned totalInitialSize = 0; 303 static unsigned totalFinalSize = 0; 304 linkCount++; 305 totalInitialSize += initialSize; 306 totalFinalSize += finalSize; 307 printf("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", 308 code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize), 309 static_cast<unsigned>(initialSize - finalSize), 310 100.0 * (initialSize - finalSize) / initialSize); 311 printf("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", 312 linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, 313 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); 314 } 315 #endif 316 317 #if DUMP_CODE dumpCode(void * code,size_t size)318 static void dumpCode(void* code, size_t size) 319 { 320 #if CPU(ARM_THUMB2) 321 // Dump the generated code in an asm file format that can be assembled and then disassembled 322 // for debugging purposes. For example, save this output as jit.s: 323 // gcc -arch armv7 -c jit.s 324 // otool -tv jit.o 325 static unsigned codeCount = 0; 326 unsigned short* tcode = static_cast<unsigned short*>(code); 327 size_t tsize = size / sizeof(short); 328 char nameBuf[128]; 329 snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); 330 printf("\t.syntax unified\n" 331 "\t.section\t__TEXT,__text,regular,pure_instructions\n" 332 "\t.globl\t%s\n" 333 "\t.align 2\n" 334 "\t.code 16\n" 335 "\t.thumb_func\t%s\n" 336 "# %p\n" 337 "%s:\n", nameBuf, nameBuf, code, nameBuf); 338 339 for (unsigned i = 0; i < tsize; i++) 340 printf("\t.short\t0x%x\n", tcode[i]); 341 #endif 342 } 343 #endif 344 345 RefPtr<ExecutablePool> m_executablePool; 346 size_t m_size; 347 void* m_code; 348 MacroAssembler* m_assembler; 349 #ifndef NDEBUG 350 bool m_completed; 351 #endif 352 }; 353 354 } // namespace JSC 355 356 #endif // ENABLE(ASSEMBLER) 357 358 #endif // LinkBuffer_h 359