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; namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 { static class InstEmitFlow { private const int SpIndex = 31; public static void B(CodeGenContext context, int imm, ArmCondition condition) { context.AddPendingBranch(InstName.B, imm); if (condition == ArmCondition.Al) { context.Arm64Assembler.B(0); } else { context.Arm64Assembler.B(condition, 0); } } public static void Bl(CodeGenContext context, int imm, bool sourceIsThumb, bool targetIsThumb) { uint nextAddress = sourceIsThumb ? context.Pc | 1u : context.Pc - 4; uint targetAddress = targetIsThumb ? context.Pc + (uint)imm : (context.Pc & ~3u) + (uint)imm; if (sourceIsThumb != targetIsThumb) { if (targetIsThumb) { InstEmitCommon.SetThumbFlag(context); } else { InstEmitCommon.ClearThumbFlag(context); } } context.AddPendingCall(targetAddress, nextAddress); context.Arm64Assembler.B(0); } public static void Blx(CodeGenContext context, uint rm, bool sourceIsThumb) { Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); InstEmitCommon.SetThumbFlag(context, rmOperand); uint nextAddress = sourceIsThumb ? (context.Pc - 2) | 1u : context.Pc - 4; context.AddPendingIndirectCall(rm, nextAddress); context.Arm64Assembler.B(0); } public static void Bx(CodeGenContext context, uint rm) { Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); InstEmitCommon.SetThumbFlag(context, rmOperand); context.AddPendingIndirectBranch(InstName.Bx, rm); context.Arm64Assembler.B(0); } public static void Cbnz(CodeGenContext context, uint rn, int imm, bool op) { Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); context.AddPendingBranch(InstName.Cbnz, imm); if (op) { context.Arm64Assembler.Cbnz(rnOperand, 0); } else { context.Arm64Assembler.Cbz(rnOperand, 0); } } public static void It(CodeGenContext context, uint firstCond, uint mask) { Debug.Assert(mask != 0); int instCount = 4 - BitOperations.TrailingZeroCount(mask); Span conditions = stackalloc ArmCondition[instCount]; int i = 0; for (int index = 5 - instCount; index < 4; index++) { bool invert = (mask & (1u << index)) != 0; if (invert) { conditions[i++] = ((ArmCondition)firstCond).Invert(); } else { conditions[i++] = (ArmCondition)firstCond; } } conditions[i] = (ArmCondition)firstCond; context.SetItBlockStart(conditions); } public static void Tbb(CodeGenContext context, uint rn, uint rm, bool h) { context.Arm64Assembler.Mov(context.RegisterAllocator.RemapGprRegister(RegisterUtils.PcRegister), context.Pc); context.AddPendingTableBranch(rn, rm, h); context.Arm64Assembler.B(0); } public unsafe static void WriteCallWithGuestAddress( CodeWriter writer, ref Assembler asm, RegisterAllocator regAlloc, TailMerger tailMerger, Action writeEpilogue, AddressTable funcTable, IntPtr funcPtr, int spillBaseOffset, uint nextAddress, Operand guestAddress, bool isTail = false) { int tempRegister; 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); } tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1; 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 { asm.Mov(rn, (ulong)funcPtr); } if (isTail) { writeEpilogue(); asm.Br(rn); } else { asm.Blr(rn); asm.Mov(rn, nextAddress); asm.Cmp(Register(0), rn); tailMerger.AddConditionalReturn(writer, asm, ArmCondition.Ne); WriteFillSkipContext(ref asm, regAlloc, spillBaseOffset); } } public static void WriteSpillSkipContext(ref Assembler asm, RegisterAllocator regAlloc, int spillOffset) { WriteSpillOrFillSkipContext(ref asm, regAlloc, spillOffset, spill: true); } public 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.UsedGprsMask & ((1u << regAlloc.FixedContextRegister) | (1u << regAlloc.FixedPageTableRegister)); while (gprMask != 0) { int reg = BitOperations.TrailingZeroCount(gprMask); if (reg < 31 && (gprMask & (2u << reg)) != 0 && spillOffset < RegisterSaveRestore.Encodable9BitsOffsetLimit) { if (spill) { asm.StpRiUn(Register(reg), Register(reg + 1), Register(SpIndex), spillOffset); } else { asm.LdpRiUn(Register(reg), Register(reg + 1), Register(SpIndex), spillOffset); } gprMask &= ~(3u << reg); spillOffset += 16; } else { if (spill) { asm.StrRiUn(Register(reg), Register(SpIndex), spillOffset); } else { asm.LdrRiUn(Register(reg), Register(SpIndex), spillOffset); } gprMask &= ~(1u << reg); spillOffset += 8; } } } private static Operand Register(int register, OperandType type = OperandType.I64) { return new Operand(register, RegisterType.Integer, type); } } }