MacroAssemblerARM64E.h [plain text]
#pragma once
#if ENABLE(ASSEMBLER) && CPU(ARM64E)
#include "DisallowMacroScratchRegisterUsage.h"
#include "ARM64EAssembler.h"
#include "JSCPtrTag.h"
#include "MacroAssemblerARM64.h"
namespace JSC {
using Assembler = TARGET_ASSEMBLER;
class MacroAssemblerARM64E : public MacroAssemblerARM64 {
public:
static constexpr unsigned numberOfPACBits = 25;
static constexpr uintptr_t nonPACBitsMask = (1ull << (64 - numberOfPACBits)) - 1;
ALWAYS_INLINE void tagReturnAddress()
{
tagPtr(ARM64Registers::sp, ARM64Registers::lr);
}
ALWAYS_INLINE void untagReturnAddress(RegisterID scratch = InvalidGPR)
{
untagPtr(ARM64Registers::sp, ARM64Registers::lr);
validateUntaggedPtr(ARM64Registers::lr, scratch);
}
ALWAYS_INLINE void tagPtr(PtrTag tag, RegisterID target)
{
auto tagGPR = getCachedDataTempRegisterIDAndInvalidate();
move(TrustedImm64(tag), tagGPR);
m_assembler.pacib(target, tagGPR);
}
ALWAYS_INLINE void tagPtr(RegisterID tag, RegisterID target)
{
if (target == ARM64Registers::lr && tag == ARM64Registers::sp) {
m_assembler.pacibsp();
return;
}
m_assembler.pacib(target, tag);
}
ALWAYS_INLINE void untagPtr(PtrTag tag, RegisterID target)
{
auto tagGPR = getCachedDataTempRegisterIDAndInvalidate();
move(TrustedImm64(tag), tagGPR);
m_assembler.autib(target, tagGPR);
}
ALWAYS_INLINE void validateUntaggedPtr(RegisterID target, RegisterID scratch = InvalidGPR)
{
if (scratch == InvalidGPR)
scratch = getCachedDataTempRegisterIDAndInvalidate();
DisallowMacroScratchRegisterUsage disallowScope(*this);
rshift64(target, TrustedImm32(8), scratch);
and64(TrustedImm64(0xff000000000000), scratch, scratch);
or64(target, scratch, scratch);
load8(Address(scratch), scratch);
}
ALWAYS_INLINE void untagPtr(RegisterID tag, RegisterID target)
{
m_assembler.autib(target, tag);
}
ALWAYS_INLINE void removePtrTag(RegisterID target)
{
m_assembler.xpaci(target);
}
ALWAYS_INLINE void tagArrayPtr(RegisterID length, RegisterID target)
{
m_assembler.pacdb(target, length);
}
ALWAYS_INLINE void untagArrayPtr(RegisterID length, RegisterID target)
{
m_assembler.autdb(target, length);
}
ALWAYS_INLINE void untagArrayPtr(Address length, RegisterID target)
{
auto lengthGPR = getCachedDataTempRegisterIDAndInvalidate();
load32(length, lengthGPR);
m_assembler.autdb(target, lengthGPR);
}
ALWAYS_INLINE void removeArrayPtrTag(RegisterID target)
{
m_assembler.xpacd(target);
}
static constexpr RegisterID InvalidGPR = static_cast<RegisterID>(-1);
enum class CallSignatureType {
CFunctionCall,
OtherCall
};
template<CallSignatureType type>
ALWAYS_INLINE Call callTrustedPtr(RegisterID tagGPR = InvalidGPR)
{
ASSERT(tagGPR != dataTempRegister);
AssemblerLabel pointerLabel = m_assembler.label();
moveWithFixedWidth(TrustedImmPtr(nullptr), getCachedDataTempRegisterIDAndInvalidate());
invalidateAllTempRegisters();
if (type == CallSignatureType::CFunctionCall)
m_assembler.blraaz(dataTempRegister);
else
m_assembler.blrab(dataTempRegister, tagGPR);
AssemblerLabel callLabel = m_assembler.label();
ASSERT_UNUSED(pointerLabel, ARM64Assembler::getDifferenceBetweenLabels(callLabel, pointerLabel) == REPATCH_OFFSET_CALL_TO_POINTER);
return Call(callLabel, Call::Linkable);
}
ALWAYS_INLINE Call call(PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::call(tag);
if (tag == CFunctionPtrTag)
return callTrustedPtr<CallSignatureType::CFunctionCall>();
move(TrustedImm64(tag), ARM64Registers::lr);
return callTrustedPtr<CallSignatureType::OtherCall>(ARM64Registers::lr);
}
ALWAYS_INLINE Call call(RegisterID tagGPR)
{
return callTrustedPtr<CallSignatureType::OtherCall>(tagGPR);
}
template<CallSignatureType type>
ALWAYS_INLINE Call callRegister(RegisterID targetGPR, RegisterID tagGPR = InvalidGPR)
{
ASSERT(tagGPR != targetGPR);
invalidateAllTempRegisters();
if (type == CallSignatureType::CFunctionCall)
m_assembler.blraaz(targetGPR);
else
m_assembler.blrab(targetGPR, tagGPR);
return Call(m_assembler.label(), Call::None);
}
ALWAYS_INLINE Call call(RegisterID targetGPR, PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::call(targetGPR, tag);
if (tag == CFunctionPtrTag)
return callRegister<CallSignatureType::CFunctionCall>(targetGPR);
move(TrustedImm64(tag), ARM64Registers::lr);
return callRegister<CallSignatureType::OtherCall>(targetGPR, ARM64Registers::lr);
}
ALWAYS_INLINE Call call(RegisterID targetGPR, RegisterID tagGPR)
{
return callRegister<CallSignatureType::OtherCall>(targetGPR, tagGPR);
}
ALWAYS_INLINE Call call(Address address, PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::call(address, tag);
load64(address, getCachedDataTempRegisterIDAndInvalidate());
return call(dataTempRegister, tag);
}
ALWAYS_INLINE Call call(Address address, RegisterID tag)
{
ASSERT(tag != dataTempRegister);
load64(address, getCachedDataTempRegisterIDAndInvalidate());
return call(dataTempRegister, tag);
}
ALWAYS_INLINE void callOperation(const FunctionPtr<OperationPtrTag> operation)
{
auto tmp = getCachedDataTempRegisterIDAndInvalidate();
move(TrustedImmPtr(operation.executableAddress()), tmp);
call(tmp, OperationPtrTag);
}
ALWAYS_INLINE Jump jump() { return MacroAssemblerARM64::jump(); }
void farJump(RegisterID target, PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::farJump(target, tag);
ASSERT(tag != CFunctionPtrTag);
RegisterID diversityGPR = getCachedDataTempRegisterIDAndInvalidate();
move(TrustedImm64(tag), diversityGPR);
farJump(target, diversityGPR);
}
void farJump(RegisterID target, RegisterID tag)
{
ASSERT(tag != target);
m_assembler.brab(target, tag);
}
void farJump(Address address, PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::farJump(address, tag);
ASSERT(tag != CFunctionPtrTag);
RegisterID targetGPR = getCachedDataTempRegisterIDAndInvalidate();
RegisterID diversityGPR = getCachedMemoryTempRegisterIDAndInvalidate();
load64(address, targetGPR);
move(TrustedImm64(tag), diversityGPR);
m_assembler.brab(targetGPR, diversityGPR);
}
void farJump(Address address, RegisterID tag)
{
RegisterID targetGPR = getCachedDataTempRegisterIDAndInvalidate();
ASSERT(tag != targetGPR);
load64(address, targetGPR);
m_assembler.brab(targetGPR, tag);
}
void farJump(BaseIndex address, PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::farJump(address, tag);
ASSERT(tag != CFunctionPtrTag);
RegisterID targetGPR = getCachedDataTempRegisterIDAndInvalidate();
RegisterID diversityGPR = getCachedMemoryTempRegisterIDAndInvalidate();
load64(address, targetGPR);
move(TrustedImm64(tag), diversityGPR);
m_assembler.brab(targetGPR, diversityGPR);
}
void farJump(BaseIndex address, RegisterID tag)
{
RegisterID targetGPR = getCachedDataTempRegisterIDAndInvalidate();
ASSERT(tag != targetGPR);
load64(address, targetGPR);
m_assembler.brab(targetGPR, tag);
}
void farJump(AbsoluteAddress address, PtrTag tag)
{
if (tag == NoPtrTag)
return MacroAssemblerARM64::farJump(address, tag);
RegisterID targetGPR = getCachedDataTempRegisterIDAndInvalidate();
RegisterID diversityGPR = getCachedMemoryTempRegisterIDAndInvalidate();
move(TrustedImmPtr(address.m_ptr), targetGPR);
load64(Address(targetGPR), targetGPR);
move(TrustedImm64(tag), diversityGPR);
m_assembler.brab(targetGPR, diversityGPR);
}
void farJump(AbsoluteAddress address, RegisterID tag)
{
RegisterID targetGPR = getCachedDataTempRegisterIDAndInvalidate();
ASSERT(tag != targetGPR);
move(TrustedImmPtr(address.m_ptr), targetGPR);
load64(Address(targetGPR), targetGPR);
m_assembler.brab(targetGPR, tag);
}
ALWAYS_INLINE void ret()
{
m_assembler.retab();
}
};
}
#endif // ENABLE(ASSEMBLER) && CPU(ARM64E)