| /* |
| * Copyright (C) 2021 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(JIT) |
| |
| #include "AssemblyHelpers.h" |
| |
| namespace JSC { |
| |
| template<typename RegType> |
| struct RegDispatch { |
| static bool hasSameType(Reg); |
| static RegType get(Reg); |
| template<typename Spooler> static RegType temp1(const Spooler*); |
| template<typename Spooler> static RegType temp2(const Spooler*); |
| template<typename Spooler> static RegType& regToStore(Spooler*); |
| static constexpr RegType invalid(); |
| static constexpr size_t regSize(); |
| static bool isValidLoadPairImm(int); |
| static bool isValidStorePairImm(int); |
| }; |
| |
| template<> |
| struct RegDispatch<GPRReg> { |
| static bool hasSameType(Reg reg) { return reg.isGPR(); } |
| static GPRReg get(Reg reg) { return reg.gpr(); } |
| template<typename Spooler> static GPRReg temp1(const Spooler* spooler) { return spooler->m_temp1GPR; } |
| template<typename Spooler> static GPRReg temp2(const Spooler* spooler) { return spooler->m_temp2GPR; } |
| template<typename Spooler> static GPRReg& regToStore(Spooler* spooler) { return spooler->m_gprToStore; } |
| static constexpr GPRReg invalid() { return InvalidGPRReg; } |
| static constexpr size_t regSize() { return sizeof(CPURegister); } |
| #if CPU(ARM64) |
| static bool isValidLoadPairImm(int offset) { return ARM64Assembler::isValidLDPImm<64>(offset); } |
| static bool isValidStorePairImm(int offset) { return ARM64Assembler::isValidSTPImm<64>(offset); } |
| #else |
| static bool isValidLoadPairImm(int) { return false; } |
| static bool isValidStorePairImm(int) { return false; } |
| #endif |
| }; |
| |
| template<> |
| struct RegDispatch<FPRReg> { |
| static bool hasSameType(Reg reg) { return reg.isFPR(); } |
| static FPRReg get(Reg reg) { return reg.fpr(); } |
| template<typename Spooler> static FPRReg temp1(const Spooler* spooler) { return spooler->m_temp1FPR; } |
| template<typename Spooler> static FPRReg temp2(const Spooler* spooler) { return spooler->m_temp2FPR; } |
| template<typename Spooler> static FPRReg& regToStore(Spooler* spooler) { return spooler->m_fprToStore; } |
| static constexpr FPRReg invalid() { return InvalidFPRReg; } |
| static constexpr size_t regSize() { return sizeof(double); } |
| #if CPU(ARM64) |
| static bool isValidLoadPairImm(int offset) { return ARM64Assembler::isValidLDPFPImm<64>(offset); } |
| static bool isValidStorePairImm(int offset) { return ARM64Assembler::isValidSTPFPImm<64>(offset); } |
| #else |
| static bool isValidLoadPairImm(int) { return false; } |
| static bool isValidStorePairImm(int) { return false; } |
| #endif |
| }; |
| |
| template<typename Op> |
| class AssemblyHelpers::Spooler { |
| public: |
| using JIT = AssemblyHelpers; |
| |
| Spooler(JIT& jit, GPRReg baseGPR) |
| : m_jit(jit) |
| , m_baseGPR(baseGPR) |
| { } |
| |
| template<typename RegType> |
| void execute(const RegisterAtOffset& entry) |
| { |
| RELEASE_ASSERT(RegDispatch<RegType>::hasSameType(entry.reg())); |
| if constexpr (!hasPairOp) |
| return op().executeSingle(entry.offset(), RegDispatch<RegType>::get(entry.reg())); |
| |
| if (!m_bufferedEntry.reg().isSet()) { |
| m_bufferedEntry = entry; |
| return; |
| } |
| |
| constexpr ptrdiff_t regSize = RegDispatch<RegType>::regSize(); |
| RegType bufferedEntryReg = RegDispatch<RegType>::get(m_bufferedEntry.reg()); |
| RegType entryReg = RegDispatch<RegType>::get(entry.reg()); |
| |
| if (entry.offset() == m_bufferedEntry.offset() + regSize) { |
| op().executePair(m_bufferedEntry.offset(), bufferedEntryReg, entryReg); |
| m_bufferedEntry = { }; |
| return; |
| } |
| if (m_bufferedEntry.offset() == entry.offset() + regSize) { |
| op().executePair(entry.offset(), entryReg, bufferedEntryReg); |
| m_bufferedEntry = { }; |
| return; |
| } |
| |
| // We don't have a pair of operations that we can execute as a pair. |
| // Execute the previous one as a single (finalize will do that), and then |
| // buffer the current entry to potentially be paired with the next entry. |
| finalize<RegType>(); |
| execute<RegType>(entry); |
| } |
| |
| template<typename RegType> |
| void finalize() |
| { |
| if constexpr (hasPairOp) { |
| if (m_bufferedEntry.reg().isSet()) { |
| op().executeSingle(m_bufferedEntry.offset(), RegDispatch<RegType>::get(m_bufferedEntry.reg())); |
| m_bufferedEntry = { }; |
| } |
| } |
| } |
| |
| private: |
| #if CPU(ARM64) |
| static constexpr bool hasPairOp = true; |
| #else |
| static constexpr bool hasPairOp = false; |
| #endif |
| |
| Op& op() { return *reinterpret_cast<Op*>(this); } |
| |
| protected: |
| JIT& m_jit; |
| GPRReg m_baseGPR; |
| RegisterAtOffset m_bufferedEntry; |
| }; |
| |
| class AssemblyHelpers::LoadRegSpooler : public AssemblyHelpers::Spooler<LoadRegSpooler> { |
| using Base = Spooler<LoadRegSpooler>; |
| using JIT = Base::JIT; |
| public: |
| LoadRegSpooler(JIT& jit, GPRReg baseGPR) |
| : Base(jit, baseGPR) |
| { } |
| |
| ALWAYS_INLINE void loadGPR(const RegisterAtOffset& entry) { execute<GPRReg>(entry); } |
| ALWAYS_INLINE void finalizeGPR() { finalize<GPRReg>(); } |
| ALWAYS_INLINE void loadFPR(const RegisterAtOffset& entry) { execute<FPRReg>(entry); } |
| ALWAYS_INLINE void finalizeFPR() { finalize<FPRReg>(); } |
| |
| private: |
| #if CPU(ARM64) |
| template<typename RegType> |
| ALWAYS_INLINE void executePair(ptrdiff_t offset, RegType reg1, RegType reg2) |
| { |
| m_jit.loadPair64(m_baseGPR, TrustedImm32(offset), reg1, reg2); |
| } |
| #else |
| template<typename RegType> |
| ALWAYS_INLINE void executePair(ptrdiff_t, RegType, RegType) { } |
| #endif |
| |
| ALWAYS_INLINE void executeSingle(ptrdiff_t offset, GPRReg reg) |
| { |
| #if USE(JSVALUE64) |
| m_jit.load64(Address(m_baseGPR, offset), reg); |
| #else |
| m_jit.load32(Address(m_baseGPR, offset), reg); |
| #endif |
| } |
| |
| ALWAYS_INLINE void executeSingle(ptrdiff_t offset, FPRReg reg) |
| { |
| m_jit.loadDouble(Address(m_baseGPR, offset), reg); |
| } |
| |
| friend class AssemblyHelpers::Spooler<LoadRegSpooler>; |
| }; |
| |
| class AssemblyHelpers::StoreRegSpooler : public AssemblyHelpers::Spooler<StoreRegSpooler> { |
| using Base = Spooler<StoreRegSpooler>; |
| using JIT = typename Base::JIT; |
| public: |
| StoreRegSpooler(JIT& jit, GPRReg baseGPR) |
| : Base(jit, baseGPR) |
| { } |
| |
| ALWAYS_INLINE void storeGPR(const RegisterAtOffset& entry) { execute<GPRReg>(entry); } |
| ALWAYS_INLINE void finalizeGPR() { finalize<GPRReg>(); } |
| ALWAYS_INLINE void storeFPR(const RegisterAtOffset& entry) { execute<FPRReg>(entry); } |
| ALWAYS_INLINE void finalizeFPR() { finalize<FPRReg>(); } |
| |
| private: |
| #if CPU(ARM64) |
| template<typename RegType> |
| ALWAYS_INLINE void executePair(ptrdiff_t offset, RegType reg1, RegType reg2) |
| { |
| m_jit.storePair64(reg1, reg2, m_baseGPR, TrustedImm32(offset)); |
| } |
| #else |
| template<typename RegType> |
| ALWAYS_INLINE void executePair(ptrdiff_t, RegType, RegType) { } |
| #endif |
| |
| ALWAYS_INLINE void executeSingle(ptrdiff_t offset, GPRReg reg) |
| { |
| #if USE(JSVALUE64) |
| m_jit.store64(reg, Address(m_baseGPR, offset)); |
| #else |
| m_jit.store32(reg, Address(m_baseGPR, offset)); |
| #endif |
| } |
| |
| ALWAYS_INLINE void executeSingle(ptrdiff_t offset, FPRReg reg) |
| { |
| m_jit.storeDouble(reg, Address(m_baseGPR, offset)); |
| } |
| |
| friend class AssemblyHelpers::Spooler<StoreRegSpooler>; |
| }; |
| |
| class AssemblyHelpers::CopySpooler { |
| public: |
| using JIT = AssemblyHelpers; |
| using Address = JIT::Address; |
| using TrustedImm32 = JIT::TrustedImm32; |
| |
| struct Source { |
| enum class Type { BufferOffset, Reg, EncodedJSValue } type; |
| int offset; |
| Reg reg; |
| EncodedJSValue value; |
| |
| template<typename RegType> RegType getReg() { return RegDispatch<RegType>::get(reg); }; |
| }; |
| |
| enum class BufferRegs { |
| NeedPreservation, |
| AllowModification |
| }; |
| |
| CopySpooler(BufferRegs attribute, JIT& jit, GPRReg srcBuffer, GPRReg destBuffer, GPRReg temp1, GPRReg temp2, FPRReg fpTemp1 = InvalidFPRReg, FPRReg fpTemp2 = InvalidFPRReg) |
| : m_jit(jit) |
| , m_srcBufferGPR(srcBuffer) |
| , m_dstBufferGPR(destBuffer) |
| , m_temp1GPR(temp1) |
| , m_temp2GPR(temp2) |
| , m_temp1FPR(fpTemp1) |
| , m_temp2FPR(fpTemp2) |
| , m_bufferRegsAttr(attribute) |
| { |
| if constexpr (hasPairOp && !isARM64()) |
| RELEASE_ASSERT_NOT_REACHED(); // unsupported architecture. |
| } |
| |
| CopySpooler(JIT& jit, GPRReg srcBuffer, GPRReg destBuffer, GPRReg temp1, GPRReg temp2, FPRReg fpTemp1 = InvalidFPRReg, FPRReg fpTemp2 = InvalidFPRReg) |
| : CopySpooler(BufferRegs::NeedPreservation, jit, srcBuffer, destBuffer, temp1, temp2, fpTemp1, fpTemp2) |
| { } |
| |
| private: |
| template<typename RegType> RegType temp1() const { return RegDispatch<RegType>::temp1(this); } |
| template<typename RegType> RegType temp2() const { return RegDispatch<RegType>::temp2(this); } |
| template<typename RegType> RegType& regToStore() { return RegDispatch<RegType>::regToStore(this); } |
| |
| template<typename RegType> static constexpr RegType invalid() { return RegDispatch<RegType>::invalid(); } |
| template<typename RegType> static constexpr int regSize() { return RegDispatch<RegType>::regSize(); } |
| |
| template<typename RegType> static bool isValidLoadPairImm(int offset) { return RegDispatch<RegType>::isValidLoadPairImm(offset); } |
| template<typename RegType> static bool isValidStorePairImm(int offset) { return RegDispatch<RegType>::isValidStorePairImm(offset); } |
| |
| template<typename RegType> |
| void load(int offset) |
| { |
| if constexpr (!hasPairOp) { |
| auto& regToStore = this->regToStore<RegType>(); |
| regToStore = temp1<RegType>(); |
| load(offset, regToStore); |
| return; |
| } |
| |
| auto& source = m_sources[m_currentSource++]; |
| source.type = Source::Type::BufferOffset; |
| source.offset = offset; |
| } |
| |
| void move(EncodedJSValue value) |
| { |
| if constexpr (!hasPairOp) { |
| auto& regToStore = this->regToStore<GPRReg>(); |
| regToStore = temp1<GPRReg>(); |
| move(value, regToStore); |
| return; |
| } |
| |
| auto& source = m_sources[m_currentSource++]; |
| source.type = Source::Type::EncodedJSValue; |
| source.value = value; |
| } |
| |
| template<typename RegType> |
| void copy(RegType reg) |
| { |
| if constexpr (!hasPairOp) { |
| auto& regToStore = this->regToStore<RegType>(); |
| regToStore = reg; |
| return; |
| } |
| |
| auto& source = m_sources[m_currentSource++]; |
| source.type = Source::Type::Reg; |
| source.reg = reg; |
| } |
| |
| template<typename RegType> |
| void store(int storeOffset) |
| { |
| if constexpr (!hasPairOp) { |
| auto regToStore = this->regToStore<RegType>(); |
| store(regToStore, storeOffset); |
| return; |
| } |
| |
| constexpr bool regTypeIsGPR = std::is_same<RegType, GPRReg>::value; |
| |
| if (m_currentSource < 2) { |
| m_deferredStoreOffset = storeOffset; |
| return; |
| } |
| |
| RegType regToStore1 = invalid<RegType>(); |
| RegType regToStore2 = invalid<RegType>(); |
| auto& source1 = m_sources[0]; |
| auto& source2 = m_sources[1]; |
| auto srcOffset1 = m_sources[0].offset - m_srcOffsetAdjustment; |
| auto srcOffset2 = m_sources[1].offset - m_srcOffsetAdjustment; |
| constexpr int registerSize = regSize<RegType>(); |
| |
| if (source1.type == Source::Type::BufferOffset && source2.type == Source::Type::BufferOffset) { |
| regToStore1 = temp1<RegType>(); |
| regToStore2 = temp2<RegType>(); |
| |
| int offsetDelta = abs(srcOffset1 - srcOffset2); |
| int minOffset = std::min(srcOffset1, srcOffset2); |
| bool isValidOffset = isValidLoadPairImm<RegType>(minOffset); |
| |
| if (offsetDelta != registerSize || (!isValidOffset && m_bufferRegsAttr != BufferRegs::AllowModification)) { |
| load(srcOffset1, regToStore1); |
| load(srcOffset2, regToStore2); |
| } else { |
| if (!isValidOffset) { |
| ASSERT(m_bufferRegsAttr == BufferRegs::AllowModification); |
| m_srcOffsetAdjustment += minOffset; |
| m_jit.addPtr(TrustedImm32(minOffset), m_srcBufferGPR); |
| |
| srcOffset1 -= minOffset; |
| srcOffset2 -= minOffset; |
| ASSERT(isValidLoadPairImm<RegType>(std::min(srcOffset1, srcOffset2))); |
| } |
| if (srcOffset1 < srcOffset2) |
| loadPair(srcOffset1, regToStore1, regToStore2); |
| else |
| loadPair(srcOffset2, regToStore2, regToStore1); |
| } |
| } else if (source1.type == Source::Type::BufferOffset) { |
| regToStore1 = temp1<RegType>(); |
| load(srcOffset1, regToStore1); |
| if (source2.type == Source::Type::EncodedJSValue) { |
| if constexpr (regTypeIsGPR) { |
| regToStore2 = temp2<RegType>(); |
| move(source2.value, regToStore2); |
| } else |
| RELEASE_ASSERT_NOT_REACHED(); |
| } else |
| regToStore2 = source2.getReg<RegType>(); |
| |
| } else if (source2.type == Source::Type::BufferOffset) { |
| if (source1.type == Source::Type::EncodedJSValue) { |
| if constexpr (regTypeIsGPR) { |
| regToStore1 = temp1<RegType>(); |
| move(source1.value, regToStore1); |
| } else |
| RELEASE_ASSERT_NOT_REACHED(); |
| } else |
| regToStore1 = source1.getReg<RegType>(); |
| regToStore2 = temp2<RegType>(); |
| load(srcOffset2, regToStore2); |
| |
| } else { |
| if (source1.type == Source::Type::EncodedJSValue) { |
| if constexpr (regTypeIsGPR) { |
| regToStore1 = temp1<RegType>(); |
| move(source1.value, regToStore1); |
| } else |
| RELEASE_ASSERT_NOT_REACHED(); |
| } else |
| regToStore1 = source1.getReg<RegType>(); |
| |
| if (source2.type == Source::Type::EncodedJSValue) { |
| if constexpr (regTypeIsGPR) { |
| regToStore2 = temp2<RegType>(); |
| move(source2.value, regToStore2); |
| } else |
| RELEASE_ASSERT_NOT_REACHED(); |
| } else |
| regToStore2 = source2.getReg<RegType>(); |
| } |
| |
| int dstOffset1 = m_deferredStoreOffset - m_dstOffsetAdjustment; |
| int dstOffset2 = storeOffset - m_dstOffsetAdjustment; |
| |
| int offsetDelta = abs(dstOffset1 - dstOffset2); |
| int minOffset = std::min(dstOffset1, dstOffset2); |
| bool isValidOffset = isValidStorePairImm<RegType>(minOffset); |
| |
| if (offsetDelta != registerSize || (!isValidOffset && m_bufferRegsAttr != BufferRegs::AllowModification)) { |
| store(regToStore1, dstOffset1); |
| store(regToStore2, dstOffset2); |
| } else { |
| if (!isValidOffset) { |
| ASSERT(m_bufferRegsAttr == BufferRegs::AllowModification); |
| m_dstOffsetAdjustment += minOffset; |
| m_jit.addPtr(TrustedImm32(minOffset), m_dstBufferGPR); |
| |
| dstOffset1 -= minOffset; |
| dstOffset2 -= minOffset; |
| ASSERT(isValidStorePairImm<RegType>(std::min(dstOffset1, dstOffset2))); |
| } |
| if (dstOffset1 < dstOffset2) |
| storePair(regToStore1, regToStore2, dstOffset1); |
| else |
| storePair(regToStore2, regToStore1, dstOffset2); |
| } |
| |
| m_currentSource = 0; |
| } |
| |
| template<typename RegType> |
| void finalize() |
| { |
| if constexpr (!hasPairOp) |
| return; |
| |
| if (!m_currentSource) |
| return; // Nothing to finalize. |
| |
| ASSERT(m_currentSource == 1); |
| |
| RegType regToStore = invalid<RegType>(); |
| auto& source = m_sources[0]; |
| auto& srcOffset = source.offset; |
| constexpr bool regTypeIsGPR = std::is_same<RegType, GPRReg>::value; |
| |
| if (source.type == Source::Type::BufferOffset) { |
| regToStore = temp1<RegType>(); |
| load(srcOffset - m_srcOffsetAdjustment, regToStore); |
| } else if (source.type == Source::Type::Reg) |
| regToStore = source.getReg<RegType>(); |
| else if constexpr (regTypeIsGPR) { |
| regToStore = temp1<RegType>(); |
| move(source.value, regToStore); |
| } else |
| RELEASE_ASSERT_NOT_REACHED(); |
| |
| store(regToStore, m_deferredStoreOffset - m_dstOffsetAdjustment); |
| m_currentSource = 0; |
| } |
| |
| public: |
| ALWAYS_INLINE void loadGPR(int srcOffset) { load<GPRReg>(srcOffset); } |
| ALWAYS_INLINE void copyGPR(GPRReg gpr) { copy<GPRReg>(gpr); } |
| ALWAYS_INLINE void moveConstant(EncodedJSValue value) { move(value); } |
| ALWAYS_INLINE void storeGPR(int dstOffset) { store<GPRReg>(dstOffset); } |
| ALWAYS_INLINE void finalizeGPR() { finalize<GPRReg>(); } |
| |
| ALWAYS_INLINE void loadFPR(int srcOffset) { load<FPRReg>(srcOffset); } |
| ALWAYS_INLINE void copyFPR(FPRReg gpr) { copy<FPRReg>(gpr); } |
| ALWAYS_INLINE void storeFPR(int dstOffset) { store<FPRReg>(dstOffset); } |
| ALWAYS_INLINE void finalizeFPR() { finalize<FPRReg>(); } |
| |
| protected: |
| #if USE(JSVALUE64) |
| ALWAYS_INLINE void move(EncodedJSValue value, GPRReg dest) |
| { |
| m_jit.move(TrustedImm64(value), dest); |
| } |
| #else |
| NO_RETURN_DUE_TO_CRASH void move(EncodedJSValue, GPRReg) { RELEASE_ASSERT_NOT_REACHED(); } |
| #endif |
| |
| ALWAYS_INLINE void load(int offset, GPRReg dest) |
| { |
| m_jit.loadPtr(Address(m_srcBufferGPR, offset), dest); |
| } |
| |
| ALWAYS_INLINE void store(GPRReg src, int offset) |
| { |
| m_jit.storePtr(src, Address(m_dstBufferGPR, offset)); |
| } |
| |
| ALWAYS_INLINE void load(int offset, FPRReg dest) |
| { |
| m_jit.loadDouble(Address(m_srcBufferGPR, offset), dest); |
| } |
| |
| ALWAYS_INLINE void store(FPRReg src, int offset) |
| { |
| m_jit.storeDouble(src, Address(m_dstBufferGPR, offset)); |
| } |
| |
| #if CPU(ARM64) |
| template<typename RegType> |
| ALWAYS_INLINE void loadPair(int offset, RegType dest1, RegType dest2) |
| { |
| m_jit.loadPair64(m_srcBufferGPR, TrustedImm32(offset), dest1, dest2); |
| } |
| |
| template<typename RegType> |
| ALWAYS_INLINE void storePair(RegType src1, RegType src2, int offset) |
| { |
| m_jit.storePair64(src1, src2, m_dstBufferGPR, TrustedImm32(offset)); |
| } |
| |
| static constexpr bool hasPairOp = true; |
| #else |
| template<typename RegType> ALWAYS_INLINE void loadPair(int, RegType, RegType) { } |
| template<typename RegType> ALWAYS_INLINE void storePair(RegType, RegType, int) { } |
| |
| static constexpr bool hasPairOp = false; |
| #endif |
| |
| JIT& m_jit; |
| |
| GPRReg m_srcBufferGPR; |
| GPRReg m_dstBufferGPR; |
| GPRReg m_temp1GPR; |
| GPRReg m_temp2GPR; |
| FPRReg m_temp1FPR; |
| FPRReg m_temp2FPR; |
| |
| private: |
| static constexpr int gprSize = static_cast<int>(sizeof(CPURegister)); |
| static constexpr int fprSize = static_cast<int>(sizeof(double)); |
| |
| // These point to which register to use. |
| GPRReg m_gprToStore { InvalidGPRReg }; // Only used when !hasPairOp. |
| FPRReg m_fprToStore { InvalidFPRReg }; // Only used when !hasPairOp. |
| |
| BufferRegs m_bufferRegsAttr; |
| Source m_sources[2]; |
| unsigned m_currentSource { 0 }; |
| int m_srcOffsetAdjustment { 0 }; |
| int m_dstOffsetAdjustment { 0 }; |
| int m_deferredStoreOffset; |
| |
| template<typename RegType> friend struct RegDispatch; |
| }; |
| |
| } // namespace JSC |
| |
| #endif // ENABLE(JIT) |