Ignore:
Timestamp:
Aug 9, 2010, 8:19:19 PM (15 years ago)
Author:
[email protected]
Message:

2010-08-09 Oliver Hunt <[email protected]>

Reviewed by Gavin Barraclough.

Allow an assembler/macroassembler to compact branches to more concise forms when linking
https://bugs.webkit.org/show_bug.cgi?id=43745

This patch makes it possible for an assembler to convert jumps into a different
(presumably more efficient) form at link time. Currently implemented in the
ARMv7 JIT as that already had logic to delay linking of jumps until the end of
compilation already. The ARMv7 JIT chooses between either a 4 byte short jump
or a full 32-bit offset (and rewrites ITTT instructions as appropriate), so does
not yet produce the most compact form possible. The general design of the linker
should make it relatively simple to introduce new branch types with little effort,
as the linker has no knowledge of the exact form of any of the branches.

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • assembler/ARMv7Assembler.cpp: Added. (JSC::): Record jump sizes
  • assembler/ARMv7Assembler.h: (JSC::ARMv7Assembler::LinkRecord::LinkRecord): (JSC::ARMv7Assembler::LinkRecord::from): (JSC::ARMv7Assembler::LinkRecord::setFrom): (JSC::ARMv7Assembler::LinkRecord::to): (JSC::ARMv7Assembler::LinkRecord::type): (JSC::ARMv7Assembler::LinkRecord::linkType): (JSC::ARMv7Assembler::LinkRecord::setLinkType): Encapsulate LinkRecord fields so we can compress the values somewhat

(JSC::ARMv7Assembler::JmpSrc::JmpSrc):

Need to record the jump type now

(JSC::ARMv7Assembler::b):
(JSC::ARMv7Assembler::blx):
(JSC::ARMv7Assembler::bx):

Need to pass the jump types

(JSC::ARMv7Assembler::executableOffsetFor):
(JSC::ARMv7Assembler::jumpSizeDelta):
(JSC::ARMv7Assembler::linkRecordSourceComparator):
(JSC::ARMv7Assembler::computeJumpType):
(JSC::ARMv7Assembler::convertJumpTo):
(JSC::ARMv7Assembler::recordLinkOffsets):
(JSC::ARMv7Assembler::jumpsToLink):
(JSC::ARMv7Assembler::link):
(JSC::ARMv7Assembler::unlinkedCode):

Helper functions for the linker

(JSC::ARMv7Assembler::linkJump):
(JSC::ARMv7Assembler::canBeShortJump):
(JSC::ARMv7Assembler::linkLongJump):
(JSC::ARMv7Assembler::linkShortJump):
(JSC::ARMv7Assembler::linkJumpAbsolute):

Moving code around for the various jump linking functions

  • assembler/AbstractMacroAssembler.h: (JSC::AbstractMacroAssembler::beginUninterruptedSequence): (JSC::AbstractMacroAssembler::endUninterruptedSequence): We have to track uninterrupted sequences in any assembler that compacts branches as that's not something we're allowed to do in such sequences. AbstractMacroAssembler has a nop version of these functions as it makes the code elsewhere nicer.
  • assembler/LinkBuffer.h: (JSC::LinkBuffer::LinkBuffer): (JSC::LinkBuffer::link): (JSC::LinkBuffer::patch): (JSC::LinkBuffer::locationOf): (JSC::LinkBuffer::locationOfNearCall): (JSC::LinkBuffer::returnAddressOffset): (JSC::LinkBuffer::trampolineAt): Updated these functions to adjust for any changed offsets in the linked code

(JSC::LinkBuffer::applyOffset):

A helper function to deal with the now potentially moved labels

(JSC::LinkBuffer::linkCode):

The new and mighty linker function

  • assembler/MacroAssemblerARMv7.h: (JSC::MacroAssemblerARMv7::MacroAssemblerARMv7): (JSC::MacroAssemblerARMv7::beginUninterruptedSequence): (JSC::MacroAssemblerARMv7::endUninterruptedSequence): (JSC::MacroAssemblerARMv7::jumpsToLink): (JSC::MacroAssemblerARMv7::unlinkedCode): (JSC::MacroAssemblerARMv7::computeJumpType): (JSC::MacroAssemblerARMv7::convertJumpTo): (JSC::MacroAssemblerARMv7::recordLinkOffsets): (JSC::MacroAssemblerARMv7::jumpSizeDelta): (JSC::MacroAssemblerARMv7::link): (JSC::MacroAssemblerARMv7::jump): (JSC::MacroAssemblerARMv7::branchMul32): (JSC::MacroAssemblerARMv7::breakpoint): (JSC::MacroAssemblerARMv7::nearCall): (JSC::MacroAssemblerARMv7::call): (JSC::MacroAssemblerARMv7::ret): (JSC::MacroAssemblerARMv7::tailRecursiveCall): (JSC::MacroAssemblerARMv7::executableOffsetFor): (JSC::MacroAssemblerARMv7::inUninterruptedSequence): (JSC::MacroAssemblerARMv7::makeJump): (JSC::MacroAssemblerARMv7::makeBranch):

All branches need to pass on their type now

  • jit/ExecutableAllocator.h: (JSC::ExecutablePool::returnLastBytes):

We can't know ahead of time how much space will be necessary to
hold the linked code if we're compacting branches, this new
function allows us to return the unused bytes at the end of linking

  • jit/JIT.cpp: (JSC::JIT::JIT): (JSC::JIT::privateCompile):
  • jit/JIT.h: (JSC::JIT::compile):

The JIT class now needs to take a linker offset so that recompilation
can generate the same jumps when using branch compaction.

  • jit/JITArithmetic32_64.cpp: (JSC::JIT::emitSlow_op_mod):
  • jit/JITOpcodes.cpp: (JSC::JIT::privateCompileCTIMachineTrampolines):
  • jit/JITOpcodes32_64.cpp: (JSC::JIT::privateCompileCTIMachineTrampolines): (JSC::JIT::privateCompileCTINativeCall): Update for new trampolineAt changes
  • wtf/FastMalloc.cpp: (WTF::TCMallocStats::):
  • wtf/Platform.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/assembler/LinkBuffer.h

    r64608 r65042  
    5050class LinkBuffer : public Noncopyable {
    5151    typedef MacroAssemblerCodeRef CodeRef;
     52    typedef MacroAssemblerCodePtr CodePtr;
    5253    typedef MacroAssembler::Label Label;
    5354    typedef MacroAssembler::Jump Jump;
     
    5657    typedef MacroAssembler::DataLabel32 DataLabel32;
    5758    typedef MacroAssembler::DataLabelPtr DataLabelPtr;
     59    typedef MacroAssembler::JmpDst JmpDst;
     60#if ENABLE(BRANCH_COMPACTION)
     61    typedef MacroAssembler::LinkRecord LinkRecord;
     62    typedef MacroAssembler::JumpLinkType JumpLinkType;
     63#endif
    5864
    5965    enum LinkBufferState {
     
    6773    //       First, executablePool is copied into m_executablePool, then the initialization of
    6874    //       m_code uses m_executablePool, *not* executablePool, since this is no longer valid.
    69     LinkBuffer(MacroAssembler* masm, PassRefPtr<ExecutablePool> executablePool)
     75    // The linkOffset parameter should only be non-null when recompiling for exception info
     76    LinkBuffer(MacroAssembler* masm, PassRefPtr<ExecutablePool> executablePool, void* linkOffset)
    7077        : m_executablePool(executablePool)
    71         , m_code(masm->m_assembler.executableCopy(m_executablePool.get()))
    72         , m_size(masm->m_assembler.size())
     78        , m_size(0)
     79        , m_code(0)
     80        , m_assembler(masm)
    7381#ifndef NDEBUG
    7482        , m_state(StateInit)
    7583#endif
    7684    {
     85        linkCode(linkOffset);
    7786    }
    7887
     
    98107    {
    99108        ASSERT(call.isFlagSet(Call::Linkable));
     109        call.m_jmp = applyOffset(call.m_jmp);
    100110        MacroAssembler::linkCall(code(), call, function);
    101111    }
     
    103113    void link(Jump jump, CodeLocationLabel label)
    104114    {
     115        jump.m_jmp = applyOffset(jump.m_jmp);
    105116        MacroAssembler::linkJump(code(), jump, label);
    106117    }
     
    109120    {
    110121        for (unsigned i = 0; i < list.m_jumps.size(); ++i)
    111             MacroAssembler::linkJump(code(), list.m_jumps[i], label);
     122            link(list.m_jumps[i], label);
    112123    }
    113124
    114125    void patch(DataLabelPtr label, void* value)
    115126    {
    116         MacroAssembler::linkPointer(code(), label.m_label, value);
     127        JmpDst target = applyOffset(label.m_label);
     128        MacroAssembler::linkPointer(code(), target, value);
    117129    }
    118130
    119131    void patch(DataLabelPtr label, CodeLocationLabel value)
    120132    {
    121         MacroAssembler::linkPointer(code(), label.m_label, value.executableAddress());
     133        JmpDst target = applyOffset(label.m_label);
     134        MacroAssembler::linkPointer(code(), target, value.executableAddress());
    122135    }
    123136
     
    128141        ASSERT(call.isFlagSet(Call::Linkable));
    129142        ASSERT(!call.isFlagSet(Call::Near));
    130         return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), call.m_jmp));
     143        return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp)));
    131144    }
    132145
     
    135148        ASSERT(call.isFlagSet(Call::Linkable));
    136149        ASSERT(call.isFlagSet(Call::Near));
    137         return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), call.m_jmp));
     150        return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp)));
    138151    }
    139152
    140153    CodeLocationLabel locationOf(Label label)
    141154    {
    142         return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), label.m_label));
     155        return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label)));
    143156    }
    144157
    145158    CodeLocationDataLabelPtr locationOf(DataLabelPtr label)
    146159    {
    147         return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), label.m_label));
     160        return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label)));
    148161    }
    149162
    150163    CodeLocationDataLabel32 locationOf(DataLabel32 label)
    151164    {
    152         return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), label.m_label));
     165        return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label)));
    153166    }
    154167
     
    157170    unsigned returnAddressOffset(Call call)
    158171    {
     172        call.m_jmp = applyOffset(call.m_jmp);
    159173        return MacroAssembler::getLinkerCallReturnOffset(call);
    160174    }
     
    170184        return CodeRef(m_code, m_executablePool, m_size);
    171185    }
     186
    172187    CodeLocationLabel finalizeCodeAddendum()
    173188    {
     
    177192    }
    178193
     194    CodePtr trampolineAt(Label label)
     195    {
     196        return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label)));
     197    }
     198
    179199private:
     200    template <typename T> T applyOffset(T src)
     201    {
     202#if ENABLE(BRANCH_COMPACTION)
     203        src.m_offset -= m_assembler->executableOffsetFor(src.m_offset);
     204#endif
     205        return src;
     206    }
     207   
    180208    // Keep this private! - the underlying code should only be obtained externally via
    181209    // finalizeCode() or finalizeCodeAddendum().
     
    185213    }
    186214
     215    void linkCode(void* linkOffset)
     216    {
     217        UNUSED_PARAM(linkOffset);
     218        ASSERT(!m_code);
     219#if !ENABLE(BRANCH_COMPACTION)
     220        m_code = m_assembler->m_assembler.executableCopy(m_executablePool.get());
     221        m_size = m_assembler->size();
     222#else
     223        size_t initialSize = m_assembler->size();
     224        m_code = (uint8_t*)m_executablePool->alloc(initialSize);
     225        if (!m_code)
     226            return;
     227        ExecutableAllocator::makeWritable(m_code, m_assembler->size());
     228        uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode();
     229        uint8_t* outData = reinterpret_cast<uint8_t*>(m_code);
     230        const uint8_t* linkBase = linkOffset ? reinterpret_cast<uint8_t*>(linkOffset) : outData;
     231        int readPtr = 0;
     232        int writePtr = 0;
     233        Vector<LinkRecord>& jumpsToLink = m_assembler->jumpsToLink();
     234        unsigned jumpCount = jumpsToLink.size();
     235        for (unsigned i = 0; i < jumpCount; ++i) {
     236            int offset = readPtr - writePtr;
     237            ASSERT(!(offset & 1));
     238           
     239            // Copy the instructions from the last jump to the current one.
     240            size_t regionSize = jumpsToLink[i].from() - readPtr;
     241            memcpy(outData + writePtr, inData + readPtr, regionSize);
     242            m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset);
     243            readPtr += regionSize;
     244            writePtr += regionSize;
     245           
     246            // Calculate absolute address of the jump target, in the case of backwards
     247            // branches we need to be precise, forward branches we are pessimistic
     248            const uint8_t* target;
     249            if (jumpsToLink[i].to() >= jumpsToLink[i].from())
     250                target = linkBase + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far
     251            else
     252                target = linkBase + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to());
     253           
     254            JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], linkBase + writePtr, target);
     255
     256            // Step back in the write stream
     257            int32_t delta = m_assembler->jumpSizeDelta(jumpLinkType);
     258            if (delta) {
     259                writePtr -= delta;
     260                m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr);
     261            }
     262            jumpsToLink[i].setFrom(writePtr);
     263        }
     264        // Copy everything after the last jump
     265        memcpy(outData + writePtr, inData + readPtr, m_assembler->size() - readPtr);
     266        m_assembler->recordLinkOffsets(readPtr, m_assembler->size(), readPtr - writePtr);
     267       
     268        // Actually link everything (don't link if we've be given a linkoffset as it's a
     269        // waste of time: linkOffset is used for recompiling to get exception info)
     270        if (!linkOffset) {
     271            for (unsigned i = 0; i < jumpCount; ++i) {
     272                uint8_t* location = outData + jumpsToLink[i].from();
     273                uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to());
     274                m_assembler->link(jumpsToLink[i], location, target);
     275            }
     276        }
     277
     278        jumpsToLink.clear();
     279        m_size = writePtr + m_assembler->size() - readPtr;
     280        m_executablePool->returnLastBytes(initialSize - m_size);
     281#endif
     282    }
     283
    187284    void performFinalization()
    188285    {
     
    197294
    198295    RefPtr<ExecutablePool> m_executablePool;
     296    size_t m_size;
    199297    void* m_code;
    200     size_t m_size;
     298    MacroAssembler* m_assembler;
    201299#ifndef NDEBUG
    202300    LinkBufferState m_state;
Note: See TracChangeset for help on using the changeset viewer.