mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-10-02 16:50:20 -07:00

* Implement a new JIT for Arm devices * Auto-format * Make a lot of Assembler members read-only * More read-only * Fix more warnings * ObjectDisposedException.ThrowIf * New JIT cache for platforms that enforce W^X, currently unused * Remove unused using * Fix assert * Pass memory manager type around * Safe memory manager mode support + other improvements * Actual safe memory manager mode masking support * PR feedback
1106 lines
42 KiB
C#
1106 lines
42 KiB
C#
using Ryujinx.Cpu.LightningJit.CodeGen;
|
|
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
|
|
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|
{
|
|
static class InstEmitAlu
|
|
{
|
|
private const uint Imm12Limit = 0x1000;
|
|
|
|
public static void AdcI(CodeGenContext context, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
EmitI(context, s ? context.Arm64Assembler.Adcs : context.Arm64Assembler.Adc, rd, rn, imm, s);
|
|
}
|
|
|
|
public static void AdcR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitR(context, s ? context.Arm64Assembler.Adcs : context.Arm64Assembler.Adc, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void AdcRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitRr(context, s ? context.Arm64Assembler.Adcs : context.Arm64Assembler.Adc, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void AddI(CodeGenContext context, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
EmitArithmeticI(context, s ? context.Arm64Assembler.Adds : context.Arm64Assembler.Add, rd, rn, imm, s);
|
|
}
|
|
|
|
public static void AddR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitArithmeticR(context, s ? context.Arm64Assembler.Adds : context.Arm64Assembler.Add, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void AddRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitRr(context, s ? context.Arm64Assembler.Adds : context.Arm64Assembler.Add, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void Adr(CodeGenContext context, uint rd, uint imm, bool add)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
|
|
uint pc = context.Pc & ~3u;
|
|
|
|
if (add)
|
|
{
|
|
pc += imm;
|
|
}
|
|
else
|
|
{
|
|
pc -= imm;
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, pc);
|
|
}
|
|
|
|
public static void AndI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s)
|
|
{
|
|
EmitLogicalI(context, s ? context.Arm64Assembler.Ands : context.Arm64Assembler.And, rd, rn, imm, immRotated, s);
|
|
}
|
|
|
|
public static void AndR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitLogicalR(context, s ? context.Arm64Assembler.Ands : context.Arm64Assembler.And, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void AndRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitLogicalRr(context, s ? context.Arm64Assembler.Ands : context.Arm64Assembler.And, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void BicI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s)
|
|
{
|
|
if (!s && CodeGenCommon.TryEncodeBitMask(OperandType.I32, ~imm, out _, out _, out _))
|
|
{
|
|
AndI(context, rd, rn, ~imm, immRotated, s);
|
|
}
|
|
else
|
|
{
|
|
EmitLogicalI(context, s ? context.Arm64Assembler.Bics : context.Arm64Assembler.Bic, rd, rn, imm, immRotated, s, immForm: false);
|
|
}
|
|
}
|
|
|
|
public static void BicR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitLogicalR(context, s ? context.Arm64Assembler.Bics : context.Arm64Assembler.Bic, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void BicRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitLogicalRr(context, s ? context.Arm64Assembler.Bics : context.Arm64Assembler.Bic, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void CmnI(CodeGenContext context, uint rn, uint imm)
|
|
{
|
|
EmitCompareI(context, context.Arm64Assembler.Cmn, rn, imm);
|
|
}
|
|
|
|
public static void CmnR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5)
|
|
{
|
|
EmitCompareR(context, context.Arm64Assembler.Cmn, rn, rm, sType, imm5);
|
|
}
|
|
|
|
public static void CmnRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs)
|
|
{
|
|
EmitCompareRr(context, context.Arm64Assembler.Cmn, rn, rm, sType, rs);
|
|
}
|
|
|
|
public static void CmpI(CodeGenContext context, uint rn, uint imm)
|
|
{
|
|
EmitCompareI(context, context.Arm64Assembler.Cmp, rn, imm);
|
|
}
|
|
|
|
public static void CmpR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5)
|
|
{
|
|
EmitCompareR(context, context.Arm64Assembler.Cmp, rn, rm, sType, imm5);
|
|
}
|
|
|
|
public static void CmpRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs)
|
|
{
|
|
EmitCompareRr(context, context.Arm64Assembler.Cmp, rn, rm, sType, rs);
|
|
}
|
|
|
|
public static void EorI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s)
|
|
{
|
|
EmitLogicalI(context, s ? context.Arm64Assembler.Eors : context.Arm64Assembler.Eor, rd, rn, imm, immRotated, s);
|
|
}
|
|
|
|
public static void EorR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitLogicalR(context, s ? context.Arm64Assembler.Eors : context.Arm64Assembler.Eor, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void EorRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitLogicalRr(context, s ? context.Arm64Assembler.Eors : context.Arm64Assembler.Eor, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void OrnI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s)
|
|
{
|
|
if (!s && CodeGenCommon.TryEncodeBitMask(OperandType.I32, ~imm, out _, out _, out _))
|
|
{
|
|
OrrI(context, rd, rn, ~imm, immRotated, s);
|
|
}
|
|
else
|
|
{
|
|
EmitLogicalI(context, s ? context.Arm64Assembler.Orns : context.Arm64Assembler.Orn, rd, rn, imm, immRotated, s, immForm: false);
|
|
}
|
|
}
|
|
|
|
public static void OrnR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitLogicalR(context, s ? context.Arm64Assembler.Orns : context.Arm64Assembler.Orn, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void OrrI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s)
|
|
{
|
|
EmitLogicalI(context, s ? context.Arm64Assembler.Orrs : context.Arm64Assembler.Orr, rd, rn, imm, immRotated, s);
|
|
}
|
|
|
|
public static void OrrR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitLogicalR(context, s ? context.Arm64Assembler.Orrs : context.Arm64Assembler.Orr, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void OrrRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitLogicalRr(context, s ? context.Arm64Assembler.Orrs : context.Arm64Assembler.Orr, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void RsbI(CodeGenContext context, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
if (imm == 0)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
|
|
if (s)
|
|
{
|
|
context.Arm64Assembler.Negs(rdOperand, rnOperand);
|
|
context.SetNzcvModified();
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Neg(rdOperand, rnOperand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EmitI(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, imm, s, reverse: true);
|
|
}
|
|
}
|
|
|
|
public static void RsbR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitR(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, imm5, s, reverse: true);
|
|
}
|
|
|
|
public static void RsbRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitRr(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, rs, s, reverse: true);
|
|
}
|
|
|
|
public static void RscI(CodeGenContext context, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
EmitI(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, imm, s, reverse: true);
|
|
}
|
|
|
|
public static void RscR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitR(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, imm5, s, reverse: true);
|
|
}
|
|
|
|
public static void RscRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitRr(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, rs, s, reverse: true);
|
|
}
|
|
|
|
public static void SbcI(CodeGenContext context, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
EmitI(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, imm, s);
|
|
}
|
|
|
|
public static void SbcR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitR(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void SbcRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitRr(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void SubI(CodeGenContext context, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
EmitArithmeticI(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, imm, s);
|
|
}
|
|
|
|
public static void SubR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
EmitArithmeticR(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, imm5, s);
|
|
}
|
|
|
|
public static void SubRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
EmitRr(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, rs, s);
|
|
}
|
|
|
|
public static void TeqI(CodeGenContext context, uint rn, uint imm, bool immRotated)
|
|
{
|
|
EmitLogicalI(context, (rnOperand, rmOperand) => EmitTeq(context, rnOperand, rmOperand), rn, imm, immRotated);
|
|
}
|
|
|
|
public static void TeqR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5)
|
|
{
|
|
EmitLogicalR(context, (rnOperand, rmOperand) => EmitTeq(context, rnOperand, rmOperand), rn, rm, sType, imm5);
|
|
}
|
|
|
|
public static void TeqRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs)
|
|
{
|
|
EmitLogicalRr(context, (rnOperand, rmOperand) => EmitTeq(context, rnOperand, rmOperand), rn, rm, sType, rs);
|
|
}
|
|
|
|
public static void TstI(CodeGenContext context, uint rn, uint imm, bool immRotated)
|
|
{
|
|
EmitLogicalI(context, context.Arm64Assembler.Tst, rn, imm, immRotated);
|
|
}
|
|
|
|
public static void TstR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5)
|
|
{
|
|
EmitLogicalR(context, context.Arm64Assembler.Tst, rn, rm, sType, imm5);
|
|
}
|
|
|
|
public static void TstRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs)
|
|
{
|
|
EmitLogicalRr(context, context.Arm64Assembler.Tst, rn, rm, sType, rs);
|
|
}
|
|
|
|
private static void EmitArithmeticI(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint imm, bool s)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
|
|
if (imm < Imm12Limit)
|
|
{
|
|
Operand rmOperand = new(OperandKind.Constant, OperandType.I32, imm);
|
|
|
|
action(rdOperand, rnOperand, rmOperand);
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Mov(tempRegister.Operand, imm);
|
|
|
|
action(rdOperand, rnOperand, tempRegister.Operand);
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
|
|
private static void EmitArithmeticR(
|
|
CodeGenContext context,
|
|
Action<Operand, Operand, Operand, ArmShiftType, int> action,
|
|
uint rd,
|
|
uint rn,
|
|
uint rm,
|
|
uint sType,
|
|
uint imm5,
|
|
bool s)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
if (CanShiftArithmetic(sType, imm5))
|
|
{
|
|
action(rdOperand, rnOperand, rmOperand, (ArmShiftType)sType, (int)imm5);
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType);
|
|
|
|
action(rdOperand, rnOperand, rmOperand, ArmShiftType.Lsl, 0);
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
|
|
private static void EmitCompareI(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint imm)
|
|
{
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
|
|
if (imm < Imm12Limit)
|
|
{
|
|
Operand rmOperand = new(OperandKind.Constant, OperandType.I32, imm);
|
|
|
|
action(rnOperand, rmOperand);
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Mov(tempRegister.Operand, imm);
|
|
|
|
action(rnOperand, tempRegister.Operand);
|
|
}
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
|
|
private static void EmitCompareR(
|
|
CodeGenContext context,
|
|
Action<Operand, Operand, ArmShiftType, int> action,
|
|
uint rn,
|
|
uint rm,
|
|
uint sType,
|
|
uint imm5)
|
|
{
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
if (CanShiftArithmetic(sType, imm5))
|
|
{
|
|
action(rnOperand, rmOperand, (ArmShiftType)sType, (int)imm5);
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType);
|
|
|
|
action(rnOperand, rmOperand, ArmShiftType.Lsl, 0);
|
|
}
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
|
|
private static void EmitCompareRr(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint rm, uint sType, uint rs)
|
|
{
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType);
|
|
|
|
action(rnOperand, rmOperand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
|
|
private static void EmitI(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint imm, bool s, bool reverse = false)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Mov(tempRegister.Operand, imm);
|
|
|
|
if (reverse)
|
|
{
|
|
action(rdOperand, tempRegister.Operand, rnOperand);
|
|
}
|
|
else
|
|
{
|
|
action(rdOperand, rnOperand, tempRegister.Operand);
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
|
|
private static void EmitR(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s, bool reverse = false)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType);
|
|
|
|
if (reverse)
|
|
{
|
|
action(rdOperand, rmOperand, rnOperand);
|
|
}
|
|
else
|
|
{
|
|
action(rdOperand, rnOperand, rmOperand);
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
|
|
private static void EmitRr(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint rm, uint sType, uint rs, bool s, bool reverse = false)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType);
|
|
|
|
if (reverse)
|
|
{
|
|
action(rdOperand, rmOperand, rnOperand);
|
|
}
|
|
else
|
|
{
|
|
action(rdOperand, rnOperand, rmOperand);
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
|
|
private static void EmitLogicalI(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint imm, bool immRotated)
|
|
{
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
if (immRotated)
|
|
{
|
|
if ((imm & (1u << 31)) != 0)
|
|
{
|
|
context.Arm64Assembler.Orr(flagsRegister.Operand, flagsRegister.Operand, InstEmitCommon.Const(2));
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Bfc(flagsRegister.Operand, 1, 1);
|
|
}
|
|
}
|
|
|
|
if (CodeGenCommon.TryEncodeBitMask(OperandType.I32, imm, out _, out _, out _))
|
|
{
|
|
action(rnOperand, InstEmitCommon.Const((int)imm));
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Mov(tempRegister.Operand, imm);
|
|
|
|
action(rnOperand, tempRegister.Operand);
|
|
}
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
|
|
private static void EmitLogicalI(
|
|
CodeGenContext context,
|
|
Action<Operand, Operand, Operand> action,
|
|
uint rd,
|
|
uint rn,
|
|
uint imm,
|
|
bool immRotated,
|
|
bool s,
|
|
bool immForm = true)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
|
|
ScopedRegister flagsRegister = default;
|
|
|
|
if (s)
|
|
{
|
|
flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
if (immRotated)
|
|
{
|
|
if ((imm & (1u << 31)) != 0)
|
|
{
|
|
context.Arm64Assembler.Orr(flagsRegister.Operand, flagsRegister.Operand, InstEmitCommon.Const(2));
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Bfc(flagsRegister.Operand, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (imm == 0 || (immForm && CodeGenCommon.TryEncodeBitMask(OperandType.I32, imm, out _, out _, out _)))
|
|
{
|
|
action(rdOperand, rnOperand, InstEmitCommon.Const((int)imm));
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Mov(tempRegister.Operand, imm);
|
|
|
|
action(rdOperand, rnOperand, tempRegister.Operand);
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
flagsRegister.Dispose();
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
|
|
private static void EmitLogicalR(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint rm, uint sType, uint imm5)
|
|
{
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand);
|
|
|
|
action(rnOperand, rmOperand);
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
|
|
private static void EmitLogicalR(CodeGenContext context, Action<Operand, Operand, Operand, ArmShiftType, int> action, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
if (CanShift(sType, imm5) && !s)
|
|
{
|
|
action(rdOperand, rnOperand, rmOperand, (ArmShiftType)sType, (int)imm5);
|
|
}
|
|
else
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
ScopedRegister flagsRegister = default;
|
|
|
|
if (s)
|
|
{
|
|
flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand);
|
|
}
|
|
else
|
|
{
|
|
rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, null);
|
|
}
|
|
|
|
action(rdOperand, rnOperand, rmOperand, ArmShiftType.Lsl, 0);
|
|
|
|
if (s)
|
|
{
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
flagsRegister.Dispose();
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void EmitLogicalRr(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint rm, uint sType, uint rs)
|
|
{
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand);
|
|
|
|
action(rnOperand, rmOperand);
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
|
|
private static void EmitLogicalRr(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint rm, uint sType, uint rs, bool s)
|
|
{
|
|
if (s)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand);
|
|
|
|
action(rdOperand, rnOperand, rmOperand);
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
else
|
|
{
|
|
EmitRr(context, action, rd, rn, rm, sType, rs, s);
|
|
}
|
|
}
|
|
|
|
public static bool CanShiftArithmetic(uint sType, uint imm5)
|
|
{
|
|
// We can't encode ROR or RRX.
|
|
|
|
return sType != 3 && (sType == 0 || imm5 != 0);
|
|
}
|
|
|
|
public static bool CanShift(uint sType, uint imm5)
|
|
{
|
|
// We can encode all shift types directly, except RRX.
|
|
|
|
return imm5 != 0 || sType == 0;
|
|
}
|
|
|
|
public static Operand GetMShiftedByImmediate(CodeGenContext context, Operand dest, Operand m, uint imm, uint sType, Operand? carryOut = null)
|
|
{
|
|
int shift = (int)imm;
|
|
|
|
if (shift == 0)
|
|
{
|
|
switch ((ArmShiftType)sType)
|
|
{
|
|
case ArmShiftType.Lsr:
|
|
shift = 32;
|
|
break;
|
|
case ArmShiftType.Asr:
|
|
shift = 32;
|
|
break;
|
|
case ArmShiftType.Ror:
|
|
shift = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (shift != 0)
|
|
{
|
|
switch ((ArmShiftType)sType)
|
|
{
|
|
case ArmShiftType.Lsl:
|
|
m = GetLslC(context, dest, m, carryOut, shift);
|
|
break;
|
|
case ArmShiftType.Lsr:
|
|
m = GetLsrC(context, dest, m, carryOut, shift);
|
|
break;
|
|
case ArmShiftType.Asr:
|
|
m = GetAsrC(context, dest, m, carryOut, shift);
|
|
break;
|
|
case ArmShiftType.Ror:
|
|
if (imm != 0)
|
|
{
|
|
m = GetRorC(context, dest, m, carryOut, shift);
|
|
}
|
|
else
|
|
{
|
|
m = GetRrxC(context, dest, m, carryOut);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
public static Operand GetMShiftedByReg(CodeGenContext context, Operand dest, Operand m, Operand s, uint sType, Operand? carryOut = null)
|
|
{
|
|
Operand shiftResult = m;
|
|
|
|
switch ((ArmShiftType)sType)
|
|
{
|
|
case ArmShiftType.Lsl:
|
|
shiftResult = EmitLslC(context, dest, m, carryOut, s);
|
|
break;
|
|
case ArmShiftType.Lsr:
|
|
shiftResult = EmitLsrC(context, dest, m, carryOut, s);
|
|
break;
|
|
case ArmShiftType.Asr:
|
|
shiftResult = EmitAsrC(context, dest, m, carryOut, s);
|
|
break;
|
|
case ArmShiftType.Ror:
|
|
shiftResult = EmitRorC(context, dest, m, carryOut, s);
|
|
break;
|
|
}
|
|
|
|
return shiftResult;
|
|
}
|
|
|
|
private static void EmitIfHelper(CodeGenContext context, Operand boolValue, Action action, bool expected = true)
|
|
{
|
|
Debug.Assert(boolValue.Type == OperandType.I32);
|
|
|
|
int branchInstructionPointer = context.CodeWriter.InstructionPointer;
|
|
|
|
if (expected)
|
|
{
|
|
context.Arm64Assembler.Cbnz(boolValue, 0);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Cbz(boolValue, 0);
|
|
}
|
|
|
|
action();
|
|
|
|
int offset = context.CodeWriter.InstructionPointer - branchInstructionPointer;
|
|
Debug.Assert(offset >= 0);
|
|
Debug.Assert((offset << 13) >> 13 == offset);
|
|
uint branchInst = context.CodeWriter.ReadInstructionAt(branchInstructionPointer);
|
|
context.CodeWriter.WriteInstructionAt(branchInstructionPointer, branchInst | (uint)(offset << 5));
|
|
}
|
|
|
|
private static Operand EmitLslC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand mask = tempRegister.Operand;
|
|
context.Arm64Assembler.Uxtb(mask, shift);
|
|
context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(32));
|
|
context.Arm64Assembler.Asr(mask, mask, InstEmitCommon.Const(31));
|
|
|
|
Operand dest64 = new(OperandKind.Register, OperandType.I64, dest.Value);
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
context.Arm64Assembler.Lslv(dest64, m, shift);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Lslv(dest, m, shift);
|
|
}
|
|
|
|
// If shift >= 32, force the result to 0.
|
|
context.Arm64Assembler.And(dest, dest, mask);
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
EmitIfHelper(context, shift, () =>
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Uxtb(mask, shift);
|
|
context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(33));
|
|
context.Arm64Assembler.Lsr(mask, mask, InstEmitCommon.Const(31));
|
|
context.Arm64Assembler.Lsr(tempRegister.Operand, dest64, InstEmitCommon.Const(32));
|
|
context.Arm64Assembler.And(tempRegister.Operand, tempRegister.Operand, mask);
|
|
|
|
UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value);
|
|
}, false);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
private static Operand GetLslC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
if ((uint)shift > 32)
|
|
{
|
|
return GetShiftByMoreThan32(context, carryOut);
|
|
}
|
|
else if (shift == 32)
|
|
{
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMLsb(context, m, carryOut.Value);
|
|
}
|
|
|
|
return InstEmitCommon.Const(0);
|
|
}
|
|
else
|
|
{
|
|
if (carryOut.HasValue)
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Lsr(tempRegister.Operand, m, InstEmitCommon.Const(32 - shift));
|
|
|
|
UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value);
|
|
}
|
|
|
|
context.Arm64Assembler.Lsl(dest, m, InstEmitCommon.Const(shift));
|
|
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
private static Operand EmitLsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand mask = tempRegister.Operand;
|
|
context.Arm64Assembler.Uxtb(mask, shift);
|
|
context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(32));
|
|
context.Arm64Assembler.Asr(mask, mask, InstEmitCommon.Const(31));
|
|
|
|
context.Arm64Assembler.Lsrv(dest, m, shift);
|
|
|
|
// If shift >= 32, force the result to 0.
|
|
context.Arm64Assembler.And(dest, dest, mask);
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
EmitIfHelper(context, shift, () =>
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Uxtb(mask, shift);
|
|
context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(33));
|
|
context.Arm64Assembler.Lsr(mask, mask, InstEmitCommon.Const(31));
|
|
context.Arm64Assembler.Sub(tempRegister.Operand, shift, InstEmitCommon.Const(1));
|
|
context.Arm64Assembler.Lsrv(tempRegister.Operand, m, tempRegister.Operand);
|
|
context.Arm64Assembler.And(tempRegister.Operand, tempRegister.Operand, mask);
|
|
|
|
UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value);
|
|
}, false);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
public static Operand GetLsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
if ((uint)shift > 32)
|
|
{
|
|
return GetShiftByMoreThan32(context, carryOut);
|
|
}
|
|
else if (shift == 32)
|
|
{
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMMsb(context, m, carryOut.Value);
|
|
}
|
|
|
|
return InstEmitCommon.Const(0);
|
|
}
|
|
else
|
|
{
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMShrOut(context, m, shift, carryOut.Value);
|
|
}
|
|
|
|
context.Arm64Assembler.Lsr(dest, m, InstEmitCommon.Const(shift));
|
|
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
private static Operand GetShiftByMoreThan32(CodeGenContext context, Operand? carryOut)
|
|
{
|
|
if (carryOut.HasValue)
|
|
{
|
|
// Clear carry flag.
|
|
|
|
context.Arm64Assembler.Bfc(carryOut.Value, 1, 1);
|
|
}
|
|
|
|
return InstEmitCommon.Const(0);
|
|
}
|
|
|
|
private static Operand EmitAsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand mask = tempRegister.Operand;
|
|
context.Arm64Assembler.Uxtb(mask, shift);
|
|
context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(31));
|
|
context.Arm64Assembler.Orn(mask, shift, mask, ArmShiftType.Asr, 31);
|
|
|
|
context.Arm64Assembler.Asrv(dest, m, mask);
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
EmitIfHelper(context, shift, () =>
|
|
{
|
|
// If shift >= 32, carry should be equal to the MSB of Rm.
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Sub(tempRegister.Operand, mask, InstEmitCommon.Const(1));
|
|
context.Arm64Assembler.Orr(tempRegister.Operand, tempRegister.Operand, mask, ArmShiftType.Asr, 31);
|
|
context.Arm64Assembler.Lsrv(tempRegister.Operand, m, tempRegister.Operand);
|
|
|
|
UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value);
|
|
}, false);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
private static Operand GetAsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
if ((uint)shift >= 32)
|
|
{
|
|
context.Arm64Assembler.Asr(dest, m, InstEmitCommon.Const(31));
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMLsb(context, dest, carryOut.Value);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
else
|
|
{
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMShrOut(context, m, shift, carryOut.Value);
|
|
}
|
|
|
|
context.Arm64Assembler.Asr(dest, m, InstEmitCommon.Const(shift));
|
|
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
private static Operand EmitRorC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32);
|
|
|
|
context.Arm64Assembler.Rorv(dest, m, shift);
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
EmitIfHelper(context, shift, () =>
|
|
{
|
|
SetCarryMMsb(context, m, carryOut.Value);
|
|
}, false);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
private static Operand GetRorC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
shift &= 0x1f;
|
|
|
|
context.Arm64Assembler.Ror(dest, m, InstEmitCommon.Const(shift));
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMMsb(context, dest, carryOut.Value);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
private static Operand GetRrxC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
// Rotate right by 1 with carry.
|
|
|
|
if (carryOut.HasValue)
|
|
{
|
|
SetCarryMLsb(context, m, carryOut.Value);
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(dest, m);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.MrsNzcv(tempRegister.Operand);
|
|
context.Arm64Assembler.Bfxil(dest, tempRegister.Operand, 29, 1);
|
|
context.Arm64Assembler.Ror(dest, dest, InstEmitCommon.Const(1));
|
|
|
|
return dest;
|
|
}
|
|
|
|
private static void SetCarryMLsb(CodeGenContext context, Operand m, Operand carryOut)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
UpdateCarryFlag(context, m, carryOut);
|
|
}
|
|
|
|
private static void SetCarryMMsb(CodeGenContext context, Operand m, Operand carryOut)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Lsr(tempRegister.Operand, m, InstEmitCommon.Const(31));
|
|
|
|
UpdateCarryFlag(context, tempRegister.Operand, carryOut);
|
|
}
|
|
|
|
private static void SetCarryMShrOut(CodeGenContext context, Operand m, int shift, Operand carryOut)
|
|
{
|
|
Debug.Assert(m.Type == OperandType.I32);
|
|
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Lsr(tempRegister.Operand, m, InstEmitCommon.Const(shift - 1));
|
|
|
|
UpdateCarryFlag(context, tempRegister.Operand, carryOut);
|
|
}
|
|
|
|
private static void UpdateCarryFlag(CodeGenContext context, Operand value, Operand carryOut)
|
|
{
|
|
context.Arm64Assembler.Bfi(carryOut, value, 1, 1);
|
|
}
|
|
|
|
private static void EmitTeq(CodeGenContext context, Operand rnOperand, Operand rmOperand)
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Eors(tempRegister.Operand, rnOperand, rmOperand);
|
|
}
|
|
}
|
|
}
|