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/ARMv7Assembler.h

    r64608 r65042  
    382382        u.d = d;
    383383
    384         int sign = (u.i >> 63);
    385         int exponent = (u.i >> 52) & 0x7ff;
     384        int sign = static_cast<int>(u.i >> 63);
     385        int exponent = static_cast<int>(u.i >> 52) & 0x7ff;
    386386        uint64_t mantissa = u.i & 0x000fffffffffffffull;
    387387
     
    445445    } m_u;
    446446};
    447 
    448447
    449448class ARMv7Assembler {
     
    477476        ConditionLE,
    478477        ConditionAL,
    479 
     478       
    480479        ConditionCS = ConditionHS,
    481480        ConditionCC = ConditionLO,
    482481    } Condition;
    483482
     483    enum JumpType { JumpNoCondition, JumpCondition, JumpFullSize };
     484    enum JumpLinkType { LinkInvalid, LinkShortJump, LinkConditionalShortJump, LinkLongJump, JumpTypeCount };
     485    static const int JumpSizes[JumpTypeCount];
     486    enum { JumpPaddingSize = 5 * sizeof(uint16_t) };
     487    class LinkRecord {
     488    public:
     489        LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition)
     490            : m_from(from)
     491            , m_to(to)
     492            , m_type(type)
     493            , m_linkType(LinkInvalid)
     494            , m_condition(condition)
     495        {
     496        }
     497        intptr_t from() const { return m_from; }
     498        void setFrom(intptr_t from) { m_from = from; }
     499        intptr_t to() const { return m_to; }
     500        JumpType type() const { return m_type; }
     501        JumpLinkType linkType() const { return m_linkType; }
     502        void setLinkType(JumpLinkType linkType) { ASSERT(m_linkType == LinkInvalid); m_linkType = linkType; }
     503        Condition condition() const { return m_condition; }
     504    private:
     505        intptr_t m_from : 31;
     506        intptr_t m_to : 31;
     507        JumpType m_type : 2;
     508        JumpLinkType m_linkType : 3;
     509        Condition m_condition : 16;
     510    };
     511   
    484512    class JmpSrc {
    485513        friend class ARMv7Assembler;
    486514        friend class ARMInstructionFormatter;
     515        friend class LinkBuffer;
    487516    public:
    488517        JmpSrc()
     
    492521
    493522    private:
    494         JmpSrc(int offset)
     523        JmpSrc(int offset, JumpType type)
    495524            : m_offset(offset)
    496         {
     525            , m_condition(0xffff)
     526            , m_type(type)
     527        {
     528            ASSERT(m_type != JumpCondition);
     529        }
     530
     531        JmpSrc(int offset, JumpType type, Condition condition)
     532            : m_offset(offset)
     533            , m_condition(condition)
     534            , m_type(type)
     535        {
     536            ASSERT(m_type == JumpCondition || m_type == JumpFullSize);
    497537        }
    498538
    499539        int m_offset;
     540        Condition m_condition : 16;
     541        JumpType m_type : 16;
     542       
    500543    };
    501544   
     
    503546        friend class ARMv7Assembler;
    504547        friend class ARMInstructionFormatter;
     548        friend class LinkBuffer;
    505549    public:
    506550        JmpDst()
     
    525569
    526570private:
    527 
    528     struct LinkRecord {
    529         LinkRecord(intptr_t from, intptr_t to)
    530             : from(from)
    531             , to(to)
    532         {
    533         }
    534 
    535         intptr_t from;
    536         intptr_t to;
    537     };
    538571
    539572    // ARMv7, Appx-A.6.3
     
    740773
    741774public:
    742 
     775   
    743776    void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm)
    744777    {
     
    879912        m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm));
    880913    }
    881 
     914   
    882915    // Only allowed in IT (if then) block if last instruction.
    883     JmpSrc b()
     916    JmpSrc b(JumpType type)
    884917    {
    885918        m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b);
    886         return JmpSrc(m_formatter.size());
     919        return JmpSrc(m_formatter.size(), type);
    887920    }
    888921   
    889922    // Only allowed in IT (if then) block if last instruction.
    890     JmpSrc blx(RegisterID rm)
     923    JmpSrc blx(RegisterID rm, JumpType type)
    891924    {
    892925        ASSERT(rm != ARMRegisters::pc);
    893926        m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8);
    894         return JmpSrc(m_formatter.size());
     927        return JmpSrc(m_formatter.size(), type);
    895928    }
    896929
    897930    // Only allowed in IT (if then) block if last instruction.
    898     JmpSrc bx(RegisterID rm)
     931    JmpSrc bx(RegisterID rm, JumpType type, Condition condition)
    899932    {
    900933        m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0);
    901         return JmpSrc(m_formatter.size());
     934        return JmpSrc(m_formatter.size(), type, condition);
     935    }
     936
     937    JmpSrc bx(RegisterID rm, JumpType type)
     938    {
     939        m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0);
     940        return JmpSrc(m_formatter.size(), type);
    902941    }
    903942
     
    16181657        return dst.m_offset - src.m_offset;
    16191658    }
     1659
     1660    int executableOffsetFor(int location)
     1661    {
     1662        if (!location)
     1663            return 0;
     1664        return static_cast<int32_t*>(m_formatter.data())[location / sizeof(int32_t) - 1];
     1665    }
     1666   
     1667    int jumpSizeDelta(JumpLinkType jumpLinkType) { return JumpPaddingSize - JumpSizes[jumpLinkType]; }
    16201668   
    16211669    // Assembler admin methods:
     
    16261674    }
    16271675
    1628     void* executableCopy(ExecutablePool* allocator)
    1629     {
    1630         void* copy = m_formatter.executableCopy(allocator);
    1631         if (!copy)
    1632             return 0;
    1633 
    1634         unsigned jumpCount = m_jumpsToLink.size();
    1635         for (unsigned i = 0; i < jumpCount; ++i) {
    1636             uint16_t* location = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(copy) + m_jumpsToLink[i].from);
    1637             uint16_t* target = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(copy) + m_jumpsToLink[i].to);
    1638             linkJumpAbsolute(location, target);
    1639         }
    1640         m_jumpsToLink.clear();
    1641 
    1642         return copy;
    1643     }
    1644 
     1676    static bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b)
     1677    {
     1678        return a.from() < b.from();
     1679    }
     1680
     1681    JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to)
     1682    {
     1683        if (record.type() >= JumpFullSize) {
     1684            record.setLinkType(LinkLongJump);
     1685            return LinkLongJump;
     1686        }
     1687        bool mayTriggerErrata = false;
     1688        const uint16_t* shortJumpLocation = reinterpret_cast<const uint16_t*>(from  - (JumpPaddingSize - JumpSizes[LinkShortJump]));
     1689        if (!canBeShortJump(shortJumpLocation, to, mayTriggerErrata)) {
     1690            record.setLinkType(LinkLongJump);
     1691            return LinkLongJump;
     1692        }
     1693        if (mayTriggerErrata) {
     1694            record.setLinkType(LinkLongJump);
     1695            return LinkLongJump;
     1696        }
     1697        if (record.type() == JumpCondition) {
     1698            record.setLinkType(LinkConditionalShortJump);
     1699            return LinkConditionalShortJump;
     1700        }
     1701        record.setLinkType(LinkShortJump);
     1702        return LinkShortJump;
     1703    }
     1704
     1705    void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset)
     1706    {
     1707        int32_t ptr = regionStart / sizeof(int32_t);
     1708        const int32_t end = regionEnd / sizeof(int32_t);
     1709        int32_t* offsets = static_cast<int32_t*>(m_formatter.data());
     1710        while (ptr < end)
     1711            offsets[ptr++] = offset;
     1712    }
     1713   
     1714    Vector<LinkRecord>& jumpsToLink()
     1715    {
     1716        std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator);
     1717        return m_jumpsToLink;
     1718    }
     1719
     1720    void link(LinkRecord& record, uint8_t* from, uint8_t* to)
     1721    {
     1722        uint16_t* itttLocation;
     1723        if (record.linkType() == LinkConditionalShortJump) {
     1724            itttLocation = reinterpret_cast<uint16_t*>(from - JumpSizes[LinkConditionalShortJump] - 2);
     1725            itttLocation[0] = ifThenElse(record.condition()) | OP_IT;
     1726        }
     1727        ASSERT(record.linkType() != LinkInvalid);
     1728        if (record.linkType() != LinkLongJump)
     1729            linkShortJump(reinterpret_cast<uint16_t*>(from), to);
     1730        else
     1731            linkLongJump(reinterpret_cast<uint16_t*>(from), to);
     1732    }
     1733
     1734    void* unlinkedCode() { return m_formatter.data(); }
     1735   
    16451736    static unsigned getCallReturnOffset(JmpSrc call)
    16461737    {
     
    16611752        ASSERT(to.m_offset != -1);
    16621753        ASSERT(from.m_offset != -1);
    1663         m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset));
     1754        m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, from.m_type, from.m_condition));
    16641755    }
    16651756
     
    18641955    }
    18651956
    1866     static void linkJumpAbsolute(uint16_t* instruction, void* target)
    1867     {
    1868         // FIMXE: this should be up in the MacroAssembler layer. :-(
    1869         const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip;
    1870 
     1957    static bool canBeShortJump(const uint16_t* instruction, const void* target, bool& mayTriggerErrata)
     1958    {
    18711959        ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
    18721960        ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
    1873 
    1874         ASSERT( (isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1))
    1875             || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2)) );
    1876 
     1961       
    18771962        intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
    1878 
    18791963        // From Cortex-A8 errata:
    18801964        // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and
     
    18851969        // The instruction is spanning two pages if it ends at an address ending 0x002
    18861970        bool spansTwo4K = ((reinterpret_cast<intptr_t>(instruction) & 0xfff) == 0x002);
     1971        mayTriggerErrata = spansTwo4K;
    18871972        // The target is in the first page if the jump branch back by [3..0x1002] bytes
    18881973        bool targetInFirstPage = (relative >= -0x1002) && (relative < -2);
    18891974        bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage;
    1890 
    1891         if (((relative << 7) >> 7) == relative && !wouldTriggerA8Errata) {
     1975        return ((relative << 7) >> 7) == relative && !wouldTriggerA8Errata;
     1976    }
     1977
     1978    static void linkLongJump(uint16_t* instruction, void* target)
     1979    {
     1980        linkJumpAbsolute(instruction, target);
     1981    }
     1982   
     1983    static void linkShortJump(uint16_t* instruction, void* target)
     1984    {
     1985        // FIMXE: this should be up in the MacroAssembler layer. :-(       
     1986        ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
     1987        ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
     1988       
     1989        intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
     1990        bool scratch;
     1991        UNUSED_PARAM(scratch);
     1992        ASSERT(canBeShortJump(instruction, target, scratch));
     1993        // ARM encoding for the top two bits below the sign bit is 'peculiar'.
     1994        if (relative >= 0)
     1995            relative ^= 0xC00000;
     1996
     1997        // All branch offsets should be an even distance.
     1998        ASSERT(!(relative & 1));
     1999        instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12);
     2000        instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1);
     2001    }
     2002
     2003    static void linkJumpAbsolute(uint16_t* instruction, void* target)
     2004    {
     2005        // FIMXE: this should be up in the MacroAssembler layer. :-(
     2006        ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
     2007        ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
     2008
     2009        ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1))
     2010            || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2)));
     2011
     2012        intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
     2013        bool scratch;
     2014        if (canBeShortJump(instruction, target, scratch)) {
    18922015            // ARM encoding for the top two bits below the sign bit is 'peculiar'.
    18932016            if (relative >= 0)
     
    19072030            instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1);
    19082031        } else {
     2032            const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip;
    19092033            ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) + 1));
    19102034            ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) >> 16));
     
    19212045        return op | (imm.m_value.i << 10) | imm.m_value.imm4;
    19222046    }
     2047
    19232048    static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm)
    19242049    {
     
    20372162
    20382163    Vector<LinkRecord> m_jumpsToLink;
     2164    Vector<int32_t> m_offsets;
    20392165};
    20402166
Note: See TracChangeset for help on using the changeset viewer.