⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 286592 in webkit


Ignore:
Timestamp:
Dec 7, 2021, 2:01:22 AM (5 years ago)
Author:
commit-queue@webkit.org
Message:

[RISCV64] Add more MacroAssemblerRISCV64 helper infrastructure
https://bugs.webkit.org/show_bug.cgi?id=233805

Patch by Zan Dobersek <zdobersek@igalia.com> on 2021-12-07
Reviewed by Yusuke Suzuki.

Introduce RISCV64Assembler::ImmediateLoader, a helper class that
generates the operations necessary for loading any immediate value into
some register. This can be as simple as using ADDI to load 12-bit values
or a combination of LUI, ADDI and possibly additional combinations of
LSHIFT and ADDI instructions. There's also a placeholder mode which
generates no-ops for unused operation slots, in order to enable
future patching and repatching for other immediate values.

MacroAssemblerRISCV64::Imm is introduced as a private helper struct that
groups together validity and construction operations for the different
immediate types implemented in the RISCV64Instructions namespace.

In MacroAssemblerRISCV64, resolveAddress() overloads are provided to
help generate most optimal address loading sequencing. RISC-V addressing
mode utilizes a base register and a 12-bit signed offset. When needed,
additional computation is done on the address object's parameters and
stored in the destination register through which the load can then be
performed.

Helper TempRegister and LazyTempRegister structs are added to the
MacroAssemblerRISCV64 class, along with the respective temps() and
lazyTemp() methods. temps() returns the TempRegister object, with the
template parameters defining which of the two scratch register types
should be allowed for use through this object. Release-time assert
on the m_allowScratchRegister value is done at the point of calling
temps(). lazyTemp() only handles one scratch register, and the assert
is done only when the register is actually used, and not just reserved
for use. This enables simpler implementations that better handle both
modes of scratch register usage (allowed or disallowed).

To get things rolling, the first set of MacroAssemblerRISCV64 methods
is implemented. Addition, subtraction and multiplication definitions
are provided, with the templated no-op declarations removed.

  • assembler/MacroAssemblerRISCV64.h:

(JSC::MacroAssemblerRISCV64::TempRegister::data):
(JSC::MacroAssemblerRISCV64::TempRegister::memory):
(JSC::MacroAssemblerRISCV64::LazyTempRegister::LazyTempRegister):
(JSC::MacroAssemblerRISCV64::LazyTempRegister::operator RegisterID):
(JSC::MacroAssemblerRISCV64::temps):
(JSC::MacroAssemblerRISCV64::lazyTemp):
(JSC::MacroAssemblerRISCV64::add32):
(JSC::MacroAssemblerRISCV64::add64):
(JSC::MacroAssemblerRISCV64::sub32):
(JSC::MacroAssemblerRISCV64::sub64):
(JSC::MacroAssemblerRISCV64::mul32):
(JSC::MacroAssemblerRISCV64::mul64):
(JSC::MacroAssemblerRISCV64::Imm::isValid):
(JSC::MacroAssemblerRISCV64::Imm::I):
(JSC::MacroAssemblerRISCV64::Imm::S):
(JSC::MacroAssemblerRISCV64::Imm::B):
(JSC::MacroAssemblerRISCV64::Imm::U):
(JSC::MacroAssemblerRISCV64::Imm::J):
(JSC::MacroAssemblerRISCV64::resolveAddress):

  • assembler/RISCV64Assembler.h:

(JSC::RISCV64Assembler::ImmediateLoader::ImmediateLoader):
(JSC::RISCV64Assembler::ImmediateLoader::moveInto):

Location:
trunk/Source/JavaScriptCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r286580 r286592  
     12021-12-07  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [RISCV64] Add more MacroAssemblerRISCV64 helper infrastructure
     4        https://bugs.webkit.org/show_bug.cgi?id=233805
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        Introduce RISCV64Assembler::ImmediateLoader, a helper class that
     9        generates the operations necessary for loading any immediate value into
     10        some register. This can be as simple as using ADDI to load 12-bit values
     11        or a combination of LUI, ADDI and possibly additional combinations of
     12        LSHIFT and ADDI instructions. There's also a placeholder mode which
     13        generates no-ops for unused operation slots, in order to enable
     14        future patching and repatching for other immediate values.
     15
     16        MacroAssemblerRISCV64::Imm is introduced as a private helper struct that
     17        groups together validity and construction operations for the different
     18        immediate types implemented in the RISCV64Instructions namespace.
     19
     20        In MacroAssemblerRISCV64, resolveAddress() overloads are provided to
     21        help generate most optimal address loading sequencing. RISC-V addressing
     22        mode utilizes a base register and a 12-bit signed offset. When needed,
     23        additional computation is done on the address object's parameters and
     24        stored in the destination register through which the load can then be
     25        performed.
     26
     27        Helper TempRegister and LazyTempRegister structs are added to the
     28        MacroAssemblerRISCV64 class, along with the respective temps() and
     29        lazyTemp() methods. temps() returns the TempRegister object, with the
     30        template parameters defining which of the two scratch register types
     31        should be allowed for use through this object. Release-time assert
     32        on the m_allowScratchRegister value is done at the point of calling
     33        temps(). lazyTemp() only handles one scratch register, and the assert
     34        is done only when the register is actually used, and not just reserved
     35        for use. This enables simpler implementations that better handle both
     36        modes of scratch register usage (allowed or disallowed).
     37
     38        To get things rolling, the first set of MacroAssemblerRISCV64 methods
     39        is implemented. Addition, subtraction and multiplication definitions
     40        are provided, with the templated no-op declarations removed.
     41
     42        * assembler/MacroAssemblerRISCV64.h:
     43        (JSC::MacroAssemblerRISCV64::TempRegister::data):
     44        (JSC::MacroAssemblerRISCV64::TempRegister::memory):
     45        (JSC::MacroAssemblerRISCV64::LazyTempRegister::LazyTempRegister):
     46        (JSC::MacroAssemblerRISCV64::LazyTempRegister::operator RegisterID):
     47        (JSC::MacroAssemblerRISCV64::temps):
     48        (JSC::MacroAssemblerRISCV64::lazyTemp):
     49        (JSC::MacroAssemblerRISCV64::add32):
     50        (JSC::MacroAssemblerRISCV64::add64):
     51        (JSC::MacroAssemblerRISCV64::sub32):
     52        (JSC::MacroAssemblerRISCV64::sub64):
     53        (JSC::MacroAssemblerRISCV64::mul32):
     54        (JSC::MacroAssemblerRISCV64::mul64):
     55        (JSC::MacroAssemblerRISCV64::Imm::isValid):
     56        (JSC::MacroAssemblerRISCV64::Imm::I):
     57        (JSC::MacroAssemblerRISCV64::Imm::S):
     58        (JSC::MacroAssemblerRISCV64::Imm::B):
     59        (JSC::MacroAssemblerRISCV64::Imm::U):
     60        (JSC::MacroAssemblerRISCV64::Imm::J):
     61        (JSC::MacroAssemblerRISCV64::resolveAddress):
     62        * assembler/RISCV64Assembler.h:
     63        (JSC::RISCV64Assembler::ImmediateLoader::ImmediateLoader):
     64        (JSC::RISCV64Assembler::ImmediateLoader::moveInto):
     65
    1662021-12-06  Keith Miller  <keith_miller@apple.com>
    267
  • trunk/Source/JavaScriptCore/assembler/MacroAssemblerRISCV64.h

    r281757 r286592  
    5555    }
    5656
     57    enum TempRegisterType : int8_t {
     58        Data,
     59        Memory,
     60    };
     61
     62    template<TempRegisterType... RegisterTypes>
     63    struct TempRegister {
     64        RegisterID data()
     65        {
     66            static_assert(((RegisterTypes == Data) || ...));
     67            return dataTempRegister;
     68        }
     69
     70        RegisterID memory()
     71        {
     72            static_assert(((RegisterTypes == Memory) || ...));
     73            return memoryTempRegister;
     74        }
     75    };
     76
     77    template<TempRegisterType RegisterType>
     78    struct LazyTempRegister {
     79        LazyTempRegister(bool allowScratchRegister)
     80            : m_allowScratchRegister(allowScratchRegister)
     81        {
     82            static_assert(RegisterType == Data || RegisterType == Memory);
     83        }
     84
     85        operator RegisterID()
     86        {
     87            RELEASE_ASSERT(m_allowScratchRegister);
     88            if constexpr (RegisterType == Data)
     89                return dataTempRegister;
     90            if constexpr (RegisterType == Memory)
     91                return memoryTempRegister;
     92            return InvalidGPRReg;
     93        }
     94
     95        bool m_allowScratchRegister;
     96    };
     97
     98    template<TempRegisterType... RegisterTypes>
     99    auto temps() -> TempRegister<RegisterTypes...>
     100    {
     101        RELEASE_ASSERT(m_allowScratchRegister);
     102        return { };
     103    }
     104
     105    template<TempRegisterType RegisterType>
     106    auto lazyTemp() -> LazyTempRegister<RegisterType>
     107    {
     108        return { m_allowScratchRegister };
     109    }
     110
    57111    static bool supportsFloatingPoint() { return true; }
    58112    static bool supportsFloatingPointTruncate() { return true; }
     
    106160    static constexpr RegisterID linkRegister = RISCV64Registers::ra;
    107161
    108     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(add32);
    109     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(add64);
    110     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(sub32);
    111     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(sub64);
    112     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(mul32);
    113     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(mul64);
     162    void add32(RegisterID src, RegisterID dest)
     163    {
     164        add32(src, dest, dest);
     165    }
     166
     167    void add32(RegisterID op1, RegisterID op2, RegisterID dest)
     168    {
     169        m_assembler.addwInsn(dest, op1, op2);
     170        m_assembler.maskRegister<32>(dest);
     171    }
     172
     173    void add32(TrustedImm32 imm, RegisterID dest)
     174    {
     175        add32(imm, dest, dest);
     176    }
     177
     178    void add32(TrustedImm32 imm, RegisterID op2, RegisterID dest)
     179    {
     180        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     181            m_assembler.addiwInsn(dest, op2, Imm::I(imm.m_value));
     182            m_assembler.maskRegister<32>(dest);
     183            return;
     184        }
     185
     186        auto temp = temps<Data>();
     187        move(imm, temp.data());
     188        m_assembler.addwInsn(dest, temp.data(), op2);
     189        m_assembler.maskRegister<32>(dest);
     190    }
     191
     192    void add32(TrustedImm32 imm, AbsoluteAddress address)
     193    {
     194        auto temp = temps<Data, Memory>();
     195        move(TrustedImmPtr(address.m_ptr), temp.memory());
     196        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     197            m_assembler.lwInsn(temp.data(), temp.memory(), Imm::I<0>());
     198            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
     199            m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
     200            return;
     201        }
     202
     203        m_assembler.lwInsn(temp.memory(), temp.memory(), Imm::I<0>());
     204        move(imm, temp.data());
     205        m_assembler.addInsn(temp.data(), temp.memory(), temp.data());
     206
     207        move(TrustedImmPtr(address.m_ptr), temp.memory());
     208        m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
     209    }
     210
     211    void add32(TrustedImm32 imm, Address address)
     212    {
     213        auto temp = temps<Data, Memory>();
     214        auto resolution = resolveAddress(address, temp.memory());
     215        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     216            m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
     217            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
     218            m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
     219            return;
     220        }
     221
     222        m_assembler.lwInsn(temp.memory(), resolution.base, Imm::I(resolution.offset));
     223        move(imm, temp.data());
     224        m_assembler.addInsn(temp.data(), temp.memory(), temp.data());
     225
     226        resolution = resolveAddress(address, temp.memory());
     227        m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
     228    }
     229
     230    void add32(Address address, RegisterID dest)
     231    {
     232        auto temp = temps<Data, Memory>();
     233        auto resolution = resolveAddress(address, temp.memory());
     234        m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
     235        m_assembler.addwInsn(dest, temp.data(), dest);
     236        m_assembler.maskRegister<32>(dest);
     237    }
     238
     239    void add64(RegisterID src, RegisterID dest)
     240    {
     241        add64(src, dest, dest);
     242    }
     243
     244    void add64(RegisterID op1, RegisterID op2, RegisterID dest)
     245    {
     246        m_assembler.addInsn(dest, op1, op2);
     247    }
     248
     249    void add64(TrustedImm32 imm, RegisterID dest)
     250    {
     251        add64(imm, dest, dest);
     252    }
     253
     254    void add64(TrustedImm32 imm, RegisterID op2, RegisterID dest)
     255    {
     256        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     257            m_assembler.addiInsn(dest, op2, Imm::I(imm.m_value));
     258            return;
     259        }
     260
     261        auto temp = temps<Data>();
     262        move(imm, temp.data());
     263        m_assembler.addInsn(dest, temp.data(), op2);
     264    }
     265
     266    void add64(TrustedImm64 imm, RegisterID dest)
     267    {
     268        add64(imm, dest, dest);
     269    }
     270
     271    void add64(TrustedImm64 imm, RegisterID op2, RegisterID dest)
     272    {
     273        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     274            m_assembler.addiInsn(dest, op2, Imm::I(imm.m_value));
     275            return;
     276        }
     277
     278        auto temp = temps<Data>();
     279        move(imm, temp.data());
     280        m_assembler.addInsn(dest, temp.data(), op2);
     281    }
     282
     283    void add64(TrustedImm32 imm, AbsoluteAddress address)
     284    {
     285        auto temp = temps<Data, Memory>();
     286        move(TrustedImmPtr(address.m_ptr), temp.memory());
     287
     288        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     289            m_assembler.ldInsn(temp.data(), temp.memory(), Imm::I<0>());
     290            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
     291            m_assembler.sdInsn(temp.memory(), temp.data(), Imm::S<0>());
     292            return;
     293        }
     294
     295        m_assembler.ldInsn(temp.memory(), temp.memory(), Imm::I<0>());
     296        move(imm, temp.data());
     297        m_assembler.addInsn(temp.data(), temp.data(), temp.memory());
     298
     299        move(TrustedImmPtr(address.m_ptr), temp.memory());
     300        m_assembler.sdInsn(temp.memory(), temp.data(), Imm::S<0>());
     301    }
     302
     303    void add64(TrustedImm32 imm, Address address)
     304    {
     305        auto temp = temps<Data, Memory>();
     306        auto resolution = resolveAddress(address, temp.memory());
     307        m_assembler.ldInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
     308
     309        if (Imm::isValid<Imm::IType>(imm.m_value)) {
     310            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
     311            m_assembler.sdInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
     312            return;
     313        }
     314
     315        move(imm, temp.memory());
     316        m_assembler.addInsn(temp.data(), temp.memory(), temp.data());
     317
     318        resolution = resolveAddress(address, temp.memory());
     319        m_assembler.sdInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
     320    }
     321
     322    void add64(AbsoluteAddress address, RegisterID dest)
     323    {
     324        auto temp = temps<Memory>();
     325        move(TrustedImmPtr(address.m_ptr), temp.memory());
     326        m_assembler.ldInsn(temp.memory(), temp.memory(), Imm::I<0>());
     327        m_assembler.addInsn(dest, temp.memory(), dest);
     328    }
     329
     330    void add64(Address address, RegisterID dest)
     331    {
     332        auto temp = temps<Data, Memory>();
     333        auto resolution = resolveAddress(address, temp.memory());
     334        m_assembler.ldInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
     335        m_assembler.addInsn(dest, temp.data(), dest);
     336    }
     337
     338    void sub32(RegisterID src, RegisterID dest)
     339    {
     340        sub32(dest, src, dest);
     341    }
     342
     343    void sub32(RegisterID op1, RegisterID op2, RegisterID dest)
     344    {
     345        m_assembler.subwInsn(dest, op1, op2);
     346        m_assembler.maskRegister<32>(dest);
     347    }
     348
     349    void sub32(TrustedImm32 imm, RegisterID dest)
     350    {
     351        sub32(dest, imm, dest);
     352    }
     353
     354    void sub32(RegisterID op1, TrustedImm32 imm, RegisterID dest)
     355    {
     356        add32(TrustedImm32(-imm.m_value), op1, dest);
     357    }
     358
     359    void sub32(TrustedImm32 imm, AbsoluteAddress address)
     360    {
     361        auto temp = temps<Data, Memory>();
     362        move(TrustedImmPtr(address.m_ptr), temp.memory());
     363
     364        if (Imm::isValid<Imm::IType>(-imm.m_value)) {
     365            m_assembler.lwInsn(temp.data(), temp.memory(), Imm::I<0>());
     366            m_assembler.addiwInsn(temp.data(), temp.data(), Imm::I(-imm.m_value));
     367            m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
     368            return;
     369        }
     370
     371        m_assembler.lwInsn(temp.memory(), temp.memory(), Imm::I<0>());
     372        move(imm, temp.data());
     373        m_assembler.subwInsn(temp.data(), temp.memory(), temp.data());
     374
     375        move(TrustedImmPtr(address.m_ptr), temp.memory());
     376        m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
     377    }
     378
     379    void sub32(TrustedImm32 imm, Address address)
     380    {
     381        auto temp = temps<Data, Memory>();
     382        auto resolution = resolveAddress(address, temp.memory());
     383        m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
     384
     385        if (Imm::isValid<Imm::IType>(-imm.m_value)) {
     386            m_assembler.addiwInsn(temp.data(), temp.data(), Imm::I(-imm.m_value));
     387            m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
     388            return;
     389        }
     390
     391        move(imm, temp.memory());
     392        m_assembler.subwInsn(temp.data(), temp.data(), temp.memory());
     393
     394        resolution = resolveAddress(address, temp.memory());
     395        m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
     396    }
     397
     398    void sub32(Address address, RegisterID dest)
     399    {
     400        auto temp = temps<Data, Memory>();
     401        auto resolution = resolveAddress(address, temp.memory());
     402        m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
     403        m_assembler.subwInsn(dest, dest, temp.data());
     404        m_assembler.maskRegister<32>(dest);
     405    }
     406
     407    void sub64(RegisterID src, RegisterID dest)
     408    {
     409        sub64(dest, src, dest);
     410    }
     411
     412    void sub64(RegisterID op1, RegisterID op2, RegisterID dest)
     413    {
     414        m_assembler.subInsn(dest, op1, op2);
     415    }
     416
     417    void sub64(TrustedImm32 imm, RegisterID dest)
     418    {
     419        sub64(dest, imm, dest);
     420    }
     421
     422    void sub64(RegisterID op1, TrustedImm32 imm, RegisterID dest)
     423    {
     424        add64(TrustedImm32(-imm.m_value), op1, dest);
     425    }
     426
     427    void sub64(TrustedImm64 imm, RegisterID dest)
     428    {
     429        sub64(dest, imm, dest);
     430    }
     431
     432    void sub64(RegisterID op1, TrustedImm64 imm, RegisterID dest)
     433    {
     434        add64(TrustedImm64(-imm.m_value), op1, dest);
     435    }
     436
     437    void mul32(RegisterID src, RegisterID dest)
     438    {
     439        mul32(src, dest, dest);
     440    }
     441
     442    void mul32(RegisterID lhs, RegisterID rhs, RegisterID dest)
     443    {
     444        m_assembler.mulwInsn(dest, lhs, rhs);
     445        m_assembler.maskRegister<32>(dest);
     446    }
     447
     448    void mul32(TrustedImm32 imm, RegisterID rhs, RegisterID dest)
     449    {
     450        auto temp = temps<Data>();
     451        move(imm, temp.data());
     452        m_assembler.mulwInsn(dest, temp.data(), rhs);
     453        m_assembler.maskRegister<32>(dest);
     454    }
     455
     456    void mul64(RegisterID src, RegisterID dest)
     457    {
     458        mul64(src, dest, dest);
     459    }
     460
     461    void mul64(RegisterID lhs, RegisterID rhs, RegisterID dest)
     462    {
     463        m_assembler.mulInsn(dest, lhs, rhs);
     464    }
     465
    114466    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(and32);
    115467    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(and64);
     
    389741    template<PtrTag tag>
    390742    static void linkCall(void*, Call, FunctionPtr<tag>) { }
     743
     744private:
     745    struct Imm {
     746        template<typename T>
     747        using EnableIfInteger = std::enable_if_t<(std::is_same_v<T, int32_t> || std::is_same_v<T, int64_t>)>;
     748
     749        template<typename ImmediateType, typename T, typename = EnableIfInteger<T>>
     750        static bool isValid(T value) { return ImmediateType::isValid(value); }
     751
     752        using IType = RISCV64Assembler::IImmediate;
     753        template<int32_t value>
     754        static IType I() { return IType::v<IType, value>(); }
     755        template<typename T, typename = EnableIfInteger<T>>
     756        static IType I(T value) { return IType::v<IType>(value); }
     757        static IType I(uint32_t value) { return IType(value); }
     758
     759        using SType = RISCV64Assembler::SImmediate;
     760        template<int32_t value>
     761        static SType S() { return SType::v<SType, value>(); }
     762        template<typename T, typename = EnableIfInteger<T>>
     763        static SType S(T value) { return SType::v<SType>(value); }
     764
     765        using BType = RISCV64Assembler::BImmediate;
     766        template<int32_t value>
     767        static BType B() { return BType::v<BType, value>(); }
     768        template<typename T, typename = EnableIfInteger<T>>
     769        static BType B(T value) { return BType::v<BType>(value); }
     770        static BType B(uint32_t value) { return BType(value); }
     771
     772        using UType = RISCV64Assembler::UImmediate;
     773        static UType U(uint32_t value) { return UType(value); }
     774
     775        using JType = RISCV64Assembler::JImmediate;
     776        template<int32_t value>
     777        static JType J() { return JType::v<JType, value>(); }
     778    };
     779
     780    struct AddressResolution {
     781        RegisterID base;
     782        int32_t offset;
     783    };
     784
     785    template<typename RegisterType>
     786    AddressResolution resolveAddress(BaseIndex address, RegisterType destination)
     787    {
     788        if (!!address.offset) {
     789            if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset)) {
     790                if (address.scale != TimesOne) {
     791                    m_assembler.slliInsn(destination, address.index, uint32_t(address.scale));
     792                    m_assembler.addInsn(destination, address.base, destination);
     793                } else
     794                    m_assembler.addInsn(destination, address.base, address.index);
     795                return { destination, address.offset };
     796            }
     797
     798            if (address.scale != TimesOne) {
     799                uint32_t scale = address.scale;
     800                int32_t upperOffset = address.offset >> scale;
     801                int32_t lowerOffset = address.offset & ((1 << scale) - 1);
     802
     803                if (!RISCV64Assembler::ImmediateBase<12>::isValid(upperOffset)) {
     804                    RISCV64Assembler::ImmediateLoader imml(upperOffset);
     805                    imml.moveInto(m_assembler, destination);
     806                    m_assembler.addInsn(destination, address.index, destination);
     807                } else
     808                    m_assembler.addiInsn(destination, address.index, Imm::I(upperOffset));
     809                m_assembler.slliInsn(destination, destination, scale);
     810                m_assembler.oriInsn(destination, destination, Imm::I(lowerOffset));
     811            } else {
     812                RISCV64Assembler::ImmediateLoader imml(address.offset);
     813                imml.moveInto(m_assembler, destination);
     814                m_assembler.addInsn(destination, destination, address.index);
     815            }
     816            m_assembler.addInsn(destination, address.base, destination);
     817            return { destination, 0 };
     818        }
     819
     820        if (address.scale != TimesOne) {
     821            m_assembler.slliInsn(destination, address.index, address.scale);
     822            m_assembler.addInsn(destination, address.base, destination);
     823        } else
     824            m_assembler.addInsn(destination, address.base, address.index);
     825        return { destination, 0 };
     826    }
     827
     828    template<typename RegisterType>
     829    AddressResolution resolveAddress(Address address, RegisterType destination)
     830    {
     831        if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset))
     832            return { address.base, address.offset };
     833
     834        uint32_t value = *reinterpret_cast<uint32_t*>(&address.offset);
     835        if (value & (1 << 11))
     836            value += (1 << 12);
     837
     838        m_assembler.luiInsn(destination, Imm::U(value));
     839        m_assembler.addiInsn(destination, destination, Imm::I(value & ((1 << 12) - 1)));
     840        m_assembler.addInsn(destination, address.base, destination);
     841        return { destination, 0 };
     842    }
     843
     844    template<typename RegisterType>
     845    AddressResolution resolveAddress(ExtendedAddress address, RegisterType destination)
     846    {
     847        if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset))
     848            return { address.base, int32_t(address.offset) };
     849
     850        RISCV64Assembler::ImmediateLoader imml(int64_t(address.offset));
     851        imml.moveInto(m_assembler, destination);
     852        m_assembler.addInsn(destination, address.base, destination);
     853        return { destination, 0 };
     854    }
    391855};
    392856
  • trunk/Source/JavaScriptCore/assembler/RISCV64Assembler.h

    r286212 r286592  
    19881988    }
    19891989
     1990    struct ImmediateLoader {
     1991        enum PlaceholderTag { Placeholder };
     1992
     1993        ImmediateLoader(int32_t imm)
     1994            : ImmediateLoader(int64_t(imm))
     1995        { }
     1996
     1997        ImmediateLoader(PlaceholderTag, int32_t imm)
     1998            : ImmediateLoader(Placeholder, int64_t(imm))
     1999        { }
     2000
     2001        ImmediateLoader(int64_t imm)
     2002        {
     2003            // If the immediate value fits into the IImmediate mold, we can short-cut to just generating that through a single ADDI.
     2004            if (IImmediate::isValid(imm)) {
     2005                m_ops[m_opCount++] = { Op::Type::IImmediate, IImmediate::v<IImmediate>(imm).imm };
     2006                return;
     2007            }
     2008
     2009            // The immediate is larger than 12 bits, so it has to be loaded through the initial LUI and then additional shift-and-addi pairs.
     2010            // This sequence is generated in reverse. moveInto() or other users traverse the sequence accordingly.
     2011            int64_t value = imm;
     2012
     2013            while (true) {
     2014                uint32_t addiImm = value & ((1 << 12) - 1);
     2015                // The addi will be sign-extending the 12-bit value and adding it to the register-contained value. If the addi-immediate
     2016                // is negative, the remaining immediate has to be increased by 2^12 to offset the subsequent subtraction.
     2017                if (addiImm & (1 << 11))
     2018                    value += (1 << 12);
     2019                m_ops[m_opCount++] = { Op::Type::ADDI, addiImm };
     2020
     2021                // Shift out the bits incorporated into the just-added addi.
     2022                value = value >> 12;
     2023
     2024                // If the remainder of the immediate can fit into a 20-bit immediate, we can generate the LUI instruction that will end up
     2025                // loading the initial higher bits of the desired immediate.
     2026                if (ImmediateBase<20>::isValid(value)) {
     2027                    m_ops[m_opCount++] = { Op::Type::LUI, uint32_t((value & ((1 << 20) - 1)) << 12) };
     2028                    return;
     2029                }
     2030
     2031                // Otherwise, generate the lshift operation that will make room for lower parts of the immediate value.
     2032                m_ops[m_opCount++] = { Op::Type::LSHIFT12, 0 };
     2033            }
     2034        }
     2035
     2036        ImmediateLoader(PlaceholderTag, int64_t imm)
     2037            : ImmediateLoader(imm)
     2038        {
     2039            // The non-placeholder constructor already generated the necessary operations to load this immediate.
     2040            // This constructor still fills out the remaining potential operations as nops. This enables future patching
     2041            // of these instructions with other immediate-load sequences.
     2042
     2043            for (unsigned i = m_opCount; i < m_ops.size(); ++i)
     2044                m_ops[i] = { Op::Type::NOP, 0 };
     2045            m_opCount = m_ops.size();
     2046        }
     2047
     2048        void moveInto(RISCV64Assembler& assembler, RegisterID dest)
     2049        {
     2050            // This is a helper method that generates the necessary instructions through the RISCV64Assembler infrastructure.
     2051            // Operations are traversed in reverse in order to match the generation process.
     2052
     2053            for (unsigned i = 0; i < m_opCount; ++i) {
     2054                auto& op = m_ops[m_opCount - (i + 1)];
     2055                switch (op.type) {
     2056                case Op::Type::IImmediate:
     2057                    assembler.addiInsn(dest, RISCV64Registers::zero, IImmediate(op.value));
     2058                    break;
     2059                case Op::Type::LUI:
     2060                    assembler.luiInsn(dest, UImmediate(op.value));
     2061                    break;
     2062                case Op::Type::ADDI:
     2063                    assembler.addiInsn(dest, dest, IImmediate(op.value));
     2064                    break;
     2065                case Op::Type::LSHIFT12:
     2066                    assembler.slliInsn<12>(dest, dest);
     2067                    break;
     2068                case Op::Type::NOP:
     2069                    assembler.addiInsn(RISCV64Registers::zero, RISCV64Registers::zero, IImmediate::v<IImmediate, 0>());
     2070                    break;
     2071                }
     2072            }
     2073        }
     2074
     2075        struct Op {
     2076            enum class Type {
     2077                IImmediate,
     2078                LUI,
     2079                ADDI,
     2080                LSHIFT12,
     2081                NOP,
     2082            };
     2083
     2084            Type type;
     2085            uint32_t value;
     2086        };
     2087        std::array<Op, 8> m_ops;
     2088        unsigned m_opCount { 0 };
     2089    };
     2090
    19902091protected:
    19912092    void insn(uint32_t instruction)
Note: See TracChangeset for help on using the changeset viewer.