Title: [286592] trunk/Source/_javascript_Core
Revision
286592
Author
[email protected]
Date
2021-12-07 02:01:22 -0800 (Tue, 07 Dec 2021)

Log Message

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

Patch by Zan Dobersek <[email protected]> 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):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (286591 => 286592)


--- trunk/Source/_javascript_Core/ChangeLog	2021-12-07 08:27:13 UTC (rev 286591)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-12-07 10:01:22 UTC (rev 286592)
@@ -1,3 +1,68 @@
+2021-12-07  Zan Dobersek  <[email protected]>
+
+        [RISCV64] Add more MacroAssemblerRISCV64 helper infrastructure
+        https://bugs.webkit.org/show_bug.cgi?id=233805
+
+        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):
+
 2021-12-06  Keith Miller  <[email protected]>
 
         TypeInfo should be materializable from Structures as a single load.

Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerRISCV64.h (286591 => 286592)


--- trunk/Source/_javascript_Core/assembler/MacroAssemblerRISCV64.h	2021-12-07 08:27:13 UTC (rev 286591)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerRISCV64.h	2021-12-07 10:01:22 UTC (rev 286592)
@@ -54,6 +54,60 @@
         return dataTempRegister;
     }
 
+    enum TempRegisterType : int8_t {
+        Data,
+        Memory,
+    };
+
+    template<TempRegisterType... RegisterTypes>
+    struct TempRegister {
+        RegisterID data()
+        {
+            static_assert(((RegisterTypes == Data) || ...));
+            return dataTempRegister;
+        }
+
+        RegisterID memory()
+        {
+            static_assert(((RegisterTypes == Memory) || ...));
+            return memoryTempRegister;
+        }
+    };
+
+    template<TempRegisterType RegisterType>
+    struct LazyTempRegister {
+        LazyTempRegister(bool allowScratchRegister)
+            : m_allowScratchRegister(allowScratchRegister)
+        {
+            static_assert(RegisterType == Data || RegisterType == Memory);
+        }
+
+        operator RegisterID()
+        {
+            RELEASE_ASSERT(m_allowScratchRegister);
+            if constexpr (RegisterType == Data)
+                return dataTempRegister;
+            if constexpr (RegisterType == Memory)
+                return memoryTempRegister;
+            return InvalidGPRReg;
+        }
+
+        bool m_allowScratchRegister;
+    };
+
+    template<TempRegisterType... RegisterTypes>
+    auto temps() -> TempRegister<RegisterTypes...>
+    {
+        RELEASE_ASSERT(m_allowScratchRegister);
+        return { };
+    }
+
+    template<TempRegisterType RegisterType>
+    auto lazyTemp() -> LazyTempRegister<RegisterType>
+    {
+        return { m_allowScratchRegister };
+    }
+
     static bool supportsFloatingPoint() { return true; }
     static bool supportsFloatingPointTruncate() { return true; }
     static bool supportsFloatingPointSqrt() { return true; }
@@ -105,12 +159,310 @@
     static constexpr RegisterID framePointerRegister = RISCV64Registers::fp;
     static constexpr RegisterID linkRegister = RISCV64Registers::ra;
 
-    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(add32);
-    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(add64);
-    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(sub32);
-    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(sub64);
-    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(mul32);
-    MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(mul64);
+    void add32(RegisterID src, RegisterID dest)
+    {
+        add32(src, dest, dest);
+    }
+
+    void add32(RegisterID op1, RegisterID op2, RegisterID dest)
+    {
+        m_assembler.addwInsn(dest, op1, op2);
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void add32(TrustedImm32 imm, RegisterID dest)
+    {
+        add32(imm, dest, dest);
+    }
+
+    void add32(TrustedImm32 imm, RegisterID op2, RegisterID dest)
+    {
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.addiwInsn(dest, op2, Imm::I(imm.m_value));
+            m_assembler.maskRegister<32>(dest);
+            return;
+        }
+
+        auto temp = temps<Data>();
+        move(imm, temp.data());
+        m_assembler.addwInsn(dest, temp.data(), op2);
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void add32(TrustedImm32 imm, AbsoluteAddress address)
+    {
+        auto temp = temps<Data, Memory>();
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.lwInsn(temp.data(), temp.memory(), Imm::I<0>());
+            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
+            m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
+            return;
+        }
+
+        m_assembler.lwInsn(temp.memory(), temp.memory(), Imm::I<0>());
+        move(imm, temp.data());
+        m_assembler.addInsn(temp.data(), temp.memory(), temp.data());
+
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+        m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
+    }
+
+    void add32(TrustedImm32 imm, Address address)
+    {
+        auto temp = temps<Data, Memory>();
+        auto resolution = resolveAddress(address, temp.memory());
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
+            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
+            m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
+            return;
+        }
+
+        m_assembler.lwInsn(temp.memory(), resolution.base, Imm::I(resolution.offset));
+        move(imm, temp.data());
+        m_assembler.addInsn(temp.data(), temp.memory(), temp.data());
+
+        resolution = resolveAddress(address, temp.memory());
+        m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
+    }
+
+    void add32(Address address, RegisterID dest)
+    {
+        auto temp = temps<Data, Memory>();
+        auto resolution = resolveAddress(address, temp.memory());
+        m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
+        m_assembler.addwInsn(dest, temp.data(), dest);
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void add64(RegisterID src, RegisterID dest)
+    {
+        add64(src, dest, dest);
+    }
+
+    void add64(RegisterID op1, RegisterID op2, RegisterID dest)
+    {
+        m_assembler.addInsn(dest, op1, op2);
+    }
+
+    void add64(TrustedImm32 imm, RegisterID dest)
+    {
+        add64(imm, dest, dest);
+    }
+
+    void add64(TrustedImm32 imm, RegisterID op2, RegisterID dest)
+    {
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.addiInsn(dest, op2, Imm::I(imm.m_value));
+            return;
+        }
+
+        auto temp = temps<Data>();
+        move(imm, temp.data());
+        m_assembler.addInsn(dest, temp.data(), op2);
+    }
+
+    void add64(TrustedImm64 imm, RegisterID dest)
+    {
+        add64(imm, dest, dest);
+    }
+
+    void add64(TrustedImm64 imm, RegisterID op2, RegisterID dest)
+    {
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.addiInsn(dest, op2, Imm::I(imm.m_value));
+            return;
+        }
+
+        auto temp = temps<Data>();
+        move(imm, temp.data());
+        m_assembler.addInsn(dest, temp.data(), op2);
+    }
+
+    void add64(TrustedImm32 imm, AbsoluteAddress address)
+    {
+        auto temp = temps<Data, Memory>();
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.ldInsn(temp.data(), temp.memory(), Imm::I<0>());
+            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
+            m_assembler.sdInsn(temp.memory(), temp.data(), Imm::S<0>());
+            return;
+        }
+
+        m_assembler.ldInsn(temp.memory(), temp.memory(), Imm::I<0>());
+        move(imm, temp.data());
+        m_assembler.addInsn(temp.data(), temp.data(), temp.memory());
+
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+        m_assembler.sdInsn(temp.memory(), temp.data(), Imm::S<0>());
+    }
+
+    void add64(TrustedImm32 imm, Address address)
+    {
+        auto temp = temps<Data, Memory>();
+        auto resolution = resolveAddress(address, temp.memory());
+        m_assembler.ldInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
+
+        if (Imm::isValid<Imm::IType>(imm.m_value)) {
+            m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value));
+            m_assembler.sdInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
+            return;
+        }
+
+        move(imm, temp.memory());
+        m_assembler.addInsn(temp.data(), temp.memory(), temp.data());
+
+        resolution = resolveAddress(address, temp.memory());
+        m_assembler.sdInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
+    }
+
+    void add64(AbsoluteAddress address, RegisterID dest)
+    {
+        auto temp = temps<Memory>();
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+        m_assembler.ldInsn(temp.memory(), temp.memory(), Imm::I<0>());
+        m_assembler.addInsn(dest, temp.memory(), dest);
+    }
+
+    void add64(Address address, RegisterID dest)
+    {
+        auto temp = temps<Data, Memory>();
+        auto resolution = resolveAddress(address, temp.memory());
+        m_assembler.ldInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
+        m_assembler.addInsn(dest, temp.data(), dest);
+    }
+
+    void sub32(RegisterID src, RegisterID dest)
+    {
+        sub32(dest, src, dest);
+    }
+
+    void sub32(RegisterID op1, RegisterID op2, RegisterID dest)
+    {
+        m_assembler.subwInsn(dest, op1, op2);
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void sub32(TrustedImm32 imm, RegisterID dest)
+    {
+        sub32(dest, imm, dest);
+    }
+
+    void sub32(RegisterID op1, TrustedImm32 imm, RegisterID dest)
+    {
+        add32(TrustedImm32(-imm.m_value), op1, dest);
+    }
+
+    void sub32(TrustedImm32 imm, AbsoluteAddress address)
+    {
+        auto temp = temps<Data, Memory>();
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+
+        if (Imm::isValid<Imm::IType>(-imm.m_value)) {
+            m_assembler.lwInsn(temp.data(), temp.memory(), Imm::I<0>());
+            m_assembler.addiwInsn(temp.data(), temp.data(), Imm::I(-imm.m_value));
+            m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
+            return;
+        }
+
+        m_assembler.lwInsn(temp.memory(), temp.memory(), Imm::I<0>());
+        move(imm, temp.data());
+        m_assembler.subwInsn(temp.data(), temp.memory(), temp.data());
+
+        move(TrustedImmPtr(address.m_ptr), temp.memory());
+        m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>());
+    }
+
+    void sub32(TrustedImm32 imm, Address address)
+    {
+        auto temp = temps<Data, Memory>();
+        auto resolution = resolveAddress(address, temp.memory());
+        m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
+
+        if (Imm::isValid<Imm::IType>(-imm.m_value)) {
+            m_assembler.addiwInsn(temp.data(), temp.data(), Imm::I(-imm.m_value));
+            m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
+            return;
+        }
+
+        move(imm, temp.memory());
+        m_assembler.subwInsn(temp.data(), temp.data(), temp.memory());
+
+        resolution = resolveAddress(address, temp.memory());
+        m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset));
+    }
+
+    void sub32(Address address, RegisterID dest)
+    {
+        auto temp = temps<Data, Memory>();
+        auto resolution = resolveAddress(address, temp.memory());
+        m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset));
+        m_assembler.subwInsn(dest, dest, temp.data());
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void sub64(RegisterID src, RegisterID dest)
+    {
+        sub64(dest, src, dest);
+    }
+
+    void sub64(RegisterID op1, RegisterID op2, RegisterID dest)
+    {
+        m_assembler.subInsn(dest, op1, op2);
+    }
+
+    void sub64(TrustedImm32 imm, RegisterID dest)
+    {
+        sub64(dest, imm, dest);
+    }
+
+    void sub64(RegisterID op1, TrustedImm32 imm, RegisterID dest)
+    {
+        add64(TrustedImm32(-imm.m_value), op1, dest);
+    }
+
+    void sub64(TrustedImm64 imm, RegisterID dest)
+    {
+        sub64(dest, imm, dest);
+    }
+
+    void sub64(RegisterID op1, TrustedImm64 imm, RegisterID dest)
+    {
+        add64(TrustedImm64(-imm.m_value), op1, dest);
+    }
+
+    void mul32(RegisterID src, RegisterID dest)
+    {
+        mul32(src, dest, dest);
+    }
+
+    void mul32(RegisterID lhs, RegisterID rhs, RegisterID dest)
+    {
+        m_assembler.mulwInsn(dest, lhs, rhs);
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void mul32(TrustedImm32 imm, RegisterID rhs, RegisterID dest)
+    {
+        auto temp = temps<Data>();
+        move(imm, temp.data());
+        m_assembler.mulwInsn(dest, temp.data(), rhs);
+        m_assembler.maskRegister<32>(dest);
+    }
+
+    void mul64(RegisterID src, RegisterID dest)
+    {
+        mul64(src, dest, dest);
+    }
+
+    void mul64(RegisterID lhs, RegisterID rhs, RegisterID dest)
+    {
+        m_assembler.mulInsn(dest, lhs, rhs);
+    }
+
     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(and32);
     MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(and64);
 
@@ -388,6 +740,118 @@
 
     template<PtrTag tag>
     static void linkCall(void*, Call, FunctionPtr<tag>) { }
+
+private:
+    struct Imm {
+        template<typename T>
+        using EnableIfInteger = std::enable_if_t<(std::is_same_v<T, int32_t> || std::is_same_v<T, int64_t>)>;
+
+        template<typename ImmediateType, typename T, typename = EnableIfInteger<T>>
+        static bool isValid(T value) { return ImmediateType::isValid(value); }
+
+        using IType = RISCV64Assembler::IImmediate;
+        template<int32_t value>
+        static IType I() { return IType::v<IType, value>(); }
+        template<typename T, typename = EnableIfInteger<T>>
+        static IType I(T value) { return IType::v<IType>(value); }
+        static IType I(uint32_t value) { return IType(value); }
+
+        using SType = RISCV64Assembler::SImmediate;
+        template<int32_t value>
+        static SType S() { return SType::v<SType, value>(); }
+        template<typename T, typename = EnableIfInteger<T>>
+        static SType S(T value) { return SType::v<SType>(value); }
+
+        using BType = RISCV64Assembler::BImmediate;
+        template<int32_t value>
+        static BType B() { return BType::v<BType, value>(); }
+        template<typename T, typename = EnableIfInteger<T>>
+        static BType B(T value) { return BType::v<BType>(value); }
+        static BType B(uint32_t value) { return BType(value); }
+
+        using UType = RISCV64Assembler::UImmediate;
+        static UType U(uint32_t value) { return UType(value); }
+
+        using JType = RISCV64Assembler::JImmediate;
+        template<int32_t value>
+        static JType J() { return JType::v<JType, value>(); }
+    };
+
+    struct AddressResolution {
+        RegisterID base;
+        int32_t offset;
+    };
+
+    template<typename RegisterType>
+    AddressResolution resolveAddress(BaseIndex address, RegisterType destination)
+    {
+        if (!!address.offset) {
+            if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset)) {
+                if (address.scale != TimesOne) {
+                    m_assembler.slliInsn(destination, address.index, uint32_t(address.scale));
+                    m_assembler.addInsn(destination, address.base, destination);
+                } else
+                    m_assembler.addInsn(destination, address.base, address.index);
+                return { destination, address.offset };
+            }
+
+            if (address.scale != TimesOne) {
+                uint32_t scale = address.scale;
+                int32_t upperOffset = address.offset >> scale;
+                int32_t lowerOffset = address.offset & ((1 << scale) - 1);
+
+                if (!RISCV64Assembler::ImmediateBase<12>::isValid(upperOffset)) {
+                    RISCV64Assembler::ImmediateLoader imml(upperOffset);
+                    imml.moveInto(m_assembler, destination);
+                    m_assembler.addInsn(destination, address.index, destination);
+                } else
+                    m_assembler.addiInsn(destination, address.index, Imm::I(upperOffset));
+                m_assembler.slliInsn(destination, destination, scale);
+                m_assembler.oriInsn(destination, destination, Imm::I(lowerOffset));
+            } else {
+                RISCV64Assembler::ImmediateLoader imml(address.offset);
+                imml.moveInto(m_assembler, destination);
+                m_assembler.addInsn(destination, destination, address.index);
+            }
+            m_assembler.addInsn(destination, address.base, destination);
+            return { destination, 0 };
+        }
+
+        if (address.scale != TimesOne) {
+            m_assembler.slliInsn(destination, address.index, address.scale);
+            m_assembler.addInsn(destination, address.base, destination);
+        } else
+            m_assembler.addInsn(destination, address.base, address.index);
+        return { destination, 0 };
+    }
+
+    template<typename RegisterType>
+    AddressResolution resolveAddress(Address address, RegisterType destination)
+    {
+        if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset))
+            return { address.base, address.offset };
+
+        uint32_t value = *reinterpret_cast<uint32_t*>(&address.offset);
+        if (value & (1 << 11))
+            value += (1 << 12);
+
+        m_assembler.luiInsn(destination, Imm::U(value));
+        m_assembler.addiInsn(destination, destination, Imm::I(value & ((1 << 12) - 1)));
+        m_assembler.addInsn(destination, address.base, destination);
+        return { destination, 0 };
+    }
+
+    template<typename RegisterType>
+    AddressResolution resolveAddress(ExtendedAddress address, RegisterType destination)
+    {
+        if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset))
+            return { address.base, int32_t(address.offset) };
+
+        RISCV64Assembler::ImmediateLoader imml(int64_t(address.offset));
+        imml.moveInto(m_assembler, destination);
+        m_assembler.addInsn(destination, address.base, destination);
+        return { destination, 0 };
+    }
 };
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/assembler/RISCV64Assembler.h (286591 => 286592)


--- trunk/Source/_javascript_Core/assembler/RISCV64Assembler.h	2021-12-07 08:27:13 UTC (rev 286591)
+++ trunk/Source/_javascript_Core/assembler/RISCV64Assembler.h	2021-12-07 10:01:22 UTC (rev 286592)
@@ -1987,6 +1987,107 @@
         srliInsn<64 - bitSize>(rd, rd);
     }
 
+    struct ImmediateLoader {
+        enum PlaceholderTag { Placeholder };
+
+        ImmediateLoader(int32_t imm)
+            : ImmediateLoader(int64_t(imm))
+        { }
+
+        ImmediateLoader(PlaceholderTag, int32_t imm)
+            : ImmediateLoader(Placeholder, int64_t(imm))
+        { }
+
+        ImmediateLoader(int64_t imm)
+        {
+            // If the immediate value fits into the IImmediate mold, we can short-cut to just generating that through a single ADDI.
+            if (IImmediate::isValid(imm)) {
+                m_ops[m_opCount++] = { Op::Type::IImmediate, IImmediate::v<IImmediate>(imm).imm };
+                return;
+            }
+
+            // The immediate is larger than 12 bits, so it has to be loaded through the initial LUI and then additional shift-and-addi pairs.
+            // This sequence is generated in reverse. moveInto() or other users traverse the sequence accordingly.
+            int64_t value = imm;
+
+            while (true) {
+                uint32_t addiImm = value & ((1 << 12) - 1);
+                // The addi will be sign-extending the 12-bit value and adding it to the register-contained value. If the addi-immediate
+                // is negative, the remaining immediate has to be increased by 2^12 to offset the subsequent subtraction.
+                if (addiImm & (1 << 11))
+                    value += (1 << 12);
+                m_ops[m_opCount++] = { Op::Type::ADDI, addiImm };
+
+                // Shift out the bits incorporated into the just-added addi.
+                value = value >> 12;
+
+                // If the remainder of the immediate can fit into a 20-bit immediate, we can generate the LUI instruction that will end up
+                // loading the initial higher bits of the desired immediate.
+                if (ImmediateBase<20>::isValid(value)) {
+                    m_ops[m_opCount++] = { Op::Type::LUI, uint32_t((value & ((1 << 20) - 1)) << 12) };
+                    return;
+                }
+
+                // Otherwise, generate the lshift operation that will make room for lower parts of the immediate value.
+                m_ops[m_opCount++] = { Op::Type::LSHIFT12, 0 };
+            }
+        }
+
+        ImmediateLoader(PlaceholderTag, int64_t imm)
+            : ImmediateLoader(imm)
+        {
+            // The non-placeholder constructor already generated the necessary operations to load this immediate.
+            // This constructor still fills out the remaining potential operations as nops. This enables future patching
+            // of these instructions with other immediate-load sequences.
+
+            for (unsigned i = m_opCount; i < m_ops.size(); ++i)
+                m_ops[i] = { Op::Type::NOP, 0 };
+            m_opCount = m_ops.size();
+        }
+
+        void moveInto(RISCV64Assembler& assembler, RegisterID dest)
+        {
+            // This is a helper method that generates the necessary instructions through the RISCV64Assembler infrastructure.
+            // Operations are traversed in reverse in order to match the generation process.
+
+            for (unsigned i = 0; i < m_opCount; ++i) {
+                auto& op = m_ops[m_opCount - (i + 1)];
+                switch (op.type) {
+                case Op::Type::IImmediate:
+                    assembler.addiInsn(dest, RISCV64Registers::zero, IImmediate(op.value));
+                    break;
+                case Op::Type::LUI:
+                    assembler.luiInsn(dest, UImmediate(op.value));
+                    break;
+                case Op::Type::ADDI:
+                    assembler.addiInsn(dest, dest, IImmediate(op.value));
+                    break;
+                case Op::Type::LSHIFT12:
+                    assembler.slliInsn<12>(dest, dest);
+                    break;
+                case Op::Type::NOP:
+                    assembler.addiInsn(RISCV64Registers::zero, RISCV64Registers::zero, IImmediate::v<IImmediate, 0>());
+                    break;
+                }
+            }
+        }
+
+        struct Op {
+            enum class Type {
+                IImmediate,
+                LUI,
+                ADDI,
+                LSHIFT12,
+                NOP,
+            };
+
+            Type type;
+            uint32_t value;
+        };
+        std::array<Op, 8> m_ops;
+        unsigned m_opCount { 0 };
+    };
+
 protected:
     void insn(uint32_t instruction)
     {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to