mirror of
https://github.com/Ryubing/Ryujinx.git
synced 2025-11-28 18:02:22 -05:00
678 lines
24 KiB
C#
678 lines
24 KiB
C#
using ARMeilleure.Common;
|
|
using Ryujinx.Cpu.LightningJit.CodeGen;
|
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|
{
|
|
static class InstEmitSystem
|
|
{
|
|
private delegate void SoftwareInterruptHandler(ulong address, int imm);
|
|
private delegate ulong Get64();
|
|
private delegate bool GetBool();
|
|
|
|
public static void RewriteInstruction(
|
|
CodeWriter writer,
|
|
RegisterAllocator regAlloc,
|
|
TailMerger tailMerger,
|
|
InstName name,
|
|
ulong pc,
|
|
uint encoding,
|
|
int spillBaseOffset)
|
|
{
|
|
if (name == InstName.Brk)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
WriteCall(ref asm, regAlloc, GetBrkHandlerPtr(), spillBaseOffset, null, pc, encoding);
|
|
WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, spillBaseOffset);
|
|
}
|
|
else if (name == InstName.Svc)
|
|
{
|
|
uint svcId = (ushort)(encoding >> 5);
|
|
|
|
Assembler asm = new(writer);
|
|
|
|
WriteCall(ref asm, regAlloc, GetSvcHandlerPtr(), spillBaseOffset, null, pc, svcId);
|
|
WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, spillBaseOffset);
|
|
}
|
|
else if (name == InstName.UdfPermUndef)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
WriteCall(ref asm, regAlloc, GetUdfHandlerPtr(), spillBaseOffset, null, pc, encoding);
|
|
WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, spillBaseOffset);
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd53bd060) // mrs x0, tpidrro_el0
|
|
{
|
|
uint rd = encoding & 0x1f;
|
|
|
|
if (rd != RegisterUtils.ZrIndex)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
asm.LdrRiUn(Register((int)rd), Register(regAlloc.FixedContextRegister), NativeContextOffsets.TpidrroEl0Offset);
|
|
}
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd53bd040) // mrs x0, tpidr_el0
|
|
{
|
|
uint rd = encoding & 0x1f;
|
|
|
|
if (rd != RegisterUtils.ZrIndex)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
asm.LdrRiUn(Register((int)rd), Register(regAlloc.FixedContextRegister), NativeContextOffsets.TpidrEl0Offset);
|
|
}
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd53b0020 && IsCtrEl0AccessForbidden()) // mrs x0, ctr_el0
|
|
{
|
|
uint rd = encoding & 0x1f;
|
|
|
|
if (rd != RegisterUtils.ZrIndex)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
// TODO: Use host value? But that register can't be accessed on macOS...
|
|
asm.Mov(Register((int)rd, OperandType.I32), 0x8444c004);
|
|
}
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd53be020) // mrs x0, cntpct_el0
|
|
{
|
|
uint rd = encoding & 0x1f;
|
|
|
|
if (rd != RegisterUtils.ZrIndex)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
WriteCall(ref asm, regAlloc, GetCntpctEl0Ptr(), spillBaseOffset, (int)rd);
|
|
}
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd51bd040) // msr tpidr_el0, x0
|
|
{
|
|
uint rd = encoding & 0x1f;
|
|
|
|
if (rd != RegisterUtils.ZrIndex)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
asm.StrRiUn(Register((int)rd), Register(regAlloc.FixedContextRegister), NativeContextOffsets.TpidrEl0Offset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.WriteInstruction(encoding);
|
|
}
|
|
}
|
|
|
|
public static bool NeedsCall(uint encoding)
|
|
{
|
|
if ((encoding & ~(0xffffu << 5)) == 0xd4000001u) // svc #0
|
|
{
|
|
return true;
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd53b0020 && IsCtrEl0AccessForbidden()) // mrs x0, ctr_el0
|
|
{
|
|
return true;
|
|
}
|
|
else if ((encoding & ~0x1f) == 0xd53be020) // mrs x0, cntpct_el0
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsCtrEl0AccessForbidden()
|
|
{
|
|
// Only Linux allows accessing CTR_EL0 from user mode.
|
|
return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsIOS();
|
|
}
|
|
|
|
public static bool IsCacheInstForbidden(uint encoding)
|
|
{
|
|
// Windows does not allow the cache maintenance instructions to be used from user mode.
|
|
return OperatingSystem.IsWindows() && SysUtils.IsCacheInstUciTrapped(encoding);
|
|
}
|
|
|
|
public static bool NeedsContextStoreLoad(InstName name)
|
|
{
|
|
return name == InstName.Svc;
|
|
}
|
|
|
|
private static nint GetBrkHandlerPtr()
|
|
{
|
|
return Marshal.GetFunctionPointerForDelegate<SoftwareInterruptHandler>(NativeInterface.Break);
|
|
}
|
|
|
|
private static nint GetSvcHandlerPtr()
|
|
{
|
|
return Marshal.GetFunctionPointerForDelegate<SoftwareInterruptHandler>(NativeInterface.SupervisorCall);
|
|
}
|
|
|
|
private static nint GetUdfHandlerPtr()
|
|
{
|
|
return Marshal.GetFunctionPointerForDelegate<SoftwareInterruptHandler>(NativeInterface.Undefined);
|
|
}
|
|
|
|
private static nint GetCntpctEl0Ptr()
|
|
{
|
|
return Marshal.GetFunctionPointerForDelegate<Get64>(NativeInterface.GetCntpctEl0);
|
|
}
|
|
|
|
private static nint CheckSynchronizationPtr()
|
|
{
|
|
return Marshal.GetFunctionPointerForDelegate<GetBool>(NativeInterface.CheckSynchronization);
|
|
}
|
|
|
|
public static void WriteSyncPoint(CodeWriter writer, RegisterAllocator regAlloc, TailMerger tailMerger, int spillBaseOffset)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, spillBaseOffset);
|
|
}
|
|
|
|
private static void WriteSyncPoint(CodeWriter writer, ref Assembler asm, RegisterAllocator regAlloc, TailMerger tailMerger, int spillBaseOffset)
|
|
{
|
|
int tempRegister = regAlloc.AllocateTempGprRegister();
|
|
|
|
Operand rt = Register(tempRegister, OperandType.I32);
|
|
|
|
asm.LdrRiUn(rt, Register(regAlloc.FixedContextRegister), NativeContextOffsets.CounterOffset);
|
|
|
|
int branchIndex = writer.InstructionPointer;
|
|
asm.Cbnz(rt, 0);
|
|
|
|
WriteSpill(ref asm, regAlloc, 1u << tempRegister, spillBaseOffset, tempRegister);
|
|
|
|
Operand rn = Register(tempRegister == 0 ? 1 : 0);
|
|
|
|
asm.Mov(rn, (ulong)CheckSynchronizationPtr());
|
|
asm.Blr(rn);
|
|
|
|
tailMerger.AddConditionalZeroReturn(writer, asm, Register(0, OperandType.I32));
|
|
|
|
WriteFill(ref asm, regAlloc, 1u << tempRegister, spillBaseOffset, tempRegister);
|
|
|
|
asm.LdrRiUn(rt, Register(regAlloc.FixedContextRegister), NativeContextOffsets.CounterOffset);
|
|
|
|
uint branchInst = writer.ReadInstructionAt(branchIndex);
|
|
writer.WriteInstructionAt(branchIndex, branchInst | (((uint)(writer.InstructionPointer - branchIndex) & 0x7ffff) << 5));
|
|
|
|
asm.Sub(rt, rt, new Operand(OperandKind.Constant, OperandType.I32, 1));
|
|
asm.StrRiUn(rt, Register(regAlloc.FixedContextRegister), NativeContextOffsets.CounterOffset);
|
|
|
|
regAlloc.FreeTempGprRegister(tempRegister);
|
|
}
|
|
|
|
public static void RewriteCallInstruction(
|
|
CodeWriter writer,
|
|
RegisterAllocator regAlloc,
|
|
TailMerger tailMerger,
|
|
Action writeEpilogue,
|
|
AddressTable<ulong> funcTable,
|
|
nint dispatchStubPtr,
|
|
InstName name,
|
|
ulong pc,
|
|
uint encoding,
|
|
int spillBaseOffset,
|
|
bool isTail = false)
|
|
{
|
|
Assembler asm = new(writer);
|
|
|
|
switch (name)
|
|
{
|
|
case InstName.BUncond:
|
|
case InstName.Bl:
|
|
case InstName.Blr:
|
|
case InstName.Br:
|
|
if (name == InstName.BUncond || name == InstName.Bl)
|
|
{
|
|
int imm = ImmUtils.ExtractSImm26Times4(encoding);
|
|
|
|
WriteCallWithGuestAddress(
|
|
writer,
|
|
ref asm,
|
|
regAlloc,
|
|
tailMerger,
|
|
writeEpilogue,
|
|
funcTable,
|
|
dispatchStubPtr,
|
|
spillBaseOffset,
|
|
pc,
|
|
new(OperandKind.Constant, OperandType.I64, pc + (ulong)imm),
|
|
isTail);
|
|
}
|
|
else
|
|
{
|
|
int rnIndex = RegisterUtils.ExtractRn(encoding);
|
|
if (rnIndex == RegisterUtils.ZrIndex)
|
|
{
|
|
WriteCallWithGuestAddress(
|
|
writer,
|
|
ref asm,
|
|
regAlloc,
|
|
tailMerger,
|
|
writeEpilogue,
|
|
funcTable,
|
|
dispatchStubPtr,
|
|
spillBaseOffset,
|
|
pc,
|
|
new(OperandKind.Constant, OperandType.I64, 0UL),
|
|
isTail);
|
|
}
|
|
else
|
|
{
|
|
rnIndex = regAlloc.RemapReservedGprRegister(rnIndex);
|
|
|
|
WriteCallWithGuestAddress(
|
|
writer,
|
|
ref asm,
|
|
regAlloc,
|
|
tailMerger,
|
|
writeEpilogue,
|
|
funcTable,
|
|
dispatchStubPtr,
|
|
spillBaseOffset,
|
|
pc,
|
|
Register(rnIndex),
|
|
isTail);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Debug.Fail($"Unknown branch instruction \"{name}\".");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public unsafe static void WriteCallWithGuestAddress(
|
|
CodeWriter writer,
|
|
ref Assembler asm,
|
|
RegisterAllocator regAlloc,
|
|
TailMerger tailMerger,
|
|
Action writeEpilogue,
|
|
AddressTable<ulong> funcTable,
|
|
nint funcPtr,
|
|
int spillBaseOffset,
|
|
ulong pc,
|
|
Operand guestAddress,
|
|
bool isTail = false)
|
|
{
|
|
int tempRegister;
|
|
int tempGuestAddress = -1;
|
|
|
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
|
funcTable is { Sparse: true };
|
|
|
|
if (guestAddress.Kind == OperandKind.Constant)
|
|
{
|
|
tempRegister = regAlloc.AllocateTempGprRegister();
|
|
|
|
asm.Mov(Register(tempRegister), guestAddress.Value);
|
|
asm.StrRiUn(Register(tempRegister), Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
|
|
|
regAlloc.FreeTempGprRegister(tempRegister);
|
|
}
|
|
else
|
|
{
|
|
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
|
|
|
if (inlineLookup && guestAddress.Value == 0)
|
|
{
|
|
// X0 will be overwritten. Move the address to a temp register.
|
|
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
|
asm.Mov(Register(tempGuestAddress), guestAddress);
|
|
}
|
|
}
|
|
|
|
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
|
|
|
if (!isTail)
|
|
{
|
|
WriteSpillSkipContext(ref asm, regAlloc, spillBaseOffset);
|
|
}
|
|
|
|
Operand rn = Register(tempRegister);
|
|
|
|
if (regAlloc.FixedContextRegister != 0)
|
|
{
|
|
asm.Mov(Register(0), Register(regAlloc.FixedContextRegister));
|
|
}
|
|
|
|
if (guestAddress.Kind == OperandKind.Constant && funcTable != null)
|
|
{
|
|
ulong funcPtrLoc = (ulong)Unsafe.AsPointer(ref funcTable.GetValue(guestAddress.Value));
|
|
|
|
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
|
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
|
}
|
|
else if (inlineLookup)
|
|
{
|
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
|
|
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
|
|
|
if (tempGuestAddress != -1)
|
|
{
|
|
guestAddress = Register(tempGuestAddress);
|
|
}
|
|
|
|
ulong tableBase = (ulong)funcTable.Base;
|
|
|
|
// Index into the table.
|
|
asm.Mov(rn, tableBase);
|
|
|
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
|
{
|
|
var level = funcTable.Levels[i];
|
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
|
asm.Lsl(indexReg, indexReg, Const(3));
|
|
|
|
// Index into the page.
|
|
asm.Add(rn, rn, indexReg);
|
|
|
|
// Load the page address.
|
|
asm.LdrRiUn(rn, rn, 0);
|
|
}
|
|
|
|
if (tempGuestAddress != -1)
|
|
{
|
|
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
asm.Mov(rn, (ulong)funcPtr);
|
|
}
|
|
|
|
if (isTail)
|
|
{
|
|
writeEpilogue();
|
|
asm.Br(rn);
|
|
}
|
|
else
|
|
{
|
|
asm.Blr(rn);
|
|
|
|
ulong nextAddress = pc + 4UL;
|
|
|
|
asm.Mov(rn, nextAddress);
|
|
asm.Cmp(Register(0), rn);
|
|
|
|
tailMerger.AddConditionalReturn(writer, asm, ArmCondition.Ne);
|
|
|
|
WriteFillSkipContext(ref asm, regAlloc, spillBaseOffset);
|
|
}
|
|
}
|
|
|
|
private static void WriteCall(
|
|
ref Assembler asm,
|
|
RegisterAllocator regAlloc,
|
|
nint funcPtr,
|
|
int spillBaseOffset,
|
|
int? resultRegister,
|
|
params ReadOnlySpan<ulong> callArgs)
|
|
{
|
|
uint resultMask = 0u;
|
|
|
|
if (resultRegister.HasValue)
|
|
{
|
|
resultMask = 1u << resultRegister.Value;
|
|
}
|
|
|
|
int tempRegister = callArgs.Length;
|
|
|
|
if (resultRegister.HasValue && tempRegister == resultRegister.Value)
|
|
{
|
|
tempRegister++;
|
|
}
|
|
|
|
WriteSpill(ref asm, regAlloc, resultMask, spillBaseOffset, tempRegister);
|
|
|
|
// We only support up to 7 arguments right now.
|
|
// ABI defines the first 8 integer arguments to be passed on registers X0-X7.
|
|
// We need at least one register to put the function address on, so that reduces the number of
|
|
// registers we can use for that by one.
|
|
|
|
Debug.Assert(callArgs.Length < 8);
|
|
|
|
for (int index = 0; index < callArgs.Length; index++)
|
|
{
|
|
asm.Mov(Register(index), callArgs[index]);
|
|
}
|
|
|
|
Operand rn = Register(tempRegister);
|
|
|
|
asm.Mov(rn, (ulong)funcPtr);
|
|
asm.Blr(rn);
|
|
|
|
if (resultRegister.HasValue && resultRegister.Value != 0)
|
|
{
|
|
asm.Mov(Register(resultRegister.Value), Register(0));
|
|
}
|
|
|
|
WriteFill(ref asm, regAlloc, resultMask, spillBaseOffset, tempRegister);
|
|
}
|
|
|
|
private static void WriteSpill(ref Assembler asm, RegisterAllocator regAlloc, uint exceptMask, int spillOffset, int tempRegister)
|
|
{
|
|
WriteSpillOrFill(ref asm, regAlloc, exceptMask, spillOffset, tempRegister, spill: true);
|
|
}
|
|
|
|
private static void WriteFill(ref Assembler asm, RegisterAllocator regAlloc, uint exceptMask, int spillOffset, int tempRegister)
|
|
{
|
|
WriteSpillOrFill(ref asm, regAlloc, exceptMask, spillOffset, tempRegister, spill: false);
|
|
}
|
|
|
|
private static void WriteSpillOrFill(
|
|
ref Assembler asm,
|
|
RegisterAllocator regAlloc,
|
|
uint exceptMask,
|
|
int spillOffset,
|
|
int tempRegister,
|
|
bool spill)
|
|
{
|
|
uint gprMask = regAlloc.AllGprMask & ~(AbiConstants.GprCalleeSavedRegsMask | exceptMask);
|
|
|
|
if (regAlloc.AllPStateMask != 0 && !spill)
|
|
{
|
|
// We must reload the status register before reloading the GPRs,
|
|
// since we might otherwise trash one of them by using it as temp register.
|
|
|
|
Operand rt = Register(tempRegister, OperandType.I32);
|
|
|
|
asm.LdrRiUn(rt, Register(RegisterUtils.SpIndex), spillOffset + BitOperations.PopCount(gprMask) * 8);
|
|
asm.MsrNzcv(rt);
|
|
}
|
|
|
|
while (gprMask != 0)
|
|
{
|
|
int reg = BitOperations.TrailingZeroCount(gprMask);
|
|
|
|
if (reg < 31 && (gprMask & (2u << reg)) != 0 && spillOffset < RegisterSaveRestore.Encodable9BitsOffsetLimit)
|
|
{
|
|
if (spill)
|
|
{
|
|
asm.StpRiUn(
|
|
Register(regAlloc.RemapReservedGprRegister(reg)),
|
|
Register(regAlloc.RemapReservedGprRegister(reg + 1)),
|
|
Register(RegisterUtils.SpIndex),
|
|
spillOffset);
|
|
}
|
|
else
|
|
{
|
|
asm.LdpRiUn(
|
|
Register(regAlloc.RemapReservedGprRegister(reg)),
|
|
Register(regAlloc.RemapReservedGprRegister(reg + 1)),
|
|
Register(RegisterUtils.SpIndex),
|
|
spillOffset);
|
|
}
|
|
|
|
gprMask &= ~(3u << reg);
|
|
spillOffset += 16;
|
|
}
|
|
else
|
|
{
|
|
if (spill)
|
|
{
|
|
asm.StrRiUn(Register(regAlloc.RemapReservedGprRegister(reg)), Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
else
|
|
{
|
|
asm.LdrRiUn(Register(regAlloc.RemapReservedGprRegister(reg)), Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
|
|
gprMask &= ~(1u << reg);
|
|
spillOffset += 8;
|
|
}
|
|
}
|
|
|
|
if (regAlloc.AllPStateMask != 0)
|
|
{
|
|
if (spill)
|
|
{
|
|
Operand rt = Register(tempRegister, OperandType.I32);
|
|
|
|
asm.MrsNzcv(rt);
|
|
asm.StrRiUn(rt, Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
|
|
spillOffset += 8;
|
|
}
|
|
|
|
if ((spillOffset & 8) != 0)
|
|
{
|
|
spillOffset += 8;
|
|
}
|
|
|
|
uint fpSimdMask = regAlloc.AllFpSimdMask;
|
|
|
|
while (fpSimdMask != 0)
|
|
{
|
|
int reg = BitOperations.TrailingZeroCount(fpSimdMask);
|
|
|
|
if (reg < 31 && (fpSimdMask & (2u << reg)) != 0 && spillOffset < RegisterSaveRestore.Encodable9BitsOffsetLimit)
|
|
{
|
|
if (spill)
|
|
{
|
|
asm.StpRiUn(
|
|
Register(reg, OperandType.V128),
|
|
Register(reg + 1, OperandType.V128),
|
|
Register(RegisterUtils.SpIndex),
|
|
spillOffset);
|
|
}
|
|
else
|
|
{
|
|
asm.LdpRiUn(
|
|
Register(reg, OperandType.V128),
|
|
Register(reg + 1, OperandType.V128),
|
|
Register(RegisterUtils.SpIndex),
|
|
spillOffset);
|
|
}
|
|
|
|
fpSimdMask &= ~(3u << reg);
|
|
spillOffset += 32;
|
|
}
|
|
else
|
|
{
|
|
if (spill)
|
|
{
|
|
asm.StrRiUn(Register(reg, OperandType.V128), Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
else
|
|
{
|
|
asm.LdrRiUn(Register(reg, OperandType.V128), Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
|
|
fpSimdMask &= ~(1u << reg);
|
|
spillOffset += 16;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteSpillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset)
|
|
{
|
|
WriteSpillOrFillSkipContext(ref asm, regAlloc, spillOffset, spill: true);
|
|
}
|
|
|
|
private static void WriteFillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset)
|
|
{
|
|
WriteSpillOrFillSkipContext(ref asm, regAlloc, spillOffset, spill: false);
|
|
}
|
|
|
|
private static void WriteSpillOrFillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset, bool spill)
|
|
{
|
|
uint gprMask = regAlloc.AllGprMask & ((1u << regAlloc.FixedContextRegister) | (1u << regAlloc.FixedPageTableRegister));
|
|
gprMask &= ~AbiConstants.GprCalleeSavedRegsMask;
|
|
|
|
while (gprMask != 0)
|
|
{
|
|
int reg = BitOperations.TrailingZeroCount(gprMask);
|
|
|
|
if (reg < 31 && (gprMask & (2u << reg)) != 0 && spillOffset < RegisterSaveRestore.Encodable9BitsOffsetLimit)
|
|
{
|
|
if (spill)
|
|
{
|
|
asm.StpRiUn(
|
|
Register(regAlloc.RemapReservedGprRegister(reg)),
|
|
Register(regAlloc.RemapReservedGprRegister(reg + 1)),
|
|
Register(RegisterUtils.SpIndex),
|
|
spillOffset);
|
|
}
|
|
else
|
|
{
|
|
asm.LdpRiUn(
|
|
Register(regAlloc.RemapReservedGprRegister(reg)),
|
|
Register(regAlloc.RemapReservedGprRegister(reg + 1)),
|
|
Register(RegisterUtils.SpIndex),
|
|
spillOffset);
|
|
}
|
|
|
|
gprMask &= ~(3u << reg);
|
|
spillOffset += 16;
|
|
}
|
|
else
|
|
{
|
|
if (spill)
|
|
{
|
|
asm.StrRiUn(Register(regAlloc.RemapReservedGprRegister(reg)), Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
else
|
|
{
|
|
asm.LdrRiUn(Register(regAlloc.RemapReservedGprRegister(reg)), Register(RegisterUtils.SpIndex), spillOffset);
|
|
}
|
|
|
|
gprMask &= ~(1u << reg);
|
|
spillOffset += 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Operand Register(int register, OperandType type = OperandType.I64)
|
|
{
|
|
return new Operand(register, RegisterType.Integer, type);
|
|
}
|
|
|
|
private static Operand Const(long value, OperandType type = OperandType.I64)
|
|
{
|
|
return new Operand(type, (ulong)value);
|
|
}
|
|
|
|
private static int NextFreeRegister(int start, int avoid)
|
|
{
|
|
if (start == avoid)
|
|
{
|
|
start++;
|
|
}
|
|
|
|
return start;
|
|
}
|
|
}
|
|
}
|