Ryujinx/ARMeilleure/Instructions/InstEmitMul32.cs
merry 98e05ee4b7
ARMeilleure: Thumb support (All T16 instructions) (#3105)
* Decoders: Add InITBlock argument

* OpCodeTable: Minor cleanup

* OpCodeTable: Remove existing thumb instruction implementations

* OpCodeTable: Prepare for thumb instructions

* OpCodeTables: Improve thumb fast lookup

* Tests: Prepare for thumb tests

* T16: Implement BX

* T16: Implement LSL/LSR/ASR (imm)

* T16: Implement ADDS, SUBS (reg)

* T16: Implement ADDS, SUBS (3-bit immediate)

* T16: Implement MOVS, CMP, ADDS, SUBS (8-bit immediate)

* T16: Implement ANDS, EORS, LSLS, LSRS, ASRS, ADCS, SBCS, RORS, TST, NEGS, CMP, CMN, ORRS, MULS, BICS, MVNS (low registers)

* T16: Implement ADD, CMP, MOV (high reg)

* T16: Implement BLX (reg)

* T16: Implement LDR (literal)

* T16: Implement {LDR,STR}{,H,B,SB,SH} (register)

* T16: Implement {LDR,STR}{,B,H} (immediate)

* T16: Implement LDR/STR (SP)

* T16: Implement ADR

* T16: Implement Add to SP (immediate)

* T16: Implement ADD/SUB (SP)

* T16: Implement SXTH, SXTB, UXTH, UTXB

* T16: Implement CBZ, CBNZ

* T16: Implement PUSH, POP

* T16: Implement REV, REV16, REVSH

* T16: Implement NOP

* T16: Implement LDM, STM

* T16: Implement SVC

* T16: Implement B (conditional)

* T16: Implement B (unconditional)

* T16: Implement IT

* fixup! T16: Implement ADD/SUB (SP)

* fixup! T16: Implement Add to SP (immediate)

* fixup! T16: Implement IT

* CpuTestThumb: Add randomized tests

* Remove inITBlock argument

* Address nits

* Use index to handle IfThenBlockState

* Reduce line noise

* fixup

* nit
2022-02-17 19:39:45 -03:00

379 lines
12 KiB
C#

using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
[Flags]
private enum MullFlags
{
Subtract = 1,
Add = 1 << 1,
Signed = 1 << 2,
SignedAdd = Signed | Add,
SignedSubtract = Signed | Subtract
}
public static void Mla(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand a = GetIntA32(context, op.Ra);
Operand res = context.Add(a, context.Multiply(n, m));
if (ShouldSetFlags(context))
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Mls(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand a = GetIntA32(context, op.Ra);
Operand res = context.Subtract(a, context.Multiply(n, m));
EmitAluStore(context, res);
}
public static void Smmla(ArmEmitterContext context)
{
EmitSmmul(context, MullFlags.SignedAdd);
}
public static void Smmls(ArmEmitterContext context)
{
EmitSmmul(context, MullFlags.SignedSubtract);
}
public static void Smmul(ArmEmitterContext context)
{
EmitSmmul(context, MullFlags.Signed);
}
private static void EmitSmmul(ArmEmitterContext context, MullFlags flags)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand res = context.Multiply(n, m);
if (flags.HasFlag(MullFlags.Add) && op.Ra != 0xf)
{
res = context.Add(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
}
else if (flags.HasFlag(MullFlags.Subtract))
{
res = context.Subtract(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
}
if (op.R)
{
res = context.Add(res, Const(0x80000000L));
}
Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32)));
EmitGenericAluStoreA32(context, op.Rd, false, hi);
}
public static void Smla__(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
Operand a = GetIntA32(context, op.Ra);
if (op.NHigh)
{
n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
}
else
{
n = context.SignExtend16(OperandType.I64, n);
}
if (op.MHigh)
{
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
}
else
{
m = context.SignExtend16(OperandType.I64, m);
}
Operand res = context.Multiply(n, m);
Operand toAdd = context.SignExtend32(OperandType.I64, a);
res = context.Add(res, toAdd);
Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
res = context.ConvertI64ToI32(res);
UpdateQFlag(context, q);
EmitGenericAluStoreA32(context, op.Rd, false, res);
}
public static void Smlal(ArmEmitterContext context)
{
EmitMlal(context, true);
}
public static void Smlal__(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (op.NHigh)
{
n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
}
else
{
n = context.SignExtend16(OperandType.I64, n);
}
if (op.MHigh)
{
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
}
else
{
m = context.SignExtend16(OperandType.I64, m);
}
Operand res = context.Multiply(n, m);
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
res = context.Add(res, toAdd);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
EmitGenericAluStoreA32(context, op.RdHi, false, hi);
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
}
public static void Smlaw_(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
Operand a = GetIntA32(context, op.Ra);
if (op.MHigh)
{
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
}
else
{
m = context.SignExtend16(OperandType.I64, m);
}
Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m);
Operand toAdd = context.ShiftLeft(context.SignExtend32(OperandType.I64, a), Const(16));
res = context.Add(res, toAdd);
res = context.ShiftRightSI(res, Const(16));
Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
res = context.ConvertI64ToI32(res);
UpdateQFlag(context, q);
EmitGenericAluStoreA32(context, op.Rd, false, res);
}
public static void Smul__(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (op.NHigh)
{
n = context.ShiftRightSI(n, Const(16));
}
else
{
n = context.SignExtend16(OperandType.I32, n);
}
if (op.MHigh)
{
m = context.ShiftRightSI(m, Const(16));
}
else
{
m = context.SignExtend16(OperandType.I32, m);
}
Operand res = context.Multiply(n, m);
EmitGenericAluStoreA32(context, op.Rd, false, res);
}
public static void Smull(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand res = context.Multiply(n, m);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
if (ShouldSetFlags(context))
{
EmitNZFlagsCheck(context, res);
}
EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
}
public static void Smulw_(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (op.MHigh)
{
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
}
else
{
m = context.SignExtend16(OperandType.I64, m);
}
Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m);
res = context.ShiftRightUI(res, Const(16));
res = context.ConvertI64ToI32(res);
EmitGenericAluStoreA32(context, op.Rd, false, res);
}
public static void Umaal(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand dHi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi));
Operand dLo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo));
Operand res = context.Multiply(n, m);
res = context.Add(res, dHi);
res = context.Add(res, dLo);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
EmitGenericAluStoreA32(context, op.RdHi, false, hi);
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
}
public static void Umlal(ArmEmitterContext context)
{
EmitMlal(context, false);
}
public static void Umull(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand res = context.Multiply(n, m);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
if (ShouldSetFlags(context))
{
EmitNZFlagsCheck(context, res);
}
EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
}
private static void EmitMlal(ArmEmitterContext context, bool signed)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (signed)
{
n = context.SignExtend32(OperandType.I64, n);
m = context.SignExtend32(OperandType.I64, m);
}
else
{
n = context.ZeroExtend32(OperandType.I64, n);
m = context.ZeroExtend32(OperandType.I64, m);
}
Operand res = context.Multiply(n, m);
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
res = context.Add(res, toAdd);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
if (ShouldSetFlags(context))
{
EmitNZFlagsCheck(context, res);
}
EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
}
private static void UpdateQFlag(ArmEmitterContext context, Operand q)
{
Operand lblSkipSetQ = Label();
context.BranchIfFalse(lblSkipSetQ, q);
SetFlag(context, PState.QFlag, Const(1));
context.MarkLabel(lblSkipSetQ);
}
}
}