]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - assembler/LinkBuffer.h
JavaScriptCore-7600.1.4.11.8.tar.gz
[apple/javascriptcore.git] / assembler / LinkBuffer.h
index eff320d5748bdfb5ddbaf6e93aa71bcbadc03567..516e4568c8074d557d9b2e00188ce09d67d8703c 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2010, 2012, 2013, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #define GLOBAL_THUNK_ID reinterpret_cast<void*>(static_cast<intptr_t>(-1))
 #define REGEXP_CODE_ID reinterpret_cast<void*>(static_cast<intptr_t>(-2))
 
 #define GLOBAL_THUNK_ID reinterpret_cast<void*>(static_cast<intptr_t>(-1))
 #define REGEXP_CODE_ID reinterpret_cast<void*>(static_cast<intptr_t>(-2))
+#define CSS_CODE_ID reinterpret_cast<void*>(static_cast<intptr_t>(-3))
 
 #include "JITCompilationEffort.h"
 #include "MacroAssembler.h"
 #include <wtf/DataLog.h>
 
 #include "JITCompilationEffort.h"
 #include "MacroAssembler.h"
 #include <wtf/DataLog.h>
+#include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
 
 namespace JSC {
 
 #include <wtf/Noncopyable.h>
 
 namespace JSC {
 
-class JSGlobalData;
+class CodeBlock;
+class VM;
 
 // LinkBuffer:
 //
 
 // LinkBuffer:
 //
@@ -58,7 +61,8 @@ class JSGlobalData;
 //   * The value referenced by a DataLabel may be set.
 //
 class LinkBuffer {
 //   * The value referenced by a DataLabel may be set.
 //
 class LinkBuffer {
-    WTF_MAKE_NONCOPYABLE(LinkBuffer);
+    WTF_MAKE_NONCOPYABLE(LinkBuffer); WTF_MAKE_FAST_ALLOCATED;
+    
     typedef MacroAssemblerCodeRef CodeRef;
     typedef MacroAssemblerCodePtr CodePtr;
     typedef MacroAssembler::Label Label;
     typedef MacroAssemblerCodeRef CodeRef;
     typedef MacroAssemblerCodePtr CodePtr;
     typedef MacroAssembler::Label Label;
@@ -69,36 +73,50 @@ class LinkBuffer {
     typedef MacroAssembler::DataLabelCompact DataLabelCompact;
     typedef MacroAssembler::DataLabel32 DataLabel32;
     typedef MacroAssembler::DataLabelPtr DataLabelPtr;
     typedef MacroAssembler::DataLabelCompact DataLabelCompact;
     typedef MacroAssembler::DataLabel32 DataLabel32;
     typedef MacroAssembler::DataLabelPtr DataLabelPtr;
+    typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel;
 #if ENABLE(BRANCH_COMPACTION)
     typedef MacroAssembler::LinkRecord LinkRecord;
     typedef MacroAssembler::JumpLinkType JumpLinkType;
 #endif
 
 public:
 #if ENABLE(BRANCH_COMPACTION)
     typedef MacroAssembler::LinkRecord LinkRecord;
     typedef MacroAssembler::JumpLinkType JumpLinkType;
 #endif
 
 public:
-    LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed)
+    LinkBuffer(VM& vm, MacroAssembler& macroAssembler, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed)
         : m_size(0)
 #if ENABLE(BRANCH_COMPACTION)
         , m_initialSize(0)
 #endif
         : m_size(0)
 #if ENABLE(BRANCH_COMPACTION)
         , m_initialSize(0)
 #endif
+        , m_didAllocate(false)
         , m_code(0)
         , m_code(0)
-        , m_assembler(masm)
-        , m_globalData(&globalData)
+        , m_vm(&vm)
+#ifndef NDEBUG
+        , m_completed(false)
+#endif
+    {
+        linkCode(macroAssembler, ownerUID, effort);
+    }
+
+    LinkBuffer(VM& vm, MacroAssembler& macroAssembler, void* code, size_t size)
+        : m_size(size)
+#if ENABLE(BRANCH_COMPACTION)
+        , m_initialSize(0)
+#endif
+        , m_didAllocate(false)
+        , m_code(code)
+        , m_vm(&vm)
 #ifndef NDEBUG
         , m_completed(false)
 #ifndef NDEBUG
         , m_completed(false)
-        , m_effort(effort)
 #endif
     {
 #endif
     {
-        linkCode(ownerUID, effort);
+        linkCode(macroAssembler, 0, JITCompilationCanFail);
     }
 
     ~LinkBuffer()
     {
     }
 
     ~LinkBuffer()
     {
-        ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail));
     }
     
     bool didFailToAllocate() const
     {
     }
     
     bool didFailToAllocate() const
     {
-        return !m_executableMemory;
+        return !m_didAllocate;
     }
 
     bool isValid() const
     }
 
     bool isValid() const
@@ -115,6 +133,11 @@ public:
         MacroAssembler::linkCall(code(), call, function);
     }
     
         MacroAssembler::linkCall(code(), call, function);
     }
     
+    void link(Call call, CodeLocationLabel label)
+    {
+        link(call, FunctionPtr(label.executableAddress()));
+    }
+    
     void link(Jump jump, CodeLocationLabel label)
     {
         jump.m_label = applyOffset(jump.m_label);
     void link(Jump jump, CodeLocationLabel label)
     {
         jump.m_label = applyOffset(jump.m_label);
@@ -140,6 +163,11 @@ public:
     }
 
     // These methods are used to obtain handles to allow the code to be relinked / repatched later.
     }
 
     // These methods are used to obtain handles to allow the code to be relinked / repatched later.
+    
+    CodeLocationLabel entrypoint()
+    {
+        return CodeLocationLabel(code());
+    }
 
     CodeLocationCall locationOf(Call call)
     {
 
     CodeLocationCall locationOf(Call call)
     {
@@ -180,6 +208,11 @@ public:
         return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label)));
     }
 
         return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label)));
     }
 
+    CodeLocationConvertibleLoad locationOf(ConvertibleLoadLabel label)
+    {
+        return CodeLocationConvertibleLoad(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label)));
+    }
+
     // This method obtains the return address of the call, given as an offset from
     // the start of the code.
     unsigned returnAddressOffset(Call call)
     // This method obtains the return address of the call, given as an offset from
     // the start of the code.
     unsigned returnAddressOffset(Call call)
@@ -193,14 +226,19 @@ public:
         return applyOffset(label.m_label).m_offset;
     }
 
         return applyOffset(label.m_label).m_offset;
     }
 
-    // Upon completion of all patching 'finalizeCode()' should be called once to complete generation of the code.
-    CodeRef finalizeCode()
+    unsigned offsetOf(PatchableJump jump)
     {
     {
-        performFinalization();
-
-        return CodeRef(m_executableMemory);
+        return applyOffset(jump.m_jump.m_label).m_offset;
     }
 
     }
 
+    // Upon completion of all patching 'FINALIZE_CODE()' should be called once to
+    // complete generation of the code. Alternatively, call
+    // finalizeCodeWithoutDisassembly() directly if you have your own way of
+    // displaying disassembly.
+    
+    JS_EXPORT_PRIVATE CodeRef finalizeCodeWithoutDisassembly();
+    JS_EXPORT_PRIVATE CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3);
+
     CodePtr trampolineAt(Label label)
     {
         return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label)));
     CodePtr trampolineAt(Label label)
     {
         return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label)));
@@ -210,17 +248,27 @@ public:
     {
         return m_code;
     }
     {
         return m_code;
     }
-    
-    size_t debugSize()
+
+    // FIXME: this does not account for the AssemblerData size!
+    size_t size()
     {
         return m_size;
     }
 
 private:
     {
         return m_size;
     }
 
 private:
+#if ENABLE(BRANCH_COMPACTION)
+    int executableOffsetFor(int location)
+    {
+        if (!location)
+            return 0;
+        return bitwise_cast<int32_t*>(m_assemblerStorage.buffer())[location / sizeof(int32_t) - 1];
+    }
+#endif
+    
     template <typename T> T applyOffset(T src)
     {
 #if ENABLE(BRANCH_COMPACTION)
     template <typename T> T applyOffset(T src)
     {
 #if ENABLE(BRANCH_COMPACTION)
-        src.m_offset -= m_assembler->executableOffsetFor(src.m_offset);
+        src.m_offset -= executableOffsetFor(src.m_offset);
 #endif
         return src;
     }
 #endif
         return src;
     }
@@ -230,186 +278,72 @@ private:
     {
         return m_code;
     }
     {
         return m_code;
     }
+    
+    void allocate(size_t initialSize, void* ownerUID, JITCompilationEffort);
+    void shrink(size_t newSize);
 
 
-    void linkCode(void* ownerUID, JITCompilationEffort effort)
-    {
-        ASSERT(!m_code);
-#if !ENABLE(BRANCH_COMPACTION)
-        m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort);
-        if (!m_executableMemory)
-            return;
-        m_code = m_executableMemory->start();
-        m_size = m_assembler->m_assembler.codeSize();
-        ASSERT(m_code);
-#else
-        m_initialSize = m_assembler->m_assembler.codeSize();
-        m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort);
-        if (!m_executableMemory)
-            return;
-        m_code = (uint8_t*)m_executableMemory->start();
-        ASSERT(m_code);
-        ExecutableAllocator::makeWritable(m_code, m_initialSize);
-        uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode();
-        uint8_t* outData = reinterpret_cast<uint8_t*>(m_code);
-        int readPtr = 0;
-        int writePtr = 0;
-        Vector<LinkRecord>& jumpsToLink = m_assembler->jumpsToLink();
-        unsigned jumpCount = jumpsToLink.size();
-        for (unsigned i = 0; i < jumpCount; ++i) {
-            int offset = readPtr - writePtr;
-            ASSERT(!(offset & 1));
-            
-            // Copy the instructions from the last jump to the current one.
-            size_t regionSize = jumpsToLink[i].from() - readPtr;
-            uint16_t* copySource = reinterpret_cast_ptr<uint16_t*>(inData + readPtr);
-            uint16_t* copyEnd = reinterpret_cast_ptr<uint16_t*>(inData + readPtr + regionSize);
-            uint16_t* copyDst = reinterpret_cast_ptr<uint16_t*>(outData + writePtr);
-            ASSERT(!(regionSize % 2));
-            ASSERT(!(readPtr % 2));
-            ASSERT(!(writePtr % 2));
-            while (copySource != copyEnd)
-                *copyDst++ = *copySource++;
-            m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset);
-            readPtr += regionSize;
-            writePtr += regionSize;
-            
-            // Calculate absolute address of the jump target, in the case of backwards
-            // branches we need to be precise, forward branches we are pessimistic
-            const uint8_t* target;
-            if (jumpsToLink[i].to() >= jumpsToLink[i].from())
-                target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far
-            else
-                target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to());
-            
-            JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target);
-            // Compact branch if we can...
-            if (m_assembler->canCompact(jumpsToLink[i].type())) {
-                // Step back in the write stream
-                int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType);
-                if (delta) {
-                    writePtr -= delta;
-                    m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr);
-                }
-            }
-            jumpsToLink[i].setFrom(writePtr);
-        }
-        // Copy everything after the last jump
-        memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr);
-        m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr);
-        
-        for (unsigned i = 0; i < jumpCount; ++i) {
-            uint8_t* location = outData + jumpsToLink[i].from();
-            uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to());
-            m_assembler->link(jumpsToLink[i], location, target);
-        }
-
-        jumpsToLink.clear();
-        m_size = writePtr + m_initialSize - readPtr;
-        m_executableMemory->shrink(m_size);
-
-#if DUMP_LINK_STATISTICS
-        dumpLinkStatistics(m_code, m_initialSize, m_size);
-#endif
-#if DUMP_CODE
-        dumpCode(m_code, m_size);
-#endif
-#endif
-    }
-
-    void performFinalization()
-    {
-#ifndef NDEBUG
-        ASSERT(!m_completed);
-        ASSERT(isValid());
-        m_completed = true;
-#endif
-
+    JS_EXPORT_PRIVATE void linkCode(MacroAssembler&, void* ownerUID, JITCompilationEffort);
 #if ENABLE(BRANCH_COMPACTION)
 #if ENABLE(BRANCH_COMPACTION)
-        ExecutableAllocator::makeExecutable(code(), m_initialSize);
-#else
-        ExecutableAllocator::makeExecutable(code(), m_size);
+    template <typename InstructionType>
+    void copyCompactAndLinkCode(MacroAssembler&, void* ownerUID, JITCompilationEffort);
 #endif
 #endif
-        MacroAssembler::cacheFlush(code(), m_size);
-    }
+
+    void performFinalization();
 
 #if DUMP_LINK_STATISTICS
 
 #if DUMP_LINK_STATISTICS
-    static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize)
-    {
-        static unsigned linkCount = 0;
-        static unsigned totalInitialSize = 0;
-        static unsigned totalFinalSize = 0;
-        linkCount++;
-        totalInitialSize += initialSize;
-        totalFinalSize += finalSize;
-        dataLog("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", 
-                    code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize),
-                    static_cast<unsigned>(initialSize - finalSize),
-                    100.0 * (initialSize - finalSize) / initialSize);
-        dataLog("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", 
-                    linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize,
-                    100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize);
-    }
+    static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize);
 #endif
     
 #if DUMP_CODE
 #endif
     
 #if DUMP_CODE
-    static void dumpCode(void* code, size_t size)
-    {
-#if CPU(ARM_THUMB2)
-        // Dump the generated code in an asm file format that can be assembled and then disassembled
-        // for debugging purposes. For example, save this output as jit.s:
-        //   gcc -arch armv7 -c jit.s
-        //   otool -tv jit.o
-        static unsigned codeCount = 0;
-        unsigned short* tcode = static_cast<unsigned short*>(code);
-        size_t tsize = size / sizeof(short);
-        char nameBuf[128];
-        snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++);
-        dataLog("\t.syntax unified\n"
-                    "\t.section\t__TEXT,__text,regular,pure_instructions\n"
-                    "\t.globl\t%s\n"
-                    "\t.align 2\n"
-                    "\t.code 16\n"
-                    "\t.thumb_func\t%s\n"
-                    "# %p\n"
-                    "%s:\n", nameBuf, nameBuf, code, nameBuf);
-        
-        for (unsigned i = 0; i < tsize; i++)
-            dataLog("\t.short\t0x%x\n", tcode[i]);
-#elif CPU(ARM_TRADITIONAL)
-        //   gcc -c jit.s
-        //   objdump -D jit.o
-        static unsigned codeCount = 0;
-        unsigned int* tcode = static_cast<unsigned int*>(code);
-        size_t tsize = size / sizeof(unsigned int);
-        char nameBuf[128];
-        snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++);
-        dataLog("\t.globl\t%s\n"
-                    "\t.align 4\n"
-                    "\t.code 32\n"
-                    "\t.text\n"
-                    "# %p\n"
-                    "%s:\n", nameBuf, code, nameBuf);
-
-        for (unsigned i = 0; i < tsize; i++)
-            dataLog("\t.long\t0x%x\n", tcode[i]);
-#endif
-    }
+    static void dumpCode(void* code, size_t);
 #endif
     
     RefPtr<ExecutableMemoryHandle> m_executableMemory;
     size_t m_size;
 #if ENABLE(BRANCH_COMPACTION)
     size_t m_initialSize;
 #endif
     
     RefPtr<ExecutableMemoryHandle> m_executableMemory;
     size_t m_size;
 #if ENABLE(BRANCH_COMPACTION)
     size_t m_initialSize;
+    AssemblerData m_assemblerStorage;
 #endif
 #endif
+    bool m_didAllocate;
     void* m_code;
     void* m_code;
-    MacroAssembler* m_assembler;
-    JSGlobalData* m_globalData;
+    VM* m_vm;
 #ifndef NDEBUG
     bool m_completed;
 #ifndef NDEBUG
     bool m_completed;
-    JITCompilationEffort m_effort;
 #endif
 };
 
 #endif
 };
 
+#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading)  \
+    (UNLIKELY((condition))                                              \
+     ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \
+     : (linkBufferReference).finalizeCodeWithoutDisassembly())
+
+bool shouldShowDisassemblyFor(CodeBlock*);
+
+#define FINALIZE_CODE_FOR(codeBlock, linkBufferReference, dataLogFArgumentsForHeading)  \
+    FINALIZE_CODE_IF(shouldShowDisassemblyFor(codeBlock), linkBufferReference, dataLogFArgumentsForHeading)
+
+// Use this to finalize code, like so:
+//
+// CodeRef code = FINALIZE_CODE(linkBuffer, ("my super thingy number %d", number));
+//
+// Which, in disassembly mode, will print:
+//
+// Generated JIT code for my super thingy number 42:
+//     Code at [0x123456, 0x234567]:
+//         0x123456: mov $0, 0
+//         0x12345a: ret
+//
+// ... and so on.
+//
+// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly
+// is true, so you can hide expensive disassembly-only computations inside there.
+
+#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading)  \
+    FINALIZE_CODE_IF(JSC::Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
+
+#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading)  \
+    FINALIZE_CODE_IF((JSC::Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading)
+
 } // namespace JSC
 
 #endif // ENABLE(ASSEMBLER)
 } // namespace JSC
 
 #endif // ENABLE(ASSEMBLER)