diff options
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs')
| -rw-r--r-- | src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs new file mode 100644 index 00000000..81e44ba0 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs @@ -0,0 +1,256 @@ +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<ArmCondition> 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<ulong> 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); + } + } + + 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.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); + } + } +} |
