diff options
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Arm32/Target')
36 files changed, 19956 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs new file mode 100644 index 00000000..1e8a8915 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs @@ -0,0 +1,789 @@ +using ARMeilleure.Common; +using ARMeilleure.Memory; +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class Compiler + { + public const uint UsableGprsMask = 0x7fff; + public const uint UsableFpSimdMask = 0xffff; + public const uint UsablePStateMask = 0xf0000000; + + private const int Encodable26BitsOffsetLimit = 0x2000000; + + private readonly struct Context + { + public readonly CodeWriter Writer; + public readonly RegisterAllocator RegisterAllocator; + public readonly MemoryManagerType MemoryManagerType; + public readonly TailMerger TailMerger; + public readonly AddressTable<ulong> FuncTable; + public readonly IntPtr DispatchStubPointer; + + private readonly RegisterSaveRestore _registerSaveRestore; + private readonly IntPtr _pageTablePointer; + + public Context( + CodeWriter writer, + RegisterAllocator registerAllocator, + MemoryManagerType mmType, + TailMerger tailMerger, + AddressTable<ulong> funcTable, + RegisterSaveRestore registerSaveRestore, + IntPtr dispatchStubPointer, + IntPtr pageTablePointer) + { + Writer = writer; + RegisterAllocator = registerAllocator; + MemoryManagerType = mmType; + TailMerger = tailMerger; + FuncTable = funcTable; + _registerSaveRestore = registerSaveRestore; + DispatchStubPointer = dispatchStubPointer; + _pageTablePointer = pageTablePointer; + } + + public readonly int GetReservedStackOffset() + { + return _registerSaveRestore.GetReservedStackOffset(); + } + + public readonly void WritePrologueAt(int instructionPointer) + { + CodeWriter writer = new(); + Assembler asm = new(writer); + + _registerSaveRestore.WritePrologue(ref asm); + + // If needed, set up the fixed registers with the pointers we will use. + // First one is the context pointer (passed as first argument), + // second one is the page table or address space base, it is at a fixed memory location and considered constant. + + if (RegisterAllocator.FixedContextRegister != 0) + { + asm.Mov(Register(RegisterAllocator.FixedContextRegister), Register(0)); + } + + asm.Mov(Register(RegisterAllocator.FixedPageTableRegister), (ulong)_pageTablePointer); + + LoadFromContext(ref asm); + + // Write the prologue at the specified position in our writer. + Writer.WriteInstructionsAt(instructionPointer, writer); + } + + public readonly void WriteEpilogueWithoutContext() + { + Assembler asm = new(Writer); + + _registerSaveRestore.WriteEpilogue(ref asm); + } + + public void LoadFromContext() + { + Assembler asm = new(Writer); + + LoadFromContext(ref asm); + } + + private void LoadFromContext(ref Assembler asm) + { + LoadGprFromContext(ref asm, RegisterAllocator.UsedGprsMask & UsableGprsMask, NativeContextOffsets.GprBaseOffset); + LoadFpSimdFromContext(ref asm, RegisterAllocator.UsedFpSimdMask & UsableFpSimdMask, NativeContextOffsets.FpSimdBaseOffset); + LoadPStateFromContext(ref asm, UsablePStateMask, NativeContextOffsets.FlagsBaseOffset); + } + + public void StoreToContext() + { + Assembler asm = new(Writer); + + StoreToContext(ref asm); + } + + private void StoreToContext(ref Assembler asm) + { + StoreGprToContext(ref asm, RegisterAllocator.UsedGprsMask & UsableGprsMask, NativeContextOffsets.GprBaseOffset); + StoreFpSimdToContext(ref asm, RegisterAllocator.UsedFpSimdMask & UsableFpSimdMask, NativeContextOffsets.FpSimdBaseOffset); + StorePStateToContext(ref asm, UsablePStateMask, NativeContextOffsets.FlagsBaseOffset); + } + + private void LoadGprFromContext(ref Assembler asm, uint mask, int baseOffset) + { + Operand contextPtr = Register(RegisterAllocator.FixedContextRegister); + + while (mask != 0) + { + int reg = BitOperations.TrailingZeroCount(mask); + int offset = baseOffset + reg * 8; + + if (reg < 31 && (mask & (2u << reg)) != 0 && offset < RegisterSaveRestore.Encodable9BitsOffsetLimit) + { + mask &= ~(3u << reg); + + asm.LdpRiUn(Register(reg), Register(reg + 1), contextPtr, offset); + } + else + { + mask &= ~(1u << reg); + + asm.LdrRiUn(Register(reg), contextPtr, offset); + } + } + } + + private void LoadFpSimdFromContext(ref Assembler asm, uint mask, int baseOffset) + { + Operand contextPtr = Register(RegisterAllocator.FixedContextRegister); + + while (mask != 0) + { + int reg = BitOperations.TrailingZeroCount(mask); + int offset = baseOffset + reg * 16; + + mask &= ~(1u << reg); + + asm.LdrRiUn(Register(reg, OperandType.V128), contextPtr, offset); + } + } + + private void LoadPStateFromContext(ref Assembler asm, uint mask, int baseOffset) + { + if (mask == 0) + { + return; + } + + Operand contextPtr = Register(RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = RegisterAllocator.AllocateTempGprRegisterScoped(); + + asm.LdrRiUn(tempRegister.Operand, contextPtr, baseOffset); + asm.MsrNzcv(tempRegister.Operand); + } + + private void StoreGprToContext(ref Assembler asm, uint mask, int baseOffset) + { + Operand contextPtr = Register(RegisterAllocator.FixedContextRegister); + + while (mask != 0) + { + int reg = BitOperations.TrailingZeroCount(mask); + int offset = baseOffset + reg * 8; + + if (reg < 31 && (mask & (2u << reg)) != 0 && offset < RegisterSaveRestore.Encodable9BitsOffsetLimit) + { + mask &= ~(3u << reg); + + asm.StpRiUn(Register(reg), Register(reg + 1), contextPtr, offset); + } + else + { + mask &= ~(1u << reg); + + asm.StrRiUn(Register(reg), contextPtr, offset); + } + } + } + + private void StoreFpSimdToContext(ref Assembler asm, uint mask, int baseOffset) + { + Operand contextPtr = Register(RegisterAllocator.FixedContextRegister); + + while (mask != 0) + { + int reg = BitOperations.TrailingZeroCount(mask); + int offset = baseOffset + reg * 16; + + mask &= ~(1u << reg); + + asm.StrRiUn(Register(reg, OperandType.V128), contextPtr, offset); + } + } + + private void StorePStateToContext(ref Assembler asm, uint mask, int baseOffset) + { + if (mask == 0) + { + return; + } + + Operand contextPtr = Register(RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempRegister2 = RegisterAllocator.AllocateTempGprRegisterScoped(); + + asm.LdrRiUn(tempRegister.Operand, contextPtr, baseOffset); + asm.MrsNzcv(tempRegister2.Operand); + asm.And(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(0xfffffff)); + asm.Orr(tempRegister.Operand, tempRegister.Operand, tempRegister2.Operand); + asm.StrRiUn(tempRegister.Operand, contextPtr, baseOffset); + } + } + + public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, IntPtr dispatchStubPtr, bool isThumb) + { + MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb); + + Dictionary<ulong, int> targets = new(); + + CodeWriter writer = new(); + RegisterAllocator regAlloc = new(); + Assembler asm = new(writer); + CodeGenContext cgContext = new(writer, asm, regAlloc, memoryManager.Type, isThumb); + ArmCondition lastCondition = ArmCondition.Al; + int lastConditionIp = 0; + + // Required for load/store to context. + regAlloc.EnsureTempGprRegisters(2); + + ulong pc = address; + + for (int blockIndex = 0; blockIndex < multiBlock.Blocks.Count; blockIndex++) + { + Block block = multiBlock.Blocks[blockIndex]; + + Debug.Assert(block.Address == pc); + + targets.Add(pc, writer.InstructionPointer); + + for (int index = 0; index < block.Instructions.Count; index++) + { + InstInfo instInfo = block.Instructions[index]; + + if (index < block.Instructions.Count - 1) + { + cgContext.SetNextInstruction(block.Instructions[index + 1]); + } + else + { + cgContext.SetNextInstruction(default); + } + + SetConditionalStart(cgContext, ref lastCondition, ref lastConditionIp, instInfo.Name, instInfo.Flags, instInfo.Encoding); + + if (block.IsLoopEnd && index == block.Instructions.Count - 1) + { + // If this is a loop, the code might run for a long time uninterrupted. + // We insert a "sync point" here to ensure the loop can be interrupted if needed. + + cgContext.AddPendingSyncPoint(); + + asm.B(0); + } + + cgContext.SetPc((uint)pc); + + instInfo.EmitFunc(cgContext, instInfo.Encoding); + + if (cgContext.ConsumeNzcvModified()) + { + ForceConditionalEnd(cgContext, ref lastCondition, lastConditionIp); + } + + cgContext.UpdateItState(); + + pc += instInfo.Flags.HasFlag(InstFlags.Thumb16) ? 2UL : 4UL; + } + + if (Decoder<InstEmit>.WritesToPC(block.Instructions[^1].Encoding, block.Instructions[^1].Name, block.Instructions[^1].Flags, block.IsThumb)) + { + // If the block ends with a PC register write, then we have a branch from register. + + InstEmitCommon.SetThumbFlag(cgContext, regAlloc.RemapGprRegister(RegisterUtils.PcRegister)); + + cgContext.AddPendingIndirectBranch(block.Instructions[^1].Name, RegisterUtils.PcRegister); + + asm.B(0); + } + + ForceConditionalEnd(cgContext, ref lastCondition, lastConditionIp); + } + + RegisterSaveRestore rsr = new( + regAlloc.UsedGprsMask & AbiConstants.GprCalleeSavedRegsMask, + regAlloc.UsedFpSimdMask & AbiConstants.FpSimdCalleeSavedRegsMask, + OperandType.FP64, + multiBlock.HasHostCall, + multiBlock.HasHostCall ? CalculateStackSizeForCallSpill(regAlloc.UsedGprsMask, regAlloc.UsedFpSimdMask, UsablePStateMask) : 0); + + TailMerger tailMerger = new(); + + Context context = new(writer, regAlloc, memoryManager.Type, tailMerger, funcTable, rsr, dispatchStubPtr, memoryManager.PageTablePointer); + + InstInfo lastInstruction = multiBlock.Blocks[^1].Instructions[^1]; + bool lastInstIsConditional = GetCondition(lastInstruction, isThumb) != ArmCondition.Al; + + if (multiBlock.IsTruncated || lastInstIsConditional || lastInstruction.Name.IsCall() || IsConditionalBranch(lastInstruction)) + { + WriteTailCallConstant(context, ref asm, (uint)pc); + } + + IEnumerable<PendingBranch> pendingBranches = cgContext.GetPendingBranches(); + + foreach (PendingBranch pendingBranch in pendingBranches) + { + RewriteBranchInstructionWithTarget(context, pendingBranch, targets); + } + + tailMerger.WriteReturn(writer, context.WriteEpilogueWithoutContext); + + context.WritePrologueAt(0); + + return new(writer.AsByteSpan(), (int)(pc - address)); + } + + private static int CalculateStackSizeForCallSpill(uint gprUseMask, uint fpSimdUseMask, uint pStateUseMask) + { + // Note that we don't discard callee saved FP/SIMD register because only the lower 64 bits is callee saved, + // so if the function is using the full register, that won't be enough. + // We could do better, but it's likely not worth it since this case happens very rarely in practice. + + return BitOperations.PopCount(gprUseMask & ~AbiConstants.GprCalleeSavedRegsMask) * 8 + + BitOperations.PopCount(fpSimdUseMask) * 16 + + (pStateUseMask != 0 ? 8 : 0); + } + + private static void SetConditionalStart( + CodeGenContext context, + ref ArmCondition condition, + ref int instructionPointer, + InstName name, + InstFlags flags, + uint encoding) + { + if (!context.ConsumeItCondition(out ArmCondition currentCond)) + { + currentCond = GetCondition(name, flags, encoding, context.IsThumb); + } + + if (currentCond != condition) + { + WriteConditionalEnd(context, condition, instructionPointer); + + condition = currentCond; + + if (currentCond != ArmCondition.Al) + { + instructionPointer = context.CodeWriter.InstructionPointer; + context.Arm64Assembler.B(currentCond.Invert(), 0); + } + } + } + + private static bool IsConditionalBranch(in InstInfo instInfo) + { + return instInfo.Name == InstName.B && (ArmCondition)(instInfo.Encoding >> 28) != ArmCondition.Al; + } + + private static ArmCondition GetCondition(in InstInfo instInfo, bool isThumb) + { + return GetCondition(instInfo.Name, instInfo.Flags, instInfo.Encoding, isThumb); + } + + private static ArmCondition GetCondition(InstName name, InstFlags flags, uint encoding, bool isThumb) + { + // For branch, we handle conditional execution on the instruction itself. + bool hasCond = flags.HasFlag(InstFlags.Cond) && !CanHandleConditionalInstruction(name, encoding, isThumb); + + return hasCond ? (ArmCondition)(encoding >> 28) : ArmCondition.Al; + } + + private static bool CanHandleConditionalInstruction(InstName name, uint encoding, bool isThumb) + { + if (name == InstName.B) + { + return true; + } + + // We can use CSEL for conditional MOV from registers, as long the instruction is not setting flags. + // We don't handle thumb right now because the condition comes from the IT block which would be more complicated to handle. + if (name == InstName.MovR && !isThumb && (encoding & (1u << 20)) == 0) + { + return true; + } + + return false; + } + + private static void ForceConditionalEnd(CodeGenContext context, ref ArmCondition condition, int instructionPointer) + { + WriteConditionalEnd(context, condition, instructionPointer); + + condition = ArmCondition.Al; + } + + private static void WriteConditionalEnd(CodeGenContext context, ArmCondition condition, int instructionPointer) + { + if (condition != ArmCondition.Al) + { + int delta = context.CodeWriter.InstructionPointer - instructionPointer; + uint branchInst = context.CodeWriter.ReadInstructionAt(instructionPointer) | (((uint)delta & 0x7ffff) << 5); + Debug.Assert((int)((branchInst & ~0x1fu) << 8) >> 11 == delta * 4); + + context.CodeWriter.WriteInstructionAt(instructionPointer, branchInst); + } + } + + private static void RewriteBranchInstructionWithTarget(in Context context, in PendingBranch pendingBranch, Dictionary<ulong, int> targets) + { + switch (pendingBranch.BranchType) + { + case BranchType.Branch: + RewriteBranchInstructionWithTarget(context, pendingBranch.Name, pendingBranch.TargetAddress, pendingBranch.WriterPointer, targets); + break; + case BranchType.Call: + RewriteCallInstructionWithTarget(context, pendingBranch.TargetAddress, pendingBranch.NextAddress, pendingBranch.WriterPointer); + break; + case BranchType.IndirectBranch: + RewriteIndirectBranchInstructionWithTarget(context, pendingBranch.Name, pendingBranch.TargetAddress, pendingBranch.WriterPointer); + break; + case BranchType.TableBranchByte: + case BranchType.TableBranchHalfword: + RewriteTableBranchInstructionWithTarget( + context, + pendingBranch.BranchType == BranchType.TableBranchHalfword, + pendingBranch.TargetAddress, + pendingBranch.NextAddress, + pendingBranch.WriterPointer); + break; + case BranchType.IndirectCall: + RewriteIndirectCallInstructionWithTarget(context, pendingBranch.TargetAddress, pendingBranch.NextAddress, pendingBranch.WriterPointer); + break; + case BranchType.SyncPoint: + case BranchType.SoftwareInterrupt: + case BranchType.ReadCntpct: + RewriteHostCall(context, pendingBranch.Name, pendingBranch.BranchType, pendingBranch.TargetAddress, pendingBranch.NextAddress, pendingBranch.WriterPointer); + break; + default: + Debug.Fail($"Invalid branch type '{pendingBranch.BranchType}'"); + break; + } + } + + private static void RewriteBranchInstructionWithTarget(in Context context, InstName name, uint targetAddress, int branchIndex, Dictionary<ulong, int> targets) + { + CodeWriter writer = context.Writer; + Assembler asm = new(writer); + + int delta; + int targetIndex; + uint encoding = writer.ReadInstructionAt(branchIndex); + + if (encoding == 0x14000000) + { + // Unconditional branch. + + if (targets.TryGetValue(targetAddress, out targetIndex)) + { + delta = targetIndex - branchIndex; + + if (delta >= -Encodable26BitsOffsetLimit && delta < Encodable26BitsOffsetLimit) + { + writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff)); + + return; + } + } + + targetIndex = writer.InstructionPointer; + delta = targetIndex - branchIndex; + + writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff)); + WriteTailCallConstant(context, ref asm, targetAddress); + } + else + { + // Conditional branch. + + uint branchMask = 0x7ffff; + int branchMax = (int)(branchMask + 1) / 2; + + if (targets.TryGetValue(targetAddress, out targetIndex)) + { + delta = targetIndex - branchIndex; + + if (delta >= -branchMax && delta < branchMax) + { + writer.WriteInstructionAt(branchIndex, encoding | (uint)((delta & branchMask) << 5)); + + return; + } + } + + targetIndex = writer.InstructionPointer; + delta = targetIndex - branchIndex; + + if (delta >= -branchMax && delta < branchMax) + { + writer.WriteInstructionAt(branchIndex, encoding | (uint)((delta & branchMask) << 5)); + WriteTailCallConstant(context, ref asm, targetAddress); + } + else + { + // If the branch target is too far away, we use a regular unconditional branch + // instruction instead which has a much higher range. + // We branch directly to the end of the function, where we put the conditional branch, + // and then branch back to the next instruction or return the branch target depending + // on the branch being taken or not. + + uint branchInst = 0x14000000u | ((uint)delta & 0x3ffffff); + Debug.Assert((int)(branchInst << 6) >> 4 == delta * 4); + + writer.WriteInstructionAt(branchIndex, branchInst); + + int movedBranchIndex = writer.InstructionPointer; + + writer.WriteInstruction(0u); // Placeholder + asm.B((branchIndex + 1 - writer.InstructionPointer) * 4); + + delta = writer.InstructionPointer - movedBranchIndex; + + writer.WriteInstructionAt(movedBranchIndex, encoding | (uint)((delta & branchMask) << 5)); + WriteTailCallConstant(context, ref asm, targetAddress); + } + } + + Debug.Assert(name == InstName.B || name == InstName.Cbnz, $"Unknown branch instruction \"{name}\"."); + } + + private static void RewriteCallInstructionWithTarget(in Context context, uint targetAddress, uint nextAddress, int branchIndex) + { + CodeWriter writer = context.Writer; + Assembler asm = new(writer); + + WriteBranchToCurrentPosition(context, branchIndex); + + asm.Mov(context.RegisterAllocator.RemapGprRegister(RegisterUtils.LrRegister), nextAddress); + + context.StoreToContext(); + InstEmitFlow.WriteCallWithGuestAddress( + writer, + ref asm, + context.RegisterAllocator, + context.TailMerger, + context.WriteEpilogueWithoutContext, + context.FuncTable, + context.DispatchStubPointer, + context.GetReservedStackOffset(), + nextAddress, + InstEmitCommon.Const((int)targetAddress)); + context.LoadFromContext(); + + // Branch back to the next instruction (after the call). + asm.B((branchIndex + 1 - writer.InstructionPointer) * 4); + } + + private static void RewriteIndirectBranchInstructionWithTarget(in Context context, InstName name, uint targetRegister, int branchIndex) + { + CodeWriter writer = context.Writer; + Assembler asm = new(writer); + + WriteBranchToCurrentPosition(context, branchIndex); + + using ScopedRegister target = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + asm.And(target.Operand, context.RegisterAllocator.RemapGprRegister((int)targetRegister), InstEmitCommon.Const(~1)); + + context.StoreToContext(); + + if ((name == InstName.Bx && targetRegister == RegisterUtils.LrRegister) || + name == InstName.Ldm || + name == InstName.Ldmda || + name == InstName.Ldmdb || + name == InstName.Ldmib) + { + // Arm32 does not have a return instruction, instead returns are implemented + // either using BX LR (for leaf functions), or POP { ... PC }. + + asm.Mov(Register(0), target.Operand); + + context.TailMerger.AddUnconditionalReturn(writer, asm); + } + else + { + InstEmitFlow.WriteCallWithGuestAddress( + writer, + ref asm, + context.RegisterAllocator, + context.TailMerger, + context.WriteEpilogueWithoutContext, + context.FuncTable, + context.DispatchStubPointer, + context.GetReservedStackOffset(), + 0u, + target.Operand, + isTail: true); + } + } + + private static void RewriteTableBranchInstructionWithTarget(in Context context, bool halfword, uint rn, uint rm, int branchIndex) + { + CodeWriter writer = context.Writer; + Assembler asm = new(writer); + + WriteBranchToCurrentPosition(context, branchIndex); + + using ScopedRegister target = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + asm.Add( + target.Operand, + context.RegisterAllocator.RemapGprRegister((int)rn), + context.RegisterAllocator.RemapGprRegister((int)rm), + ArmShiftType.Lsl, + halfword ? 1 : 0); + + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, asm, target.Operand, target.Operand); + + if (halfword) + { + asm.LdrhRiUn(target.Operand, target.Operand, 0); + } + else + { + asm.LdrbRiUn(target.Operand, target.Operand, 0); + } + + asm.Add(target.Operand, context.RegisterAllocator.RemapGprRegister(RegisterUtils.PcRegister), target.Operand, ArmShiftType.Lsl, 1); + + context.StoreToContext(); + + InstEmitFlow.WriteCallWithGuestAddress( + writer, + ref asm, + context.RegisterAllocator, + context.TailMerger, + context.WriteEpilogueWithoutContext, + context.FuncTable, + context.DispatchStubPointer, + context.GetReservedStackOffset(), + 0u, + target.Operand, + isTail: true); + } + + private static void RewriteIndirectCallInstructionWithTarget(in Context context, uint targetRegister, uint nextAddress, int branchIndex) + { + CodeWriter writer = context.Writer; + Assembler asm = new(writer); + + WriteBranchToCurrentPosition(context, branchIndex); + + using ScopedRegister target = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + asm.And(target.Operand, context.RegisterAllocator.RemapGprRegister((int)targetRegister), InstEmitCommon.Const(~1)); + asm.Mov(context.RegisterAllocator.RemapGprRegister(RegisterUtils.LrRegister), nextAddress); + + context.StoreToContext(); + InstEmitFlow.WriteCallWithGuestAddress( + writer, + ref asm, + context.RegisterAllocator, + context.TailMerger, + context.WriteEpilogueWithoutContext, + context.FuncTable, + context.DispatchStubPointer, + context.GetReservedStackOffset(), + nextAddress & ~1u, + target.Operand); + context.LoadFromContext(); + + // Branch back to the next instruction (after the call). + asm.B((branchIndex + 1 - writer.InstructionPointer) * 4); + } + + private static void RewriteHostCall(in Context context, InstName name, BranchType type, uint imm, uint pc, int branchIndex) + { + CodeWriter writer = context.Writer; + Assembler asm = new(writer); + + uint encoding = writer.ReadInstructionAt(branchIndex); + int targetIndex = writer.InstructionPointer; + int delta = targetIndex - branchIndex; + + writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff)); + + switch (type) + { + case BranchType.SyncPoint: + InstEmitSystem.WriteSyncPoint(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset()); + break; + case BranchType.SoftwareInterrupt: + context.StoreToContext(); + switch (name) + { + case InstName.Bkpt: + InstEmitSystem.WriteBkpt(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset(), pc, imm); + break; + case InstName.Svc: + InstEmitSystem.WriteSvc(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset(), pc, imm); + break; + case InstName.Udf: + InstEmitSystem.WriteUdf(context.Writer, context.RegisterAllocator, context.TailMerger, context.GetReservedStackOffset(), pc, imm); + break; + } + context.LoadFromContext(); + break; + case BranchType.ReadCntpct: + InstEmitSystem.WriteReadCntpct(context.Writer, context.RegisterAllocator, context.GetReservedStackOffset(), (int)imm, (int)pc); + break; + default: + Debug.Fail($"Invalid branch type '{type}'"); + break; + } + + // Branch back to the next instruction. + asm.B((branchIndex + 1 - writer.InstructionPointer) * 4); + } + + private static void WriteBranchToCurrentPosition(in Context context, int branchIndex) + { + CodeWriter writer = context.Writer; + + int targetIndex = writer.InstructionPointer; + + if (branchIndex + 1 == targetIndex) + { + writer.RemoveLastInstruction(); + } + else + { + uint encoding = writer.ReadInstructionAt(branchIndex); + int delta = targetIndex - branchIndex; + + writer.WriteInstructionAt(branchIndex, encoding | (uint)(delta & 0x3ffffff)); + } + } + + private static void WriteTailCallConstant(in Context context, ref Assembler asm, uint address) + { + context.StoreToContext(); + InstEmitFlow.WriteCallWithGuestAddress( + context.Writer, + ref asm, + context.RegisterAllocator, + context.TailMerger, + context.WriteEpilogueWithoutContext, + context.FuncTable, + context.DispatchStubPointer, + context.GetReservedStackOffset(), + 0u, + InstEmitCommon.Const((int)address), + isTail: true); + } + + private static Operand Register(int register, OperandType type = OperandType.I64) + { + return new Operand(register, RegisterType.Integer, type); + } + + public static void PrintStats() + { + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmit.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmit.cs new file mode 100644 index 00000000..48891932 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmit.cs @@ -0,0 +1,8502 @@ +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + class InstEmit : IInstEmit + { + public static void AdcIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.AdcI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void AdcIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.AdcI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void AdcRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AdcR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void AdcRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdnb16w3 inst = new(encoding); + + InstEmitAlu.AdcR(context, inst.Rdn, inst.Rdn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void AdcRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AdcR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void AdcRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AdcRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void AddIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void AddIT1(CodeGenContext context, uint encoding) + { + InstImm3b22w3Rnb19w3Rdb16w3 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, inst.Rn, inst.Imm3, !context.InITBlock); + } + + public static void AddIT2(CodeGenContext context, uint encoding) + { + InstRdnb24w3Imm8b16w8 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rdn, inst.Rdn, inst.Imm8, !context.InITBlock); + } + + public static void AddIT3(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void AddIT4(CodeGenContext context, uint encoding) + { + InstIb26w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, inst.Rn, ImmUtils.CombineImmU12(inst.Imm8, inst.Imm3, inst.I), false); + } + + public static void AddRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AddR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void AddRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rdb16w3 inst = new(encoding); + + InstEmitAlu.AddR(context, inst.Rd, inst.Rn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void AddRT2(CodeGenContext context, uint encoding) + { + InstDnb23w1Rmb19w4Rdnb16w3 inst = new(encoding); + + uint rdn = (inst.Dn << 3) | inst.Rdn; + + InstEmitAlu.AddR(context, rdn, rdn, inst.Rm, 0, 0, false); + } + + public static void AddRT3(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AddR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void AddRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AddRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void AddSpIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, RegisterUtils.SpRegister, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void AddSpIT1(CodeGenContext context, uint encoding) + { + InstRdb24w3Imm8b16w8 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, RegisterUtils.SpRegister, inst.Imm8 << 2, false); + } + + public static void AddSpIT2(CodeGenContext context, uint encoding) + { + InstImm7b16w7 inst = new(encoding); + + InstEmitAlu.AddI(context, RegisterUtils.SpRegister, RegisterUtils.SpRegister, inst.Imm7 << 2, false); + } + + public static void AddSpIT3(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, RegisterUtils.SpRegister, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void AddSpIT4(CodeGenContext context, uint encoding) + { + InstIb26w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.AddI(context, inst.Rd, RegisterUtils.SpRegister, ImmUtils.CombineImmU12(inst.Imm8, inst.Imm3, inst.I), false); + } + + public static void AddSpRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AddR(context, inst.Rd, RegisterUtils.SpRegister, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void AddSpRT1(CodeGenContext context, uint encoding) + { + InstDmb23w1Rdmb16w3 inst = new(encoding); + + uint rdm = inst.Rdm | (inst.Dm << 3); + + InstEmitAlu.AddR(context, rdm, RegisterUtils.SpRegister, rdm, 0, 0, false); + } + + public static void AddSpRT2(CodeGenContext context, uint encoding) + { + InstRmb19w4 inst = new(encoding); + + InstEmitAlu.AddR(context, RegisterUtils.SpRegister, RegisterUtils.SpRegister, inst.Rm, 0, 0, false); + } + + public static void AddSpRT3(CodeGenContext context, uint encoding) + { + InstSb20w1Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AddR(context, inst.Rd, RegisterUtils.SpRegister, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void AdrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.Adr(context, inst.Rd, ImmUtils.ExpandImm(inst.Imm12), true); + } + + public static void AdrA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.Adr(context, inst.Rd, ImmUtils.ExpandImm(inst.Imm12), false); + } + + public static void AdrT1(CodeGenContext context, uint encoding) + { + InstRdb24w3Imm8b16w8 inst = new(encoding); + + InstEmitAlu.Adr(context, inst.Rd, inst.Imm8 << 2, true); + } + + public static void AdrT2(CodeGenContext context, uint encoding) + { + InstIb26w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.Adr(context, inst.Rd, ImmUtils.CombineImmU12(inst.Imm8, inst.Imm3, inst.I), false); + } + + public static void AdrT3(CodeGenContext context, uint encoding) + { + InstIb26w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.Adr(context, inst.Rd, ImmUtils.CombineImmU12(inst.Imm8, inst.Imm3, inst.I), true); + } + + public static void AesdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aesd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AesdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aesd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AeseA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aese(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AeseT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aese(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AesimcA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aesimc(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AesimcT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aesimc(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AesmcA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aesmc(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AesmcT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCrypto.Aesmc(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void AndIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.AndI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12), inst.S != 0); + } + + public static void AndIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.AndI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void AndRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AndR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void AndRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdnb16w3 inst = new(encoding); + + InstEmitAlu.AndR(context, inst.Rdn, inst.Rdn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void AndRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AndR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void AndRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.AndRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void BA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm24b0w24 inst = new(encoding); + + InstEmitFlow.B(context, ImmUtils.ExtractSImm24Times4(inst.Imm24), (ArmCondition)inst.Cond); + } + + public static void BT1(CodeGenContext context, uint encoding) + { + InstCondb24w4Imm8b16w8 inst = new(encoding); + + InstEmitFlow.B(context, ImmUtils.ExtractT16SImm8Times2(inst.Imm8), (ArmCondition)inst.Cond); + } + + public static void BT2(CodeGenContext context, uint encoding) + { + InstImm11b16w11 inst = new(encoding); + + InstEmitFlow.B(context, ImmUtils.ExtractT16SImm11Times2(inst.Imm11), ArmCondition.Al); + } + + public static void BT3(CodeGenContext context, uint encoding) + { + InstSb26w1Condb22w4Imm6b16w6J1b13w1J2b11w1Imm11b0w11 inst = new(encoding); + + InstEmitFlow.B(context, ImmUtils.CombineSImm20Times2(inst.Imm11, inst.Imm6, inst.J1, inst.J2, inst.S), (ArmCondition)inst.Cond); + } + + public static void BT4(CodeGenContext context, uint encoding) + { + InstSb26w1Imm10b16w10J1b13w1J2b11w1Imm11b0w11 inst = new(encoding); + + InstEmitFlow.B(context, ImmUtils.CombineSImm24Times2(inst.Imm11, inst.Imm10, inst.J1, inst.J2, inst.S), ArmCondition.Al); + } + + public static void BfcA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Msbb16w5Rdb12w4Lsbb7w5 inst = new(encoding); + + InstEmitBit.Bfc(context, inst.Rd, inst.Lsb, inst.Msb); + } + + public static void BfcT1(CodeGenContext context, uint encoding) + { + InstImm3b12w3Rdb8w4Imm2b6w2Msbb0w5 inst = new(encoding); + + InstEmitBit.Bfc(context, inst.Rd, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.Msb); + } + + public static void BfiA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Msbb16w5Rdb12w4Lsbb7w5Rnb0w4 inst = new(encoding); + + InstEmitBit.Bfi(context, inst.Rd, inst.Rn, inst.Lsb, inst.Msb); + } + + public static void BfiT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Rdb8w4Imm2b6w2Msbb0w5 inst = new(encoding); + + InstEmitBit.Bfi(context, inst.Rd, inst.Rn, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.Msb); + } + + public static void BicIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.BicI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12), inst.S != 0); + } + + public static void BicIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.BicI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void BicRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.BicR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void BicRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdnb16w3 inst = new(encoding); + + InstEmitAlu.BicR(context, inst.Rdn, inst.Rdn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void BicRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.BicR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void BicRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.BicRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void BkptA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm12b8w12Imm4b0w4 inst = new(encoding); + + InstEmitSystem.Bkpt(context, ImmUtils.CombineImmU16(inst.Imm12, inst.Imm4)); + } + + public static void BkptT1(CodeGenContext context, uint encoding) + { + InstImm8b16w8 inst = new(encoding); + + InstEmitSystem.Bkpt(context, inst.Imm8); + } + + public static void BlxRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rmb0w4 inst = new(encoding); + + InstEmitFlow.Blx(context, inst.Rm, false); + } + + public static void BlxRT1(CodeGenContext context, uint encoding) + { + InstRmb19w4 inst = new(encoding); + + InstEmitFlow.Blx(context, inst.Rm, true); + } + + public static void BlIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm24b0w24 inst = new(encoding); + + InstEmitFlow.Bl(context, ImmUtils.ExtractSImm24Times4(inst.Imm24), false, false); + } + + public static void BlIA2(CodeGenContext context, uint encoding) + { + InstHb24w1Imm24b0w24 inst = new(encoding); + + InstEmitFlow.Bl(context, ImmUtils.ExtractSImm24Times4(inst.Imm24) | ((int)inst.H << 1), false, true); + } + + public static void BlIT1(CodeGenContext context, uint encoding) + { + InstSb26w1Imm10b16w10J1b13w1J2b11w1Imm11b0w11 inst = new(encoding); + + InstEmitFlow.Bl(context, ImmUtils.CombineSImm24Times2(inst.Imm11, inst.Imm10, inst.J1, inst.J2, inst.S), true, true); + } + + public static void BlIT2(CodeGenContext context, uint encoding) + { + InstSb26w1Imm10hb16w10J1b13w1J2b11w1Imm10lb1w10Hb0w1 inst = new(encoding); + + InstEmitFlow.Bl(context, ImmUtils.CombineSImm24Times4(inst.Imm10l, inst.Imm10h, inst.J1, inst.J2, inst.S), true, false); + } + + public static void BxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rmb0w4 inst = new(encoding); + + InstEmitFlow.Bx(context, inst.Rm); + } + + public static void BxT1(CodeGenContext context, uint encoding) + { + InstRmb19w4 inst = new(encoding); + + InstEmitFlow.Bx(context, inst.Rm); + } + + public static void BxjA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rmb0w4 inst = new(encoding); + + InstEmitFlow.Bx(context, inst.Rm); + } + + public static void BxjT1(CodeGenContext context, uint encoding) + { + InstRmb16w4 inst = new(encoding); + + InstEmitFlow.Bx(context, inst.Rm); + } + + public static void CbnzT1(CodeGenContext context, uint encoding) + { + InstOpb27w1Ib25w1Imm5b19w5Rnb16w3 inst = new(encoding); + + InstEmitFlow.Cbnz(context, inst.Rn, (int)((inst.Imm5 << 1) | (inst.I << 6)), inst.Op != 0); + } + + public static void ClrbhbA1(CodeGenContext context, uint encoding) + { + _ = new InstCondb28w4(encoding); + + throw new NotImplementedException(); + } + + public static void ClrbhbT1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void ClrexA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Clrex(); + } + + public static void ClrexT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Clrex(); + } + + public static void ClzA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Clz(context, inst.Rd, inst.Rm); + } + + public static void ClzT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Clz(context, inst.Rd, inst.Rm); + } + + public static void CmnIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.CmnI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm12)); + } + + public static void CmnIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Rnb16w4Imm3b12w3Imm8b0w8 inst = new(encoding); + + InstEmitAlu.CmnI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I)); + } + + public static void CmnRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.CmnR(context, inst.Rn, inst.Rm, inst.Stype, inst.Imm5); + } + + public static void CmnRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rnb16w3 inst = new(encoding); + + InstEmitAlu.CmnR(context, inst.Rn, inst.Rm, 0, 0); + } + + public static void CmnRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.CmnR(context, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void CmnRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.CmnRr(context, inst.Rn, inst.Rm, inst.Stype, inst.Rs); + } + + public static void CmpIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.CmpI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm12)); + } + + public static void CmpIT1(CodeGenContext context, uint encoding) + { + InstRnb24w3Imm8b16w8 inst = new(encoding); + + InstEmitAlu.CmpI(context, inst.Rn, inst.Imm8); + } + + public static void CmpIT2(CodeGenContext context, uint encoding) + { + InstIb26w1Rnb16w4Imm3b12w3Imm8b0w8 inst = new(encoding); + + InstEmitAlu.CmpI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I)); + } + + public static void CmpRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.CmpR(context, inst.Rn, inst.Rm, inst.Stype, inst.Imm5); + } + + public static void CmpRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rnb16w3 inst = new(encoding); + + InstEmitAlu.CmpR(context, inst.Rn, inst.Rm, 0, 0); + } + + public static void CmpRT2(CodeGenContext context, uint encoding) + { + InstNb23w1Rmb19w4Rnb16w3 inst = new(encoding); + + InstEmitAlu.CmpR(context, inst.Rn | (inst.N << 3), inst.Rm, 0, 0); + } + + public static void CmpRT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.CmpR(context, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void CmpRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.CmpRr(context, inst.Rn, inst.Rm, inst.Stype, inst.Rs); + } + + public static void CpsA1(CodeGenContext context, uint encoding) + { + InstImodb18w2Mb17w1Ab8w1Ib7w1Fb6w1Modeb0w5 inst = new(encoding); + + InstEmitSystem.Cps(context, inst.Imod, inst.M, inst.A, inst.I, inst.F, inst.Mode); + } + + public static void CpsT1(CodeGenContext context, uint encoding) + { + InstImb20w1Ab18w1Ib17w1Fb16w1 inst = new(encoding); + + InstEmitSystem.Cps(context, inst.Im, 0, inst.A, inst.I, inst.F, 0); + } + + public static void CpsT2(CodeGenContext context, uint encoding) + { + InstImodb9w2Mb8w1Ab7w1Ib6w1Fb5w1Modeb0w5 inst = new(encoding); + + InstEmitSystem.Cps(context, inst.Imod, inst.M, inst.A, inst.I, inst.F, inst.Mode); + } + + public static void Crc32A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Szb21w2Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitCrc32.Crc32(context, inst.Rd, inst.Rn, inst.Rm, inst.Sz); + } + + public static void Crc32T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Szb4w2Rmb0w4 inst = new(encoding); + + InstEmitCrc32.Crc32(context, inst.Rd, inst.Rn, inst.Rm, inst.Sz); + } + + public static void Crc32cA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Szb21w2Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitCrc32.Crc32c(context, inst.Rd, inst.Rn, inst.Rm, inst.Sz); + } + + public static void Crc32cT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Szb4w2Rmb0w4 inst = new(encoding); + + InstEmitCrc32.Crc32c(context, inst.Rd, inst.Rn, inst.Rm, inst.Sz); + } + + public static void CsdbA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Csdb(); + } + + public static void CsdbT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Csdb(); + } + + public static void DbgA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Optionb0w4 inst = new(encoding); + + InstEmitSystem.Dbg(context, inst.Option); + } + + public static void DbgT1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + InstEmitSystem.Dbg(context, inst.Option); + } + + public static void Dcps1T1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void Dcps2T1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void Dcps3T1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void DmbA1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + context.Arm64Assembler.Dmb(inst.Option); + } + + public static void DmbT1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + context.Arm64Assembler.Dmb(inst.Option); + } + + public static void DsbA1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + context.Arm64Assembler.Dsb(inst.Option); + } + + public static void DsbT1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + context.Arm64Assembler.Dsb(inst.Option); + } + + public static void EorIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.EorI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12), inst.S != 0); + } + + public static void EorIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.EorI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void EorRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.EorR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void EorRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdnb16w3 inst = new(encoding); + + InstEmitAlu.EorR(context, inst.Rdn, inst.Rdn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void EorRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.EorR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void EorRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.EorRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void EretA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void EretT1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void EsbA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Esb(); + } + + public static void EsbT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Esb(); + } + + public static void FldmxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vldm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void FldmxT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vldm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void FstmxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vstm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void FstmxT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vstm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void HltA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm12b8w12Imm4b0w4 inst = new(encoding); + + InstEmitSystem.Hlt(context, ImmUtils.CombineImmU16(inst.Imm12, inst.Imm4)); + } + + public static void HltT1(CodeGenContext context, uint encoding) + { + InstImm6b16w6 inst = new(encoding); + + InstEmitSystem.Hlt(context, inst.Imm6); + } + + public static void HvcA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void HvcT1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void IsbA1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + context.Arm64Assembler.Isb(inst.Option); + } + + public static void IsbT1(CodeGenContext context, uint encoding) + { + InstOptionb0w4 inst = new(encoding); + + context.Arm64Assembler.Isb(inst.Option); + } + + public static void ItT1(CodeGenContext context, uint encoding) + { + InstFirstcondb20w4Maskb16w4 inst = new(encoding); + + InstEmitFlow.It(context, inst.Firstcond, inst.Mask); + } + + public static void LdaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Lda(context, inst.Rt, inst.Rn); + } + + public static void LdaT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Lda(context, inst.Rt, inst.Rn); + } + + public static void LdabA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldab(context, inst.Rt, inst.Rn); + } + + public static void LdabT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldab(context, inst.Rt, inst.Rn); + } + + public static void LdaexA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaex(context, inst.Rt, inst.Rn); + } + + public static void LdaexT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaex(context, inst.Rt, inst.Rn); + } + + public static void LdaexbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaexb(context, inst.Rt, inst.Rn); + } + + public static void LdaexbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaexb(context, inst.Rt, inst.Rn); + } + + public static void LdaexdA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaexd(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn); + } + + public static void LdaexdT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rt2b8w4 inst = new(encoding); + + InstEmitMemory.Ldaexd(context, inst.Rt, inst.Rt2, inst.Rn); + } + + public static void LdaexhA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaexh(context, inst.Rt, inst.Rn); + } + + public static void LdaexhT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldaexh(context, inst.Rt, inst.Rn); + } + + public static void LdahA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldah(context, inst.Rt, inst.Rn); + } + + public static void LdahT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldah(context, inst.Rt, inst.Rn); + } + + public static void LdcIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdcI(context, inst.Rn, (int)inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdcIT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Wb21w1Rnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdcI(context, inst.Rn, (int)inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdcLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdcL(context, inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdcLT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Wb21w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdcL(context, inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdmA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Ldm(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void LdmT1(CodeGenContext context, uint encoding) + { + InstRnb24w3RegisterListb16w8 inst = new(encoding); + + InstEmitMemory.Ldm(context, inst.Rn, inst.RegisterList, false); + } + + public static void LdmT2(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Pb15w1Mb14w1RegisterListb0w14 inst = new(encoding); + + InstEmitMemory.Ldm(context, inst.Rn, ImmUtils.CombineRegisterList(inst.RegisterList, inst.M, inst.P), inst.W != 0); + } + + public static void LdmdaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Ldmda(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void LdmdbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Ldmdb(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void LdmdbT1(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Pb15w1Mb14w1RegisterListb0w14 inst = new(encoding); + + InstEmitMemory.Ldmdb(context, inst.Rn, ImmUtils.CombineRegisterList(inst.RegisterList, inst.M, inst.P), inst.W != 0); + } + + public static void LdmibA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Ldmib(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void LdmEA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void LdmUA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void LdrbtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrbtI(context, inst.Rt, inst.Rn, (int)inst.Imm12, postIndex: true, inst.U != 0); + } + + public static void LdrbtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrbtR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, postIndex: true, inst.U != 0); + } + + public static void LdrbtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrbtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void LdrbIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrbI(context, inst.Rt, inst.Rn, (int)inst.Imm12, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrbIT1(CodeGenContext context, uint encoding) + { + InstImm5b22w5Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrbI(context, inst.Rt, inst.Rn, (int)inst.Imm5, true, true, false); + } + + public static void LdrbIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrbI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void LdrbIT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrbI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrbLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrbL(context, inst.Rt, inst.Imm12, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrbLT1(CodeGenContext context, uint encoding) + { + InstUb23w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrbL(context, inst.Rt, inst.Imm12, true, inst.U != 0, false); + } + + public static void LdrbRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrbR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrbRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrbR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void LdrbRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrbR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void LdrdIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrdI(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn, ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrdIT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rt2b8w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrdI(context, inst.Rt, inst.Rt2, inst.Rn, inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrdLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrdL(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), true, inst.U != 0, false); + } + + public static void LdrdLT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Wb21w1Rtb12w4Rt2b8w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrdL(context, inst.Rt, inst.Rt2, inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrdRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrdR(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn, inst.Rm, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrexA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldrex(context, inst.Rt, inst.Rn); + } + + public static void LdrexT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.Ldrex(context, inst.Rt, inst.Rn); + } + + public static void LdrexbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldrexb(context, inst.Rt, inst.Rn); + } + + public static void LdrexbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldrexb(context, inst.Rt, inst.Rn); + } + + public static void LdrexdA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldrexd(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn); + } + + public static void LdrexdT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rt2b8w4 inst = new(encoding); + + InstEmitMemory.Ldrexd(context, inst.Rt, inst.Rt2, inst.Rn); + } + + public static void LdrexhA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldrexh(context, inst.Rt, inst.Rn); + } + + public static void LdrexhT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Ldrexh(context, inst.Rt, inst.Rn); + } + + public static void LdrhtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrhtI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), postIndex: true, inst.U != 0); + } + + public static void LdrhtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrhtR(context, inst.Rt, inst.Rn, inst.Rm, postIndex: true, inst.U != 0); + } + + public static void LdrhtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrhtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void LdrhIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrhI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrhIT1(CodeGenContext context, uint encoding) + { + InstImm5b22w5Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrhI(context, inst.Rt, inst.Rn, (int)inst.Imm5 << 1, true, true, false); + } + + public static void LdrhIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrhI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void LdrhIT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrhI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrhLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrhL(context, inst.Rt, ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrhLT1(CodeGenContext context, uint encoding) + { + InstUb23w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrhL(context, inst.Rt, inst.Imm12, true, inst.U != 0, false); + } + + public static void LdrhRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrhR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrhRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrhR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void LdrhRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrhR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void LdrsbtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrsbtI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), postIndex: true, inst.U != 0); + } + + public static void LdrsbtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrsbtR(context, inst.Rt, inst.Rn, inst.Rm, postIndex: true, inst.U != 0); + } + + public static void LdrsbtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrsbtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void LdrsbIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrsbI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrsbIT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrsbI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void LdrsbIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrsbI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrsbLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrsbL(context, inst.Rt, ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrsbLT1(CodeGenContext context, uint encoding) + { + InstUb23w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrsbL(context, inst.Rt, inst.Imm12, true, inst.U != 0, false); + } + + public static void LdrsbRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrsbR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrsbRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrsbR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void LdrsbRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrsbR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void LdrshtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrshtI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), postIndex: true, inst.U != 0); + } + + public static void LdrshtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrshtR(context, inst.Rt, inst.Rn, inst.Rm, postIndex: true, inst.U != 0); + } + + public static void LdrshtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrshtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void LdrshIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrshI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrshIT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrshI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void LdrshIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrshI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrshLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.LdrshL(context, inst.Rt, ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrshLT1(CodeGenContext context, uint encoding) + { + InstUb23w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrshL(context, inst.Rt, inst.Imm12, true, inst.U != 0, false); + } + + public static void LdrshRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrshR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrshRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrshR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void LdrshRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrshR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void LdrtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrtI(context, inst.Rt, inst.Rn, (int)inst.Imm12, postIndex: true, inst.U != 0); + } + + public static void LdrtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrtR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, postIndex: true, inst.U != 0); + } + + public static void LdrtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void LdrIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrI(context, inst.Rt, inst.Rn, (int)inst.Imm12, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrIT1(CodeGenContext context, uint encoding) + { + InstImm5b22w5Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrI(context, inst.Rt, inst.Rn, (int)inst.Imm5 << 2, true, true, false); + } + + public static void LdrIT2(CodeGenContext context, uint encoding) + { + InstRtb24w3Imm8b16w8 inst = new(encoding); + + InstEmitMemory.LdrI(context, inst.Rt, RegisterUtils.SpRegister, (int)inst.Imm8 << 2, true, true, false); + } + + public static void LdrIT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void LdrIT4(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.LdrI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrL(context, inst.Rt, inst.Imm12, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrLT1(CodeGenContext context, uint encoding) + { + InstRtb24w3Imm8b16w8 inst = new(encoding); + + InstEmitMemory.LdrL(context, inst.Rt, inst.Imm8 << 2, true, true, false); + } + + public static void LdrLT2(CodeGenContext context, uint encoding) + { + InstUb23w1Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.LdrL(context, inst.Rt, inst.Imm12, true, inst.U != 0, false); + } + + public static void LdrRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void LdrRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.LdrR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void LdrRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.LdrR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void McrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opc1b21w3Crnb16w4Rtb12w4Coproc0b8w1Opc2b5w3Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mcr(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Crn, inst.Crm, inst.Opc2); + } + + public static void McrT1(CodeGenContext context, uint encoding) + { + InstOpc1b21w3Crnb16w4Rtb12w4Coproc0b8w1Opc2b5w3Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mcr(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Crn, inst.Crm, inst.Opc2); + } + + public static void McrrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rt2b16w4Rtb12w4Coproc0b8w1Opc1b4w4Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mcrr(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Crm); + } + + public static void McrrT1(CodeGenContext context, uint encoding) + { + InstRt2b16w4Rtb12w4Coproc0b8w1Opc1b4w4Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mcrr(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Crm); + } + + public static void MlaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb16w4Rab12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Mla(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra); + } + + public static void MlaT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Mla(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra); + } + + public static void MlsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Mls(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra); + } + + public static void MlsT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Mls(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra); + } + + public static void MovtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm4b16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMove.Movt(context, inst.Rd, ImmUtils.CombineImmU16(inst.Imm12, inst.Imm4)); + } + + public static void MovtT1(CodeGenContext context, uint encoding) + { + InstIb26w1Imm4b16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitMove.Movt(context, inst.Rd, ImmUtils.CombineImmU16(inst.Imm8, inst.Imm3, inst.I, inst.Imm4)); + } + + public static void MovIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMove.MovI(context, inst.Rd, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12), inst.S != 0); + } + + public static void MovIA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm4b16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMove.MovI(context, inst.Rd, ImmUtils.CombineImmU16(inst.Imm12, inst.Imm4), false, false); + } + + public static void MovIT1(CodeGenContext context, uint encoding) + { + InstRdb24w3Imm8b16w8 inst = new(encoding); + + InstEmitMove.MovI(context, inst.Rd, inst.Imm8, false, !context.InITBlock); + } + + public static void MovIT2(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitMove.MovI(context, inst.Rd, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void MovIT3(CodeGenContext context, uint encoding) + { + InstIb26w1Imm4b16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitMove.MovI(context, inst.Rd, ImmUtils.CombineImmU16(inst.Imm8, inst.Imm3, inst.I, inst.Imm4), false, false); + } + + public static void MovRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMove.MovR(context, inst.Cond, inst.Rd, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void MovRT1(CodeGenContext context, uint encoding) + { + InstDb23w1Rmb19w4Rdb16w3 inst = new(encoding); + + InstEmitMove.MovR(context, inst.Rd | (inst.D << 3), inst.Rm, 0, 0, false); + } + + public static void MovRT2(CodeGenContext context, uint encoding) + { + InstOpb27w2Imm5b22w5Rmb19w3Rdb16w3 inst = new(encoding); + + InstEmitMove.MovR(context, inst.Rd, inst.Rm, inst.Op, inst.Imm5, !context.InITBlock); + } + + public static void MovRT3(CodeGenContext context, uint encoding) + { + InstSb20w1Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitMove.MovR(context, inst.Rd, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void MovRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMove.MovRr(context, inst.Rd, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void MovRrT1(CodeGenContext context, uint encoding) + { + InstRsb19w3Rdmb16w3 inst = new(encoding); + + InstEmitMove.MovRr(context, inst.Rdm, inst.Rdm, ((encoding >> 7) & 2) | ((encoding >> 6) & 1), inst.Rs, !context.InITBlock); + } + + public static void MovRrT2(CodeGenContext context, uint encoding) + { + InstStypeb21w2Sb20w1Rmb16w4Rdb8w4Rsb0w4 inst = new(encoding); + + InstEmitMove.MovRr(context, inst.Rd, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void MrcA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opc1b21w3Crnb16w4Rtb12w4Coproc0b8w1Opc2b5w3Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mrc(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Crn, inst.Crm, inst.Opc2); + } + + public static void MrcT1(CodeGenContext context, uint encoding) + { + InstOpc1b21w3Crnb16w4Rtb12w4Coproc0b8w1Opc2b5w3Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mrc(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Crn, inst.Crm, inst.Opc2); + } + + public static void MrrcA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rt2b16w4Rtb12w4Coproc0b8w1Opc1b4w4Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mrrc(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Rt2, inst.Crm); + } + + public static void MrrcT1(CodeGenContext context, uint encoding) + { + InstRt2b16w4Rtb12w4Coproc0b8w1Opc1b4w4Crmb0w4 inst = new(encoding); + + InstEmitSystem.Mrrc(context, encoding, inst.Coproc0 | 0xe, inst.Opc1, inst.Rt, inst.Rt2, inst.Crm); + } + + public static void MrsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rb22w1Rdb12w4 inst = new(encoding); + + InstEmitSystem.Mrs(context, inst.Rd, inst.R != 0); + } + + public static void MrsT1(CodeGenContext context, uint encoding) + { + InstRb20w1Rdb8w4 inst = new(encoding); + + InstEmitSystem.Mrs(context, inst.Rd, inst.R != 0); + } + + public static void MrsBrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rb22w1M1b16w4Rdb12w4Mb8w1 inst = new(encoding); + + InstEmitSystem.MrsBr(context, inst.Rd, inst.M1 | (inst.M << 4), inst.R != 0); + } + + public static void MrsBrT1(CodeGenContext context, uint encoding) + { + InstRb20w1M1b16w4Rdb8w4Mb4w1 inst = new(encoding); + + InstEmitSystem.MrsBr(context, inst.Rd, inst.M1 | (inst.M << 4), inst.R != 0); + } + + public static void MsrBrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rb22w1M1b16w4Mb8w1Rnb0w4 inst = new(encoding); + + InstEmitSystem.MsrBr(context, inst.Rn, inst.M1 | (inst.M << 4), inst.R != 0); + } + + public static void MsrBrT1(CodeGenContext context, uint encoding) + { + InstRb20w1Rnb16w4M1b8w4Mb4w1 inst = new(encoding); + + InstEmitSystem.MsrBr(context, inst.Rn, inst.M1 | (inst.M << 4), inst.R != 0); + } + + public static void MsrIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rb22w1Maskb16w4Imm12b0w12 inst = new(encoding); + + InstEmitSystem.MsrI(context, inst.Imm12, inst.Mask, inst.R != 0); + } + + public static void MsrRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rb22w1Maskb16w4Rnb0w4 inst = new(encoding); + + InstEmitSystem.MsrR(context, inst.Rn, inst.Mask, inst.R != 0); + } + + public static void MsrRT1(CodeGenContext context, uint encoding) + { + InstRb20w1Rnb16w4Maskb8w4 inst = new(encoding); + + InstEmitSystem.MsrR(context, inst.Rn, inst.Mask, inst.R != 0); + } + + public static void MulA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb16w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Mul(context, inst.Rd, inst.Rn, inst.Rm, inst.S != 0); + } + + public static void MulT1(CodeGenContext context, uint encoding) + { + InstRnb19w3Rdmb16w3 inst = new(encoding); + + InstEmitMultiply.Mul(context, inst.Rdm, inst.Rn, inst.Rdm, !context.InITBlock); + } + + public static void MulT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Mul(context, inst.Rd, inst.Rn, inst.Rm, false); + } + + public static void MvnIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMove.MvnI(context, inst.Rd, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12), inst.S != 0); + } + + public static void MvnIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitMove.MvnI(context, inst.Rd, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void MvnRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMove.MvnR(context, inst.Rd, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void MvnRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitMove.MvnR(context, inst.Rd, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void MvnRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitMove.MvnR(context, inst.Rd, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void MvnRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMove.MvnRr(context, inst.Rd, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void NopA1(CodeGenContext context, uint encoding) + { + } + + public static void NopT1(CodeGenContext context, uint encoding) + { + } + + public static void NopT2(CodeGenContext context, uint encoding) + { + } + + public static void OrnIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.OrnI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void OrnRT1(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.OrnR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void OrrIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.OrrI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12), inst.S != 0); + } + + public static void OrrIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.OrrI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void OrrRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.OrrR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void OrrRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdnb16w3 inst = new(encoding); + + InstEmitAlu.OrrR(context, inst.Rdn, inst.Rdn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void OrrRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.OrrR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void OrrRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.OrrRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void PkhA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Imm5b7w5Tbb6w1Rmb0w4 inst = new(encoding); + + InstEmitMove.Pkh(context, inst.Rd, inst.Rn, inst.Rm, inst.Tb != 0, inst.Imm5); + } + + public static void PkhT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Rdb8w4Imm2b6w2Tbb5w1Rmb0w4 inst = new(encoding); + + InstEmitMove.Pkh(context, inst.Rd, inst.Rn, inst.Rm, inst.Tb != 0, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void PldIA1(CodeGenContext context, uint encoding) + { + InstUb23w1Rb22w1Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PldI(context, inst.Rn, inst.Imm12, inst.U != 0, inst.R != 0); + } + + public static void PldIT1(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PldI(context, inst.Rn, inst.Imm12, true, inst.W == 0); + } + + public static void PldIT2(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.PldI(context, inst.Rn, inst.Imm8, false, inst.W == 0); + } + + public static void PldLA1(CodeGenContext context, uint encoding) + { + InstUb23w1Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PldL(context, inst.Imm12, inst.U != 0); + } + + public static void PldLT1(CodeGenContext context, uint encoding) + { + InstUb23w1Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PldL(context, inst.Imm12, inst.U != 0); + } + + public static void PldRA1(CodeGenContext context, uint encoding) + { + InstUb23w1Rb22w1Rnb16w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.PldR(context, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.U != 0, inst.R != 0); + } + + public static void PldRT1(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.PldR(context, inst.Rn, inst.Rm, 0, inst.Imm2, true, inst.W == 0); + } + + public static void PliIA1(CodeGenContext context, uint encoding) + { + InstUb23w1Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PliI(context, inst.Rn, inst.Imm12, inst.U != 0); + } + + public static void PliIT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PliI(context, inst.Rn, inst.Imm12, true); + } + + public static void PliIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.PliI(context, inst.Rn, inst.Imm8, false); + } + + public static void PliIT3(CodeGenContext context, uint encoding) + { + InstUb23w1Imm12b0w12 inst = new(encoding); + + InstEmitMemory.PliL(context, inst.Imm12, inst.U != 0); + } + + public static void PliRA1(CodeGenContext context, uint encoding) + { + InstUb23w1Rnb16w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.PliR(context, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.U != 0); + } + + public static void PliRT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.PliR(context, inst.Rn, inst.Rm, 0, inst.Imm2, true); + } + + public static void PopT1(CodeGenContext context, uint encoding) + { + InstPb24w1RegisterListb16w8 inst = new(encoding); + + InstEmitMemory.Ldm(context, RegisterUtils.SpRegister, inst.RegisterList | (inst.P << RegisterUtils.PcRegister), true); + } + + public static void PssbbA1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void PssbbT1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void PushT1(CodeGenContext context, uint encoding) + { + InstMb24w1RegisterListb16w8 inst = new(encoding); + + InstEmitMemory.Stmdb(context, RegisterUtils.SpRegister, inst.RegisterList | (inst.M << RegisterUtils.LrRegister), true); + } + + public static void QaddA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qadd(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QaddT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qadd(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qadd16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qadd16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qadd8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qadd8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QasxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QasxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QdaddA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qdadd(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QdaddT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qdadd(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QdsubA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qdsub(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QdsubT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qdsub(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QsaxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QsaxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QsubA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsub(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void QsubT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsub(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qsub16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qsub16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qsub8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Qsub8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Qsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void RbitA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Rbit(context, inst.Rd, inst.Rm); + } + + public static void RbitT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Rbit(context, inst.Rd, inst.Rm); + } + + public static void RevA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Rev(context, inst.Rd, inst.Rm); + } + + public static void RevT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitBit.Rev(context, inst.Rd, inst.Rm); + } + + public static void RevT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Rev(context, inst.Rd, inst.Rm); + } + + public static void Rev16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Rev16(context, inst.Rd, inst.Rm); + } + + public static void Rev16T1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitBit.Rev16(context, inst.Rd, inst.Rm); + } + + public static void Rev16T2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Rev16(context, inst.Rd, inst.Rm); + } + + public static void RevshA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Revsh(context, inst.Rd, inst.Rm); + } + + public static void RevshT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitBit.Revsh(context, inst.Rd, inst.Rm); + } + + public static void RevshT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitBit.Revsh(context, inst.Rd, inst.Rm); + } + + public static void RfeA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void RfeT1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void RfeT2(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void RsbIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.RsbI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void RsbIT1(CodeGenContext context, uint encoding) + { + InstRnb19w3Rdb16w3 inst = new(encoding); + + InstEmitAlu.RsbI(context, inst.Rd, inst.Rn, 0, !context.InITBlock); + } + + public static void RsbIT2(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.RsbI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void RsbRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.RsbR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void RsbRT1(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.RsbR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void RsbRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.RsbRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void RscIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.RscI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void RscRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.RscR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void RscRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.RscRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void Sadd16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Sadd16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Sadd8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Sadd8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SasxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SasxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SbA1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void SbT1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void SbcIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.SbcI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void SbcIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.SbcI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void SbcRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SbcR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void SbcRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdnb16w3 inst = new(encoding); + + InstEmitAlu.SbcR(context, inst.Rdn, inst.Rdn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void SbcRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SbcR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void SbcRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SbcRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void SbfxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Widthm1b16w5Rdb12w4Lsbb7w5Rnb0w4 inst = new(encoding); + + InstEmitBit.Sbfx(context, inst.Rd, inst.Rn, inst.Lsb, inst.Widthm1); + } + + public static void SbfxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Rdb8w4Imm2b6w2Widthm1b0w5 inst = new(encoding); + + InstEmitBit.Sbfx(context, inst.Rd, inst.Rn, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.Widthm1); + } + + public static void SdivA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitDivide.Sdiv(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SdivT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitDivide.Sdiv(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SelA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sel(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SelT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Sel(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SetendA1(CodeGenContext context, uint encoding) + { + InstEb9w1 inst = new(encoding); + + InstEmitSystem.Setend(context, inst.E != 0); + } + + public static void SetendT1(CodeGenContext context, uint encoding) + { + InstEb19w1 inst = new(encoding); + + InstEmitSystem.Setend(context, inst.E != 0); + } + + public static void SetpanA1(CodeGenContext context, uint encoding) + { + _ = new InstImm1b9w1(encoding); + + throw new NotImplementedException(); + } + + public static void SetpanT1(CodeGenContext context, uint encoding) + { + _ = new InstImm1b19w1(encoding); + + throw new NotImplementedException(); + } + + public static void SevA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Sev(); + } + + public static void SevT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Sev(); + } + + public static void SevT2(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Sev(); + } + + public static void SevlA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Sevl(); + } + + public static void SevlT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Sevl(); + } + + public static void SevlT2(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Sevl(); + } + + public static void Sha1cA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1c(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1cT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1c(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1hA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1h(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void Sha1hT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1h(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void Sha1mA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1m(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1mT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1m(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1pA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1p(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1pT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1p(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1su0A1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1su0(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1su0T1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1su0(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha1su1A1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1su1(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void Sha1su1T1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha1su1(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void Sha256hA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256h(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha256hT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256h(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha256h2A1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256h2(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha256h2T1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256h2(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha256su0A1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256su0(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void Sha256su0T1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256su0(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void Sha256su1A1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256su1(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Sha256su1T1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonHash.Sha256su1(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void Shadd16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shadd16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shadd8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shadd8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void ShasxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void ShasxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void ShsaxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void ShsaxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shsub16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shsub16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shsub8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Shsub8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Shsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SmcA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void SmcT1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void SmlabbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Mb6w1Nb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlabb(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.N != 0, inst.M != 0); + } + + public static void SmlabbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Nb5w1Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlabb(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.N != 0, inst.M != 0); + } + + public static void SmladA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Mb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlad(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.M != 0); + } + + public static void SmladT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlad(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.M != 0); + } + + public static void SmlalA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdhib16w4Rdlob12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlal(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.S != 0); + } + + public static void SmlalT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlal(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, false); + } + + public static void SmlalbbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdhib16w4Rdlob12w4Rmb8w4Mb6w1Nb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlalbb(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.N != 0, inst.M != 0); + } + + public static void SmlalbbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Nb5w1Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlalbb(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.N != 0, inst.M != 0); + } + + public static void SmlaldA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdhib16w4Rdlob12w4Rmb8w4Mb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlald(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmlaldT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlald(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmlawbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Mb6w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlawb(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.M != 0); + } + + public static void SmlawbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlawb(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.M != 0); + } + + public static void SmlsdA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Mb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlsd(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.M != 0); + } + + public static void SmlsdT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlsd(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.M != 0); + } + + public static void SmlsldA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdhib16w4Rdlob12w4Rmb8w4Mb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smlsld(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmlsldT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smlsld(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmmlaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Rb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smmla(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.R != 0); + } + + public static void SmmlaT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Rb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smmla(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.R != 0); + } + + public static void SmmlsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Rb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smmls(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.R != 0); + } + + public static void SmmlsT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Rb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smmls(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra, inst.R != 0); + } + + public static void SmmulA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Rb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smmul(context, inst.Rd, inst.Rn, inst.Rm, inst.R != 0); + } + + public static void SmmulT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smmul(context, inst.Rd, inst.Rn, inst.Rm, inst.R != 0); + } + + public static void SmuadA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Mb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smuad(context, inst.Rd, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmuadT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smuad(context, inst.Rd, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmulbbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Mb6w1Nb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smulbb(context, inst.Rd, inst.Rn, inst.Rm, inst.N != 0, inst.M != 0); + } + + public static void SmulbbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Nb5w1Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smulbb(context, inst.Rd, inst.Rn, inst.Rm, inst.N != 0, inst.M != 0); + } + + public static void SmullA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdhib16w4Rdlob12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smull(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.S != 0); + } + + public static void SmullT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smull(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, false); + } + + public static void SmulwbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Mb6w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smulwb(context, inst.Rd, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmulwbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smulwb(context, inst.Rd, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmusdA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Mb5w1Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Smusd(context, inst.Rd, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SmusdT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Mb4w1Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Smusd(context, inst.Rd, inst.Rn, inst.Rm, inst.M != 0); + } + + public static void SrsA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void SrsT1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void SrsT2(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void SsatA1(CodeGenContext context, uint encoding) + { + InstCondb28w4SatImmb16w5Rdb12w4Imm5b7w5Shb6w1Rnb0w4 inst = new(encoding); + + InstEmitSaturate.Ssat(context, inst.Rd, inst.SatImm, inst.Rn, inst.Sh != 0, inst.Imm5); + } + + public static void SsatT1(CodeGenContext context, uint encoding) + { + InstShb21w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2SatImmb0w5 inst = new(encoding); + + InstEmitSaturate.Ssat(context, inst.Rd, inst.SatImm, inst.Rn, inst.Sh != 0, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void Ssat16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4SatImmb16w4Rdb12w4Rnb0w4 inst = new(encoding); + + InstEmitSaturate.Ssat16(context, inst.Rd, inst.SatImm, inst.Rn); + } + + public static void Ssat16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4SatImmb0w4 inst = new(encoding); + + InstEmitSaturate.Ssat16(context, inst.Rd, inst.SatImm, inst.Rn); + } + + public static void SsaxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Ssax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SsaxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Ssax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void SsbbA1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void SsbbT1(CodeGenContext context, uint encoding) + { + throw new NotImplementedException(); + } + + public static void Ssub16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Ssub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Ssub16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Ssub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Ssub8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Ssub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Ssub8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Ssub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void StcA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.Stc(context, inst.Rn, (int)inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StcT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Wb21w1Rnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.Stc(context, inst.Rn, (int)inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StlA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stl(context, inst.Rt, inst.Rn); + } + + public static void StlT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Stl(context, inst.Rt, inst.Rn); + } + + public static void StlbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stlb(context, inst.Rt, inst.Rn); + } + + public static void StlbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Stlb(context, inst.Rt, inst.Rn); + } + + public static void StlexA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stlex(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StlexT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Stlex(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StlexbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stlexb(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StlexbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Stlexb(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StlexdA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stlexd(context, inst.Rd, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn); + } + + public static void StlexdT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rt2b8w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Stlexd(context, inst.Rd, inst.Rt, inst.Rt2, inst.Rn); + } + + public static void StlexhA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stlexh(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StlexhT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Stlexh(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StlhA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Stlh(context, inst.Rt, inst.Rn); + } + + public static void StlhT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4 inst = new(encoding); + + InstEmitMemory.Stlh(context, inst.Rt, inst.Rn); + } + + public static void StmA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Stm(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void StmT1(CodeGenContext context, uint encoding) + { + InstRnb24w3RegisterListb16w8 inst = new(encoding); + + InstEmitMemory.Stm(context, inst.Rn, inst.RegisterList, false); + } + + public static void StmT2(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Mb14w1RegisterListb0w14 inst = new(encoding); + + InstEmitMemory.Stm(context, inst.Rn, ImmUtils.CombineRegisterList(inst.RegisterList, inst.M), inst.W != 0); + } + + public static void StmdaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Stmda(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void StmdbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Stmdb(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void StmdbT1(CodeGenContext context, uint encoding) + { + InstWb21w1Rnb16w4Mb14w1RegisterListb0w14 inst = new(encoding); + + InstEmitMemory.Stmdb(context, inst.Rn, ImmUtils.CombineRegisterList(inst.RegisterList, inst.M), inst.W != 0); + } + + public static void StmibA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Wb21w1Rnb16w4RegisterListb0w16 inst = new(encoding); + + InstEmitMemory.Stmib(context, inst.Rn, inst.RegisterList, inst.W != 0); + } + + public static void StmUA1(CodeGenContext context, uint encoding) + { + InstEmitSystem.PrivilegedInstruction(context, encoding); + } + + public static void StrbtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrbtI(context, inst.Rt, inst.Rn, (int)inst.Imm12, postIndex: true, inst.U != 0); + } + + public static void StrbtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrbtR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, postIndex: true, inst.U != 0); + } + + public static void StrbtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrbtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void StrbIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrbI(context, inst.Rt, inst.Rn, (int)inst.Imm12, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrbIT1(CodeGenContext context, uint encoding) + { + InstImm5b22w5Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.StrbI(context, inst.Rt, inst.Rn, (int)inst.Imm5, true, true, false); + } + + public static void StrbIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrbI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void StrbIT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrbI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrbRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrbR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrbRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.StrbR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void StrbRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrbR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void StrdIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.StrdI(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn, ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrdIT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rt2b8w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrdI(context, inst.Rt, inst.Rt2, inst.Rn, inst.Imm8 << 2, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrdRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrdR(context, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn, inst.Rm, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrexA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Strex(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StrexT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.Strex(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StrexbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Strexb(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StrexbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Strexb(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StrexdA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Strexd(context, inst.Rd, inst.Rt, RegisterUtils.GetRt2(inst.Rt), inst.Rn); + } + + public static void StrexdT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rt2b8w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Strexd(context, inst.Rd, inst.Rt, inst.Rt2, inst.Rn); + } + + public static void StrexhA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rtb0w4 inst = new(encoding); + + InstEmitMemory.Strexh(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StrexhT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Rdb0w4 inst = new(encoding); + + InstEmitMemory.Strexh(context, inst.Rd, inst.Rt, inst.Rn); + } + + public static void StrhtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.StrhtI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), postIndex: true, inst.U != 0); + } + + public static void StrhtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrhtR(context, inst.Rt, inst.Rn, inst.Rm, postIndex: true, inst.U != 0); + } + + public static void StrhtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrhtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void StrhIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm4hb8w4Imm4lb0w4 inst = new(encoding); + + InstEmitMemory.StrhI(context, inst.Rt, inst.Rn, (int)ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrhIT1(CodeGenContext context, uint encoding) + { + InstImm5b22w5Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.StrhI(context, inst.Rt, inst.Rn, (int)inst.Imm5 << 1, true, true, false); + } + + public static void StrhIT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrhI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void StrhIT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrhI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrhRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrhR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrhRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.StrhR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void StrhRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrhR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void StrtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrtI(context, inst.Rt, inst.Rn, (int)inst.Imm12, postIndex: true, inst.U != 0); + } + + public static void StrtA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrtR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, postIndex: true, inst.U != 0); + } + + public static void StrtT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrtI(context, inst.Rt, inst.Rn, (int)inst.Imm8, postIndex: false, true); + } + + public static void StrIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrI(context, inst.Rt, inst.Rn, (int)inst.Imm12, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrIT1(CodeGenContext context, uint encoding) + { + InstImm5b22w5Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.StrI(context, inst.Rt, inst.Rn, (int)inst.Imm5 << 2, true, true, false); + } + + public static void StrIT2(CodeGenContext context, uint encoding) + { + InstRtb24w3Imm8b16w8 inst = new(encoding); + + InstEmitMemory.StrI(context, inst.Rt, RegisterUtils.SpRegister, (int)inst.Imm8 << 2, true, true, false); + } + + public static void StrIT3(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm12b0w12 inst = new(encoding); + + InstEmitMemory.StrI(context, inst.Rt, inst.Rn, (int)inst.Imm12, true, true, false); + } + + public static void StrIT4(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Pb10w1Ub9w1Wb8w1Imm8b0w8 inst = new(encoding); + + InstEmitMemory.StrI(context, inst.Rt, inst.Rn, (int)inst.Imm8, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Wb21w1Rnb16w4Rtb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrR(context, inst.Rt, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.P != 0, inst.U != 0, inst.W != 0); + } + + public static void StrRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rtb16w3 inst = new(encoding); + + InstEmitMemory.StrR(context, inst.Rt, inst.Rn, inst.Rm, 0, 0, true, true, false); + } + + public static void StrRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Rtb12w4Imm2b4w2Rmb0w4 inst = new(encoding); + + InstEmitMemory.StrR(context, inst.Rt, inst.Rn, inst.Rm, 0, inst.Imm2, true, true, false); + } + + public static void SubIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void SubIT1(CodeGenContext context, uint encoding) + { + InstImm3b22w3Rnb19w3Rdb16w3 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, inst.Rn, inst.Imm3, !context.InITBlock); + } + + public static void SubIT2(CodeGenContext context, uint encoding) + { + InstRdnb24w3Imm8b16w8 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rdn, inst.Rdn, inst.Imm8, !context.InITBlock); + } + + public static void SubIT3(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void SubIT4(CodeGenContext context, uint encoding) + { + InstIb26w1Rnb16w4Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, inst.Rn, ImmUtils.CombineImmU12(inst.Imm8, inst.Imm3, inst.I), false); + } + + public static void SubIT5(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.SubI(context, RegisterUtils.PcRegister, inst.Rn, inst.Imm8, true); + } + + public static void SubRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SubR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void SubRT1(CodeGenContext context, uint encoding) + { + InstRmb22w3Rnb19w3Rdb16w3 inst = new(encoding); + + InstEmitAlu.SubR(context, inst.Rd, inst.Rn, inst.Rm, 0, 0, !context.InITBlock); + } + + public static void SubRT2(CodeGenContext context, uint encoding) + { + InstSb20w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SubR(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), !context.InITBlock); + } + + public static void SubRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rnb16w4Rdb12w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SubRr(context, inst.Rd, inst.Rn, inst.Rm, inst.Stype, inst.Rs, inst.S != 0); + } + + public static void SubSpIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, RegisterUtils.SpRegister, ImmUtils.ExpandImm(inst.Imm12), inst.S != 0); + } + + public static void SubSpIT1(CodeGenContext context, uint encoding) + { + InstImm7b16w7 inst = new(encoding); + + InstEmitAlu.SubI(context, RegisterUtils.SpRegister, RegisterUtils.SpRegister, inst.Imm7 << 2, false); + } + + public static void SubSpIT2(CodeGenContext context, uint encoding) + { + InstIb26w1Sb20w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, RegisterUtils.SpRegister, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), inst.S != 0); + } + + public static void SubSpIT3(CodeGenContext context, uint encoding) + { + InstIb26w1Imm3b12w3Rdb8w4Imm8b0w8 inst = new(encoding); + + InstEmitAlu.SubI(context, inst.Rd, RegisterUtils.SpRegister, ImmUtils.CombineImmU12(inst.Imm8, inst.Imm3, inst.I), false); + } + + public static void SubSpRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SubR(context, inst.Rd, RegisterUtils.SpRegister, inst.Rm, inst.Stype, inst.Imm5, inst.S != 0); + } + + public static void SubSpRT1(CodeGenContext context, uint encoding) + { + InstSb20w1Imm3b12w3Rdb8w4Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.SubR(context, inst.Rd, RegisterUtils.SpRegister, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.S != 0); + } + + public static void SvcA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Imm24b0w24 inst = new(encoding); + + InstEmitSystem.Svc(context, inst.Imm24); + } + + public static void SvcT1(CodeGenContext context, uint encoding) + { + InstImm8b16w8 inst = new(encoding); + + InstEmitSystem.Svc(context, inst.Imm8); + } + + public static void SxtabA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtab(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void SxtabT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtab(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void Sxtab16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtab16(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void Sxtab16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtab16(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void SxtahA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtah(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void SxtahT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtah(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void SxtbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtb(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void SxtbT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitExtension.Sxtb(context, inst.Rd, inst.Rm, 0); + } + + public static void SxtbT2(CodeGenContext context, uint encoding) + { + InstRdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtb(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void Sxtb16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtb16(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void Sxtb16T1(CodeGenContext context, uint encoding) + { + InstRdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxtb16(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void SxthA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxth(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void SxthT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitExtension.Sxth(context, inst.Rd, inst.Rm, 0); + } + + public static void SxthT2(CodeGenContext context, uint encoding) + { + InstRdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Sxth(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void TbbT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Hb4w1Rmb0w4 inst = new(encoding); + + InstEmitFlow.Tbb(context, inst.Rn, inst.Rm, inst.H != 0); + } + + public static void TeqIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.TeqI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12)); + } + + public static void TeqIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Rnb16w4Imm3b12w3Imm8b0w8 inst = new(encoding); + + InstEmitAlu.TeqI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I)); + } + + public static void TeqRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.TeqR(context, inst.Rn, inst.Rm, inst.Stype, inst.Imm5); + } + + public static void TeqRT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.TeqR(context, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void TeqRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.TeqRr(context, inst.Rn, inst.Rm, inst.Stype, inst.Rs); + } + + public static void TsbA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Tsb(); + } + + public static void TsbT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Tsb(); + } + + public static void TstIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm12b0w12 inst = new(encoding); + + InstEmitAlu.TstI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm12), ImmUtils.ExpandedImmRotated(inst.Imm12)); + } + + public static void TstIT1(CodeGenContext context, uint encoding) + { + InstIb26w1Rnb16w4Imm3b12w3Imm8b0w8 inst = new(encoding); + + InstEmitAlu.TstI(context, inst.Rn, ImmUtils.ExpandImm(inst.Imm8, inst.Imm3, inst.I), ImmUtils.ExpandedImmRotated(inst.Imm8, inst.Imm3, inst.I)); + } + + public static void TstRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Imm5b7w5Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.TstR(context, inst.Rn, inst.Rm, inst.Stype, inst.Imm5); + } + + public static void TstRT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rnb16w3 inst = new(encoding); + + InstEmitAlu.TstR(context, inst.Rn, inst.Rm, 0, 0); + } + + public static void TstRT2(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Imm2b6w2Stypeb4w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.TstR(context, inst.Rn, inst.Rm, inst.Stype, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void TstRrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rsb8w4Stypeb5w2Rmb0w4 inst = new(encoding); + + InstEmitAlu.TstRr(context, inst.Rn, inst.Rm, inst.Stype, inst.Rs); + } + + public static void Uadd16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Uadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uadd16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Uadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uadd8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Uadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uadd8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Uadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UasxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Uasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UasxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Uasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UbfxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Widthm1b16w5Rdb12w4Lsbb7w5Rnb0w4 inst = new(encoding); + + InstEmitBit.Ubfx(context, inst.Rd, inst.Rn, inst.Lsb, inst.Widthm1); + } + + public static void UbfxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Imm3b12w3Rdb8w4Imm2b6w2Widthm1b0w5 inst = new(encoding); + + InstEmitBit.Ubfx(context, inst.Rd, inst.Rn, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3), inst.Widthm1); + } + + public static void UdfA1(CodeGenContext context, uint encoding) + { + InstImm12b8w12Imm4b0w4 inst = new(encoding); + + InstEmitSystem.Udf(context, encoding, ImmUtils.CombineImmU16(inst.Imm12, inst.Imm4)); + } + + public static void UdfT1(CodeGenContext context, uint encoding) + { + InstImm8b16w8 inst = new(encoding); + + InstEmitSystem.Udf(context, encoding, inst.Imm8); + } + + public static void UdfT2(CodeGenContext context, uint encoding) + { + InstImm4b16w4Imm12b0w12 inst = new(encoding); + + InstEmitSystem.Udf(context, encoding, ImmUtils.CombineImmU16(inst.Imm12, inst.Imm4)); + } + + public static void UdivA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitDivide.Udiv(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UdivT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitDivide.Udiv(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhadd16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhadd16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhadd8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhadd8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UhasxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UhasxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UhsaxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UhsaxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhsub16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhsub16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhsub8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uhsub8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitHalve.Uhsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UmaalA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdhib16w4Rdlob12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Umaal(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm); + } + + public static void UmaalT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Umaal(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm); + } + + public static void UmlalA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdhib16w4Rdlob12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Umlal(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.S != 0); + } + + public static void UmlalT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Umlal(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, false); + } + + public static void UmullA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Sb20w1Rdhib16w4Rdlob12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitMultiply.Umull(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, inst.S != 0); + } + + public static void UmullT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdlob12w4Rdhib8w4Rmb0w4 inst = new(encoding); + + InstEmitMultiply.Umull(context, inst.Rdlo, inst.Rdhi, inst.Rn, inst.Rm, false); + } + + public static void Uqadd16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqadd16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqadd16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqadd8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqadd8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqadd8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UqasxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UqasxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqasx(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UqsaxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UqsaxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqsax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqsub16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqsub16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqsub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqsub8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Uqsub8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitSaturate.Uqsub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usad8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitAbsDiff.Usad8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usad8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitAbsDiff.Usad8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usada8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb16w4Rab12w4Rmb8w4Rnb0w4 inst = new(encoding); + + InstEmitAbsDiff.Usada8(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra); + } + + public static void Usada8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rab12w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitAbsDiff.Usada8(context, inst.Rd, inst.Rn, inst.Rm, inst.Ra); + } + + public static void UsatA1(CodeGenContext context, uint encoding) + { + InstCondb28w4SatImmb16w5Rdb12w4Imm5b7w5Shb6w1Rnb0w4 inst = new(encoding); + + InstEmitSaturate.Usat(context, inst.Rd, inst.SatImm, inst.Rn, inst.Sh != 0, inst.Imm5); + } + + public static void UsatT1(CodeGenContext context, uint encoding) + { + InstShb21w1Rnb16w4Imm3b12w3Rdb8w4Imm2b6w2SatImmb0w5 inst = new(encoding); + + InstEmitSaturate.Usat(context, inst.Rd, inst.SatImm, inst.Rn, inst.Sh != 0, ImmUtils.CombineImmU5(inst.Imm2, inst.Imm3)); + } + + public static void Usat16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4SatImmb16w4Rdb12w4Rnb0w4 inst = new(encoding); + + InstEmitSaturate.Usat16(context, inst.Rd, inst.SatImm, inst.Rn); + } + + public static void Usat16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4SatImmb0w4 inst = new(encoding); + + InstEmitSaturate.Usat16(context, inst.Rd, inst.SatImm, inst.Rn); + } + + public static void UsaxA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Usax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UsaxT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Usax(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usub16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Usub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usub16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Usub16(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usub8A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Usub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void Usub8T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rmb0w4 inst = new(encoding); + + InstEmitGE.Usub8(context, inst.Rd, inst.Rn, inst.Rm); + } + + public static void UxtabA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtab(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void UxtabT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtab(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void Uxtab16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtab16(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void Uxtab16T1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtab16(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void UxtahA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rnb16w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtah(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void UxtahT1(CodeGenContext context, uint encoding) + { + InstRnb16w4Rdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtah(context, inst.Rd, inst.Rn, inst.Rm, inst.Rotate); + } + + public static void UxtbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtb(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void UxtbT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitExtension.Uxtb(context, inst.Rd, inst.Rm, 0); + } + + public static void UxtbT2(CodeGenContext context, uint encoding) + { + InstRdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtb(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void Uxtb16A1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtb16(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void Uxtb16T1(CodeGenContext context, uint encoding) + { + InstRdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxtb16(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void UxthA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Rdb12w4Rotateb10w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxth(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void UxthT1(CodeGenContext context, uint encoding) + { + InstRmb19w3Rdb16w3 inst = new(encoding); + + InstEmitExtension.Uxth(context, inst.Rd, inst.Rm, 0); + } + + public static void UxthT2(CodeGenContext context, uint encoding) + { + InstRdb8w4Rotateb4w2Rmb0w4 inst = new(encoding); + + InstEmitExtension.Uxth(context, inst.Rd, inst.Rm, inst.Rotate); + } + + public static void VabaA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaba(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VabaT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaba(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VabalA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vabal(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VabalT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vabal(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VabdlIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vabdl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VabdlIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vabdl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VabdFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VabdF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VabdFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VabdF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VabdIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VabdI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VabdIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VabdI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VabsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vabs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VabsA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VabsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VabsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vabs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VabsT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VabsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VacgeA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.Vacge(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VacgeT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.Vacge(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VacgtA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.Vacgt(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VacgtT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.Vacgt(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VaddhnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaddhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VaddhnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaddhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VaddlA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaddl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VaddlT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaddl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VaddwA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaddw(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VaddwT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vaddw(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VaddFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VaddF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VaddFA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VaddF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VaddFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VaddF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VaddFT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VaddF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VaddIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VaddI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VaddIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VaddI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VandRA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VandR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VandRT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VandR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbicIA1(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VbicI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VbicIA2(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VbicI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VbicIT1(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VbicI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VbicIT2(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VbicI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VbicRA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbicR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbicRT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbicR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbifA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbifR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbifT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbifR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbitA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbitR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbitT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbitR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbslA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbslR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VbslT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VbslR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VcaddA1(CodeGenContext context, uint encoding) + { + _ = new InstRotb24w1Db22w1Sb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcaddT1(CodeGenContext context, uint encoding) + { + _ = new InstRotb24w1Db22w1Sb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VceqIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VceqI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VceqIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VceqI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VceqRA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VceqR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VceqRA2(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VceqFR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VceqRT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VceqR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VceqRT2(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VceqFR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VcgeIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgeI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VcgeIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgeI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VcgeRA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgeR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VcgeRA2(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgeFR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VcgeRT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgeR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VcgeRT2(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgeFR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VcgtIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgtI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VcgtIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgtI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VcgtRA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgtR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VcgtRA2(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgtFR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VcgtRT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgtR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VcgtRT2(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcgtFR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VcleIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcleI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VcleIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcleI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VclsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vcls(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VclsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vcls(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VcltIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcltI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VcltIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.VcltI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VclzA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vclz(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VclzT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vclz(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VcmlaA1(CodeGenContext context, uint encoding) + { + _ = new InstRotb23w2Db22w1Sb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcmlaT1(CodeGenContext context, uint encoding) + { + _ = new InstRotb23w2Db22w1Sb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcmlaSA1(CodeGenContext context, uint encoding) + { + _ = new InstSb23w1Db22w1Rotb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcmlaST1(CodeGenContext context, uint encoding) + { + _ = new InstSb23w1Db22w1Rotb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcmpA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpCompare.VcmpR(context, inst.Cond, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VcmpA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2 inst = new(encoding); + + InstEmitVfpCompare.VcmpI(context, inst.Cond, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Size); + } + + public static void VcmpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpCompare.VcmpR(context, 0xe, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VcmpT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2 inst = new(encoding); + + InstEmitVfpCompare.VcmpI(context, 0xe, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Size); + } + + public static void VcmpeA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpCompare.VcmpeR(context, inst.Cond, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VcmpeA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2 inst = new(encoding); + + InstEmitVfpCompare.VcmpeI(context, inst.Cond, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Size); + } + + public static void VcmpeT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpCompare.VcmpeR(context, 0xe, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VcmpeT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2 inst = new(encoding); + + InstEmitVfpCompare.VcmpeI(context, 0xe, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Size); + } + + public static void VcntA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vcnt(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VcntT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vcnt(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VcvtaAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvta(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtaAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvta(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtaVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvta(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtaVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvta(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtbA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Opb16w1Vdb12w4Szb8w1Mb5w1Vmb0w4 inst = new(encoding); + + uint dSize = inst.Sz == 1 && inst.Op == 0 ? 3u : 2u; + uint mSize = inst.Sz == 1 && inst.Op == 1 ? 3u : 2u; + + InstEmitVfpConvert.Vcvtb(context, InstEmitCommon.CombineV(inst.Vd, inst.D, dSize), InstEmitCommon.CombineV(inst.Vm, inst.M, mSize), inst.Sz, inst.Op); + } + + public static void VcvtbT1(CodeGenContext context, uint encoding) + { + InstDb22w1Opb16w1Vdb12w4Szb8w1Mb5w1Vmb0w4 inst = new(encoding); + + uint dSize = inst.Sz == 1 && inst.Op == 0 ? 3u : 2u; + uint mSize = inst.Sz == 1 && inst.Op == 1 ? 3u : 2u; + + InstEmitVfpConvert.Vcvtb(context, InstEmitCommon.CombineV(inst.Vd, inst.D, dSize), InstEmitCommon.CombineV(inst.Vm, inst.M, mSize), inst.Sz, inst.Op); + } + + public static void VcvtbBfsA1(CodeGenContext context, uint encoding) + { + _ = new InstCondb28w4Db22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcvtbBfsT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcvtmAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvtm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtmAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvtm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtmVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvtm(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtmVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvtm(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtnAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvtn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtnAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvtn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtnVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvtn(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtnVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvtn(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtpAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvtp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtpAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.Vcvtp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VcvtpVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvtp(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtpVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.Vcvtp(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Op != 0, inst.Size); + } + + public static void VcvtrIvA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtrIv(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), (encoding >> 16) & 7, inst.Size); + } + + public static void VcvtrIvT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtrIv(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), (encoding >> 16) & 7, inst.Size); + } + + public static void VcvttA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Opb16w1Vdb12w4Szb8w1Mb5w1Vmb0w4 inst = new(encoding); + + uint dSize = inst.Sz == 1 && inst.Op == 0 ? 3u : 2u; + uint mSize = inst.Sz == 1 && inst.Op == 1 ? 3u : 2u; + + InstEmitVfpConvert.Vcvtt(context, InstEmitCommon.CombineV(inst.Vd, inst.D, dSize), InstEmitCommon.CombineV(inst.Vm, inst.M, mSize), inst.Sz, inst.Op); + } + + public static void VcvttT1(CodeGenContext context, uint encoding) + { + InstDb22w1Opb16w1Vdb12w4Szb8w1Mb5w1Vmb0w4 inst = new(encoding); + + uint dSize = inst.Sz == 1 && inst.Op == 0 ? 3u : 2u; + uint mSize = inst.Sz == 1 && inst.Op == 1 ? 3u : 2u; + + InstEmitVfpConvert.Vcvtt(context, InstEmitCommon.CombineV(inst.Vd, inst.D, dSize), InstEmitCommon.CombineV(inst.Vm, inst.M, mSize), inst.Sz, inst.Op); + } + + public static void VcvttBfsA1(CodeGenContext context, uint encoding) + { + _ = new InstCondb28w4Db22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcvttBfsT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcvtBfsA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcvtBfsT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VcvtDsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + uint size = (encoding >> 8) & 3; + + InstEmitVfpConvert.VcvtDs(context, InstEmitCommon.CombineV(inst.Vd, inst.D, size ^ 1u), InstEmitCommon.CombineV(inst.Vm, inst.M, size), size); + } + + public static void VcvtDsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + uint size = (encoding >> 8) & 3; + + InstEmitVfpConvert.VcvtDs(context, InstEmitCommon.CombineV(inst.Vd, inst.D, size ^ 1u), InstEmitCommon.CombineV(inst.Vm, inst.M, size), size); + } + + public static void VcvtHsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb8w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.VcvtHs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0); + } + + public static void VcvtHsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb8w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.VcvtHs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0); + } + + public static void VcvtIsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w2Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.VcvtIs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op, inst.Size, inst.Q); + } + + public static void VcvtIsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w2Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.VcvtIs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op, inst.Size, inst.Q); + } + + public static void VcvtIvA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtIv(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), (encoding & (1u << 16)) == 0, inst.Size); + } + + public static void VcvtIvT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtIv(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), (encoding & (1u << 16)) == 0, inst.Size); + } + + public static void VcvtViA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtVi(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineVF(inst.M, inst.Vm), inst.Op == 0, inst.Size); + } + + public static void VcvtViT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Opb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtVi(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineVF(inst.M, inst.Vm), inst.Op == 0, inst.Size); + } + + public static void VcvtXsA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Opb8w2Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.VcvtXs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6, inst.Op, inst.U != 0, inst.Q); + } + + public static void VcvtXsT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Opb8w2Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonConvert.VcvtXs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6, inst.Op, inst.U != 0, inst.Q); + } + + public static void VcvtXvA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Opb18w1Ub16w1Vdb12w4Sfb8w2Sxb7w1Ib5w1Imm4b0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtXv(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Sf), ImmUtils.CombineImmU5IImm4(inst.I, inst.Imm4), inst.Sx != 0, inst.Sf, inst.Op, inst.U != 0); + } + + public static void VcvtXvT1(CodeGenContext context, uint encoding) + { + InstDb22w1Opb18w1Ub16w1Vdb12w4Sfb8w2Sxb7w1Ib5w1Imm4b0w4 inst = new(encoding); + + InstEmitVfpConvert.VcvtXv(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Sf), ImmUtils.CombineImmU5IImm4(inst.I, inst.Imm4), inst.Sx != 0, inst.Sf, inst.Op, inst.U != 0); + } + + public static void VdivA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VdivF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VdivT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VdivF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VdotA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VdotT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VdotSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VdotST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VdupRA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Bb22w1Qb21w1Vdb16w4Rtb12w4Db7w1Eb5w1 inst = new(encoding); + + InstEmitNeonMove.VdupR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rt, inst.B, inst.E, inst.Q); + } + + public static void VdupRT1(CodeGenContext context, uint encoding) + { + InstBb22w1Qb21w1Vdb16w4Rtb12w4Db7w1Eb5w1 inst = new(encoding); + + InstEmitNeonMove.VdupR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rt, inst.B, inst.E, inst.Q); + } + + public static void VdupSA1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm4b16w4Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VdupS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm4, inst.Q); + } + + public static void VdupST1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm4b16w4Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VdupS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm4, inst.Q); + } + + public static void VeorA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VeorR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VeorT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VeorR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VextA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Imm4b8w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vext(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm4, inst.Q); + } + + public static void VextT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Imm4b8w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vext(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm4, inst.Q); + } + + public static void VfmaA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VfmaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VfmaA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfmaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfmaT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VfmaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VfmaT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfmaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfmalA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmalT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmalSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmalST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmaBfA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmaBfT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmaBfsA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmaBfsT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VfmsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VfmsA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfmsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfmsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VfmsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VfmsT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfmsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfmslA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmslT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmslSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfmslST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VfnmaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfnmaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfnmaT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfnmaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfnmsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfnmsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VfnmsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VfnmsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VhaddA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vhadd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VhaddT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vhadd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VhsubA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vhsub(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VhsubT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vhsub(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VinsA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VinsT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VjcvtA1(CodeGenContext context, uint encoding) + { + _ = new InstCondb28w4Db22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VjcvtT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vdb12w4Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void Vld11A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld11A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld11A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld11T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld11T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld11T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld1AA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Ab4w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.A, inst.T, inst.Size); + } + + public static void Vld1AT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Ab4w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.A, inst.T, inst.Size); + } + + public static void Vld1MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 1, inst.Align, inst.Size); + } + + public static void Vld1MA2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 2, inst.Align, inst.Size); + } + + public static void Vld1MA3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 3, inst.Align, inst.Size); + } + + public static void Vld1MA4(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 4, inst.Align, inst.Size); + } + + public static void Vld1MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 1, inst.Align, inst.Size); + } + + public static void Vld1MT2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 2, inst.Align, inst.Size); + } + + public static void Vld1MT3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 3, inst.Align, inst.Size); + } + + public static void Vld1MT4(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 4, inst.Align, inst.Size); + } + + public static void Vld21A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld21A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld21A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld21T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld21T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld21T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld2AA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Ab4w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld2A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.A, inst.T, inst.Size); + } + + public static void Vld2AT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Ab4w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld2A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.A, inst.T, inst.Size); + } + + public static void Vld2MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vld2MA2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.Align, inst.Size); + } + + public static void Vld2MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vld2MT2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.Align, inst.Size); + } + + public static void Vld31A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld31A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld31A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld31T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld31T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld31T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld3AA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld3A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 0, inst.T, inst.Size); + } + + public static void Vld3AT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld3A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 0, inst.T, inst.Size); + } + + public static void Vld3MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld3M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vld3MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld3M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vld41A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld41A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld41A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld41T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vld41T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vld41T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vld4AA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Ab4w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld4A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.A, inst.T, inst.Size); + } + + public static void Vld4AT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Tb5w1Ab4w1Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld4A(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.A, inst.T, inst.Size); + } + + public static void Vld4MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld4M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vld4MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vld4M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void VldmA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vldm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void VldmA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vldm(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), inst.Rn, inst.Imm8, inst.U != 0, inst.W != 0, singleRegs: true); + } + + public static void VldmT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vldm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void VldmT2(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vldm(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), inst.Rn, inst.Imm8, inst.U != 0, inst.W != 0, singleRegs: true); + } + + public static void VldrIA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Db22w1Rnb16w4Vdb12w4Sizeb8w2Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vldr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Rn, inst.Imm8, inst.U != 0, inst.Size); + } + + public static void VldrIT1(CodeGenContext context, uint encoding) + { + InstUb23w1Db22w1Rnb16w4Vdb12w4Sizeb8w2Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vldr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Rn, inst.Imm8, inst.U != 0, inst.Size); + } + + public static void VldrLA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Db22w1Vdb12w4Sizeb8w2Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vldr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), RegisterUtils.PcRegister, inst.Imm8, inst.U != 0, inst.Size); + } + + public static void VldrLT1(CodeGenContext context, uint encoding) + { + InstUb23w1Db22w1Vdb12w4Sizeb8w2Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vldr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), RegisterUtils.PcRegister, inst.Imm8, inst.U != 0, inst.Size); + } + + public static void VmaxnmA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vmaxnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmaxnmA2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.Vmaxnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmaxnmT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vmaxnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmaxnmT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.Vmaxnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmaxFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmaxF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmaxFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmaxF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmaxIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmaxI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VmaxIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmaxI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VminnmA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vminnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VminnmA2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.Vminnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VminnmT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vminnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VminnmT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.Vminnm(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VminFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VminF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VminFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VminF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VminIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VminI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VminIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VminI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VmlalIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlalI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlalIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlalI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlalSA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlalS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlalST1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlalS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlaFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmlaFA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VmlaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmlaFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmlaFT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VmlaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmlaIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlaI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VmlaIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlaI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VmlaSA1(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Fb8w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlaS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VmlaST1(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Fb8w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlaS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VmlslIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlslI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlslIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlslI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlslSA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlslS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlslST1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlslS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmlsFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmlsFA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VmlsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmlsFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmlsFT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VmlsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmlsIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlsI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VmlsIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlsI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VmlsSA1(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Fb8w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlsS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VmlsST1(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Fb8w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmlsS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VmmlaA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VmmlaT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VmovlA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm3hb19w3Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vmovl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Imm3h); + } + + public static void VmovlT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm3hb19w3Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vmovl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Imm3h); + } + + public static void VmovnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vmovn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VmovnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vmovn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VmovxA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vmovx(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M)); + } + + public static void VmovxT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vmovx(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M)); + } + + public static void VmovDA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opb20w1Rt2b16w4Rtb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VmovD(context, inst.Rt, inst.Rt2, InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0); + } + + public static void VmovDT1(CodeGenContext context, uint encoding) + { + InstOpb20w1Rt2b16w4Rtb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VmovD(context, inst.Rt, inst.Rt2, InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0); + } + + public static void VmovHA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opb20w1Vnb16w4Rtb12w4Nb7w1 inst = new(encoding); + + InstEmitNeonMove.VmovH(context, inst.Rt, InstEmitCommon.CombineVF(inst.N, inst.Vn), inst.Op != 0); + } + + public static void VmovHT1(CodeGenContext context, uint encoding) + { + InstOpb20w1Vnb16w4Rtb12w4Nb7w1 inst = new(encoding); + + InstEmitNeonMove.VmovH(context, inst.Rt, InstEmitCommon.CombineVF(inst.N, inst.Vn), inst.Op != 0); + } + + public static void VmovIA1(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 0, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Imm4hb16w4Vdb12w4Sizeb8w2Imm4lb0w4 inst = new(encoding); + + InstEmitNeonMove.VmovFI(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.Size); + } + + public static void VmovIA3(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 0, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIA4(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 0, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIA5(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 1, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIT1(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 0, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIT2(CodeGenContext context, uint encoding) + { + InstDb22w1Imm4hb16w4Vdb12w4Sizeb8w2Imm4lb0w4 inst = new(encoding); + + InstEmitNeonMove.VmovFI(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), ImmUtils.CombineImmU8(inst.Imm4l, inst.Imm4h), inst.Size); + } + + public static void VmovIT3(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 0, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIT4(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 0, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovIT5(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmovI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), 1, (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmovRA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + uint size = (encoding >> 8) & 3; + + InstEmitNeonMove.VmovR(context, InstEmitCommon.CombineV(inst.Vd, inst.D, size), InstEmitCommon.CombineV(inst.Vm, inst.M, size), size); + } + + public static void VmovRT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + uint size = (encoding >> 8) & 3; + + InstEmitNeonMove.VmovR(context, InstEmitCommon.CombineV(inst.Vd, inst.D, size), InstEmitCommon.CombineV(inst.Vm, inst.M, size), size); + } + + public static void VmovRsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opc1b21w2Vdb16w4Rtb12w4Db7w1Opc2b5w2 inst = new(encoding); + + InstEmitNeonMove.VmovRs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rt, inst.Opc1, inst.Opc2); + } + + public static void VmovRsT1(CodeGenContext context, uint encoding) + { + InstOpc1b21w2Vdb16w4Rtb12w4Db7w1Opc2b5w2 inst = new(encoding); + + InstEmitNeonMove.VmovRs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rt, inst.Opc1, inst.Opc2); + } + + public static void VmovSA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opb20w1Vnb16w4Rtb12w4Nb7w1 inst = new(encoding); + + InstEmitNeonMove.VmovS(context, inst.Rt, InstEmitCommon.CombineVF(inst.N, inst.Vn), inst.Op != 0); + } + + public static void VmovST1(CodeGenContext context, uint encoding) + { + InstOpb20w1Vnb16w4Rtb12w4Nb7w1 inst = new(encoding); + + InstEmitNeonMove.VmovS(context, inst.Rt, InstEmitCommon.CombineVF(inst.N, inst.Vn), inst.Op != 0); + } + + public static void VmovSrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Opc1b21w2Vnb16w4Rtb12w4Nb7w1Opc2b5w2 inst = new(encoding); + + InstEmitNeonMove.VmovSr(context, inst.Rt, InstEmitCommon.CombineV(inst.Vn, inst.N), inst.U != 0, inst.Opc1, inst.Opc2); + } + + public static void VmovSrT1(CodeGenContext context, uint encoding) + { + InstUb23w1Opc1b21w2Vnb16w4Rtb12w4Nb7w1Opc2b5w2 inst = new(encoding); + + InstEmitNeonMove.VmovSr(context, inst.Rt, InstEmitCommon.CombineV(inst.Vn, inst.N), inst.U != 0, inst.Opc1, inst.Opc2); + } + + public static void VmovSsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Opb20w1Rt2b16w4Rtb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VmovSs(context, inst.Rt, inst.Rt2, InstEmitCommon.CombineVF(inst.M, inst.Vm), inst.Op != 0); + } + + public static void VmovSsT1(CodeGenContext context, uint encoding) + { + InstOpb20w1Rt2b16w4Rtb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VmovSs(context, inst.Rt, inst.Rt2, InstEmitCommon.CombineVF(inst.M, inst.Vm), inst.Op != 0); + } + + public static void VmrsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Regb16w4Rtb12w4 inst = new(encoding); + + InstEmitNeonSystem.Vmrs(context, inst.Rt, inst.Reg); + } + + public static void VmrsT1(CodeGenContext context, uint encoding) + { + InstRegb16w4Rtb12w4 inst = new(encoding); + + InstEmitNeonSystem.Vmrs(context, inst.Rt, inst.Reg); + } + + public static void VmsrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Regb16w4Rtb12w4 inst = new(encoding); + + InstEmitNeonSystem.Vmsr(context, inst.Rt, inst.Reg); + } + + public static void VmsrT1(CodeGenContext context, uint encoding) + { + InstRegb16w4Rtb12w4 inst = new(encoding); + + InstEmitNeonSystem.Vmsr(context, inst.Rt, inst.Reg); + } + + public static void VmullIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Opb9w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmullI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.U != 0, inst.Size); + } + + public static void VmullIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Opb9w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmullI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.U != 0, inst.Size); + } + + public static void VmullSA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmullS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmullST1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmullS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VmulFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmulF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmulFA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VmulF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmulFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmulF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VmulFT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VmulF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VmulIA1(CodeGenContext context, uint encoding) + { + InstOpb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmulI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VmulIT1(CodeGenContext context, uint encoding) + { + InstOpb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmulI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VmulSA1(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Fb8w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmulS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VmulST1(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Fb8w1Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VmulS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VmvnIA1(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmvnIA2(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmvnIA3(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmvnIT1(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmvnIT2(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmvnIT3(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VmvnRA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VmvnRT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.VmvnR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VnegA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vneg(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VnegA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnegF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnegT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb10w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vneg(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VnegT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnegF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnmlaA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnmlaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnmlaT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnmlaF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnmlsA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnmlsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnmlsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnmlsF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnmulA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnmulF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VnmulT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VnmulF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VornRA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VornR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VornRT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VornR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VorrIA1(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VorrI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VorrIA2(CodeGenContext context, uint encoding) + { + InstIb24w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VorrI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VorrIT1(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VorrI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VorrIT2(CodeGenContext context, uint encoding) + { + InstIb28w1Db22w1Imm3b16w3Vdb12w4Qb6w1Imm4b0w4 inst = new(encoding); + + InstEmitNeonLogical.VorrI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), (encoding >> 8) & 0xf, ImmUtils.CombineImmU8(inst.Imm4, inst.Imm3, inst.I), inst.Q); + } + + public static void VorrRA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VorrR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VorrRT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonLogical.VorrR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VpadalA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vpadal(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VpadalT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vpadal(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VpaddlA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vpaddl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VpaddlT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vpaddl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Size, inst.Q); + } + + public static void VpaddFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpaddF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VpaddFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpaddF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VpaddIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpaddI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VpaddIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpaddI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VpmaxFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpmaxF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, 0); + } + + public static void VpmaxFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpmaxF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, 0); + } + + public static void VpmaxIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpmaxI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, 0); + } + + public static void VpmaxIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpmaxI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, 0); + } + + public static void VpminFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpminF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, 0); + } + + public static void VpminFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpminF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, 0); + } + + public static void VpminIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpminI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, 0); + } + + public static void VpminIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VpminI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, 0); + } + + public static void VqabsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqabs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqabsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqabs(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqaddA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqadd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VqaddT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqadd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VqdmlalA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmlal(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlalA2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmlalS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlalT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmlal(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlalT2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmlalS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlslA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmlsl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlslA2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmlslS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlslT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmlsl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmlslT2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmlslS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmulhA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmulh(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqdmulhA2(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmulhS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqdmulhT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmulh(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqdmulhT2(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmulhS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqdmullA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmull(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmullA2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmullS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmullT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqdmull(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqdmullT2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqdmullS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VqmovnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb6w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqmovn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op, inst.Size); + } + + public static void VqmovnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Opb6w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqmovn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op, inst.Size); + } + + public static void VqnegA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqneg(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqnegT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqneg(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlahA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrdmlah(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlahA2(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqrdmlahS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlahT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrdmlah(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlahT2(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqrdmlahS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlshA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrdmlsh(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlshA2(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqrdmlshS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlshT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrdmlsh(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmlshT2(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqrdmlshS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmulhA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrdmulh(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmulhA2(CodeGenContext context, uint encoding) + { + InstQb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqrdmulhS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmulhT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrdmulh(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrdmulhT2(CodeGenContext context, uint encoding) + { + InstQb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqrdmulhS(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrshlA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrshl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrshlT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrshl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VqrshrnA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Opb8w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Op, inst.Imm6); + } + + public static void VqrshrnT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Opb8w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqrshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Op, inst.Imm6); + } + + public static void VqshlIA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Opb8w1Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqshlI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Op, inst.L, inst.Imm6, inst.Q); + } + + public static void VqshlIT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Opb8w1Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqshlI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Op, inst.L, inst.Imm6, inst.Q); + } + + public static void VqshlRA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqshlR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VqshlRT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.VqshlR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VqshrnA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Opb8w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Op, inst.Imm6); + } + + public static void VqshrnT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Opb8w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Op, inst.Imm6); + } + + public static void VqsubA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqsub(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VqsubT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonSaturate.Vqsub(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VraddhnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vraddhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VraddhnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vraddhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VrecpeA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb8w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrecpe(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VrecpeT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb8w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrecpe(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VrecpsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrecps(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VrecpsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrecps(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void Vrev16A1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vrev16(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void Vrev16T1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vrev16(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void Vrev32A1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vrev32(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void Vrev32T1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vrev32(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void Vrev64A1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vrev64(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void Vrev64T1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonBit.Vrev64(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrhaddA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrhadd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VrhaddT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrhadd(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VrintaAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrinta(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintaAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrinta(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintaVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrinta(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintaVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrinta(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintmAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintmAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintmVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintm(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintmVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintm(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintnAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintnAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintnVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintn(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintnVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintn(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintpAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintpAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintpVfpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintp(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintpVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintp(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintrVfpA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintrVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintxAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintx(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintxAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintx(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintxVfpA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintx(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintxVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintx(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintzAsimdA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintz(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintzAsimdT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrintz(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VrintzVfpA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintz(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrintzVfpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpRound.Vrintz(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VrshlA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrshl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VrshlT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrshl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VrshrA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrshr(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VrshrT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrshr(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VrshrnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6); + } + + public static void VrshrnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6); + } + + public static void VrsqrteA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb8w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrsqrte(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VrsqrteT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Fb8w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrsqrte(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.F != 0, inst.Size, inst.Q); + } + + public static void VrsqrtsA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrsqrts(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VrsqrtsT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vrsqrts(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VrsraA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrsra(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VrsraT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrsra(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VrsubhnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrsubhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VrsubhnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonRound.Vrsubhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VsdotA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VsdotT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VsdotSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VsdotST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VselA1(CodeGenContext context, uint encoding) + { + InstDb22w1Ccb20w2Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpMove.Vsel(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Cc, inst.Size); + } + + public static void VselT1(CodeGenContext context, uint encoding) + { + InstDb22w1Ccb20w2Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpMove.Vsel(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Cc, inst.Size); + } + + public static void VshllA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshll(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6, inst.U != 0); + } + + public static void VshllA2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshll2(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VshllT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshll(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6, inst.U != 0); + } + + public static void VshllT2(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshll2(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VshlIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.VshlI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.L, inst.Imm6, inst.Q); + } + + public static void VshlIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.VshlI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.L, inst.Imm6, inst.Q); + } + + public static void VshlRA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.VshlR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VshlRT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.VshlR(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size, inst.Q); + } + + public static void VshrA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshr(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VshrT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshr(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VshrnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6); + } + + public static void VshrnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vshrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Imm6); + } + + public static void VsliA1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vsli(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.L, inst.Imm6, inst.Q); + } + + public static void VsliT1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vsli(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.L, inst.Imm6, inst.Q); + } + + public static void VsmmlaA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VsmmlaT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VsqrtA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VsqrtF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VsqrtT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Sizeb8w2Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VsqrtF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VsraA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vsra(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VsraT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vsra(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.L, inst.Imm6, inst.Q); + } + + public static void VsriA1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vsri(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.L, inst.Imm6, inst.Q); + } + + public static void VsriT1(CodeGenContext context, uint encoding) + { + InstDb22w1Imm6b16w6Vdb12w4Lb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonShift.Vsri(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.L, inst.Imm6, inst.Q); + } + + public static void Vst11A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst11A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst11A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst11T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst11T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst11T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst11(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst1MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 1, inst.Align, inst.Size); + } + + public static void Vst1MA2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 2, inst.Align, inst.Size); + } + + public static void Vst1MA3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 3, inst.Align, inst.Size); + } + + public static void Vst1MA4(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 4, inst.Align, inst.Size); + } + + public static void Vst1MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 1, inst.Align, inst.Size); + } + + public static void Vst1MT2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 2, inst.Align, inst.Size); + } + + public static void Vst1MT3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 3, inst.Align, inst.Size); + } + + public static void Vst1MT4(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst1M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, 4, inst.Align, inst.Size); + } + + public static void Vst21A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst21A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst21A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst21T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst21T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst21T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst21(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst2MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vst2MA2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.Align, inst.Size); + } + + public static void Vst2MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vst2MT2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst2M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.Align, inst.Size); + } + + public static void Vst31A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst31A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst31A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst31T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst31T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst31T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst31(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst3MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst3M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vst3MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst3M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vst41A1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst41A2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst41A3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst41T1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 0); + } + + public static void Vst41T2(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 1); + } + + public static void Vst41T3(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4IndexAlignb4w4Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst41(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, inst.IndexAlign, 2); + } + + public static void Vst4MA1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst4M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void Vst4MT1(CodeGenContext context, uint encoding) + { + InstDb22w1Rnb16w4Vdb12w4Sizeb6w2Alignb4w2Rmb0w4 inst = new(encoding); + + InstEmitNeonMemory.Vst4M(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Rm, (encoding >> 8) & 0xf, inst.Align, inst.Size); + } + + public static void VstmA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vstm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void VstmA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Pb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vstm(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), inst.Rn, inst.Imm8, inst.U != 0, inst.W != 0, singleRegs: true); + } + + public static void VstmT1(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm871b1w7 inst = new(encoding); + + InstEmitNeonMemory.Vstm(context, InstEmitCommon.CombineV(inst.Vd, inst.D), inst.Rn, inst.Imm871, inst.U != 0, inst.W != 0, singleRegs: false); + } + + public static void VstmT2(CodeGenContext context, uint encoding) + { + InstPb24w1Ub23w1Db22w1Wb21w1Rnb16w4Vdb12w4Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vstm(context, InstEmitCommon.CombineVF(inst.D, inst.Vd), inst.Rn, inst.Imm8, inst.U != 0, inst.W != 0, singleRegs: true); + } + + public static void VstrA1(CodeGenContext context, uint encoding) + { + InstCondb28w4Ub23w1Db22w1Rnb16w4Vdb12w4Sizeb8w2Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vstr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Rn, inst.Imm8, inst.U != 0, inst.Size); + } + + public static void VstrT1(CodeGenContext context, uint encoding) + { + InstUb23w1Db22w1Rnb16w4Vdb12w4Sizeb8w2Imm8b0w8 inst = new(encoding); + + InstEmitNeonMemory.Vstr(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), inst.Rn, inst.Imm8, inst.U != 0, inst.Size); + } + + public static void VsubhnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vsubhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VsubhnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vsubhn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size); + } + + public static void VsublA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vsubl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VsublT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vsubl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VsubwA1(CodeGenContext context, uint encoding) + { + InstUb24w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vsubw(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VsubwT1(CodeGenContext context, uint encoding) + { + InstUb28w1Db22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.Vsubw(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.U != 0, inst.Size); + } + + public static void VsubFA1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VsubF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VsubFA2(CodeGenContext context, uint encoding) + { + InstCondb28w4Db22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VsubF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VsubFT1(CodeGenContext context, uint encoding) + { + InstDb22w1Szb20w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VsubF(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Sz, inst.Q); + } + + public static void VsubFT2(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Sizeb8w2Nb7w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitVfpArithmetic.VsubF(context, InstEmitCommon.CombineV(inst.Vd, inst.D, inst.Size), InstEmitCommon.CombineV(inst.Vn, inst.N, inst.Size), InstEmitCommon.CombineV(inst.Vm, inst.M, inst.Size), inst.Size); + } + + public static void VsubIA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VsubI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VsubIT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonArithmetic.VsubI(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VsudotSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VsudotST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VswpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vswp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VswpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vswp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Q); + } + + public static void VtblA1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Lenb8w2Nb7w1Opb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vtbl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Len); + } + + public static void VtblT1(CodeGenContext context, uint encoding) + { + InstDb22w1Vnb16w4Vdb12w4Lenb8w2Nb7w1Opb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vtbl(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Op != 0, inst.Len); + } + + public static void VtrnA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vtrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VtrnT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vtrn(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VtstA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.Vtst(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VtstT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb20w2Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonCompare.Vtst(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vn, inst.N), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VudotA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VudotT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VudotSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VudotST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VummlaA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VummlaT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VusdotA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VusdotT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VusdotSA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VusdotST1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Qb6w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VusmmlaA1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VusmmlaT1(CodeGenContext context, uint encoding) + { + _ = new InstDb22w1Vnb16w4Vdb12w4Nb7w1Mb5w1Vmb0w4(encoding); + + throw new NotImplementedException(); + } + + public static void VuzpA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vuzp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VuzpT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vuzp(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VzipA1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vzip(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void VzipT1(CodeGenContext context, uint encoding) + { + InstDb22w1Sizeb18w2Vdb12w4Qb6w1Mb5w1Vmb0w4 inst = new(encoding); + + InstEmitNeonMove.Vzip(context, InstEmitCommon.CombineV(inst.Vd, inst.D), InstEmitCommon.CombineV(inst.Vm, inst.M), inst.Size, inst.Q); + } + + public static void WfeA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Wfe(); + } + + public static void WfeT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Wfe(); + } + + public static void WfeT2(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Wfe(); + } + + public static void WfiA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Wfi(); + } + + public static void WfiT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Wfi(); + } + + public static void WfiT2(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Wfi(); + } + + public static void YieldA1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Yield(); + } + + public static void YieldT1(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Yield(); + } + + public static void YieldT2(CodeGenContext context, uint encoding) + { + context.Arm64Assembler.Yield(); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAbsDiff.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAbsDiff.cs new file mode 100644 index 00000000..f8100503 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAbsDiff.cs @@ -0,0 +1,87 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitAbsDiff + { + public static void Usad8(CodeGenContext context, uint rd, uint rn, uint rm) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + for (int b = 0; b < 4; b++) + { + context.Arm64Assembler.Ubfx(tempN.Operand, rnOperand, b * 8, 8); + context.Arm64Assembler.Ubfx(tempM.Operand, rmOperand, b * 8, 8); + + Operand dest = b == 0 ? tempD.Operand : tempD2.Operand; + + context.Arm64Assembler.Sub(dest, tempN.Operand, tempM.Operand); + + EmitAbs(context, dest); + + if (b > 0) + { + if (b < 3) + { + context.Arm64Assembler.Add(tempD.Operand, tempD.Operand, dest); + } + else + { + context.Arm64Assembler.Add(rdOperand, tempD.Operand, dest); + } + } + } + } + + public static void Usada8(CodeGenContext context, uint rd, uint rn, uint rm, uint ra) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + for (int b = 0; b < 4; b++) + { + context.Arm64Assembler.Ubfx(tempN.Operand, rnOperand, b * 8, 8); + context.Arm64Assembler.Ubfx(tempM.Operand, rmOperand, b * 8, 8); + + Operand dest = b == 0 ? tempD.Operand : tempD2.Operand; + + context.Arm64Assembler.Sub(dest, tempN.Operand, tempM.Operand); + + EmitAbs(context, dest); + + if (b > 0) + { + context.Arm64Assembler.Add(tempD.Operand, tempD.Operand, dest); + } + } + + context.Arm64Assembler.Add(rdOperand, tempD.Operand, raOperand); + } + + private static void EmitAbs(CodeGenContext context, Operand value) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + // r = (value + ((int)value >> 31)) ^ ((int)value >> 31). + // Subtracts 1 and then inverts the value if the sign bit is set, same as a conditional negation. + + context.Arm64Assembler.Add(tempRegister.Operand, value, value, ArmShiftType.Asr, 31); + context.Arm64Assembler.Eor(value, tempRegister.Operand, value, ArmShiftType.Asr, 31); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs new file mode 100644 index 00000000..c0762819 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs @@ -0,0 +1,1105 @@ +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); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitBit.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitBit.cs new file mode 100644 index 00000000..3f91d45f --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitBit.cs @@ -0,0 +1,103 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitBit + { + public static void Bfc(CodeGenContext context, uint rd, uint lsb, uint msb) + { + // This is documented as "unpredictable". + if (msb < lsb) + { + return; + } + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + + context.Arm64Assembler.Bfc(rdOperand, (int)lsb, (int)(msb - lsb + 1)); + } + + public static void Bfi(CodeGenContext context, uint rd, uint rn, uint lsb, uint msb) + { + // This is documented as "unpredictable". + if (msb < lsb) + { + return; + } + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + context.Arm64Assembler.Bfi(rdOperand, rnOperand, (int)lsb, (int)(msb - lsb + 1)); + } + + public static void Clz(CodeGenContext context, uint rd, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Clz(rdOperand, rmOperand); + } + + public static void Rbit(CodeGenContext context, uint rd, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Rbit(rdOperand, rmOperand); + } + + public static void Rev(CodeGenContext context, uint rd, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Rev(rdOperand, rmOperand); + } + + public static void Rev16(CodeGenContext context, uint rd, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Rev16(rdOperand, rmOperand); + } + + public static void Revsh(CodeGenContext context, uint rd, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Rev16(rdOperand, rmOperand); + context.Arm64Assembler.Sxth(rdOperand, rdOperand); + } + + public static void Sbfx(CodeGenContext context, uint rd, uint rn, uint lsb, uint widthMinus1) + { + // This is documented as "unpredictable". + if (lsb + widthMinus1 > 31) + { + return; + } + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + context.Arm64Assembler.Sbfx(rdOperand, rnOperand, (int)lsb, (int)widthMinus1 + 1); + } + + public static void Ubfx(CodeGenContext context, uint rd, uint rn, uint lsb, uint widthMinus1) + { + // This is documented as "unpredictable". + if (lsb + widthMinus1 > 31) + { + return; + } + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + context.Arm64Assembler.Ubfx(rdOperand, rnOperand, (int)lsb, (int)widthMinus1 + 1); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitCommon.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitCommon.cs new file mode 100644 index 00000000..1ec4c807 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitCommon.cs @@ -0,0 +1,263 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitCommon + { + public static Operand Const(int value) + { + return new(OperandKind.Constant, OperandType.I32, (uint)value); + } + + public static Operand GetInputGpr(CodeGenContext context, uint register) + { + Operand operand = context.RegisterAllocator.RemapGprRegister((int)register); + + if (register == RegisterUtils.PcRegister) + { + context.Arm64Assembler.Mov(operand, context.Pc); + } + + return operand; + } + + public static Operand GetOutputGpr(CodeGenContext context, uint register) + { + return context.RegisterAllocator.RemapGprRegister((int)register); + } + + public static void GetCurrentFlags(CodeGenContext context, Operand flagsOut) + { + context.Arm64Assembler.MrsNzcv(flagsOut); + context.Arm64Assembler.Lsr(flagsOut, flagsOut, Const(28)); + } + + public static void RestoreNzcvFlags(CodeGenContext context, Operand nzcvFlags) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Lsl(tempRegister.Operand, nzcvFlags, Const(28)); + context.Arm64Assembler.MsrNzcv(tempRegister.Operand); + } + + public static void RestoreCvFlags(CodeGenContext context, Operand cvFlags) + { + // Arm64 zeros the carry and overflow flags for logical operations, but Arm32 keeps them unchanged. + // This will restore carry and overflow after a operation has zeroed them. + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.MrsNzcv(tempRegister.Operand); + context.Arm64Assembler.Bfi(tempRegister.Operand, cvFlags, 28, 2); + context.Arm64Assembler.MsrNzcv(tempRegister.Operand); + } + + public static void SetThumbFlag(CodeGenContext context) + { + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + context.Arm64Assembler.Orr(tempRegister.Operand, tempRegister.Operand, Const(1 << 5)); + context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + } + + public static void SetThumbFlag(CodeGenContext context, Operand value) + { + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + context.Arm64Assembler.Bfi(tempRegister.Operand, value, 5, 1); + context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + } + + public static void ClearThumbFlag(CodeGenContext context) + { + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + context.Arm64Assembler.Bfc(tempRegister.Operand, 5, 1); + context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + } + + public static void EmitSigned16BitPair(CodeGenContext context, uint rd, uint rn, Action<Operand, Operand> elementAction) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = GetOutputGpr(context, rd); + Operand rnOperand = GetInputGpr(context, rn); + + context.Arm64Assembler.Sxth(tempN.Operand, rnOperand); + elementAction(tempD.Operand, tempN.Operand); + context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand); + + context.Arm64Assembler.Asr(tempN.Operand, rnOperand, Const(16)); + elementAction(tempD.Operand, tempN.Operand); + context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16); + } + + public static void EmitSigned16BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = GetOutputGpr(context, rd); + Operand rnOperand = GetInputGpr(context, rn); + Operand rmOperand = GetInputGpr(context, rm); + + context.Arm64Assembler.Sxth(tempN.Operand, rnOperand); + context.Arm64Assembler.Sxth(tempM.Operand, rmOperand); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand); + context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand); + + context.Arm64Assembler.Asr(tempN.Operand, rnOperand, Const(16)); + context.Arm64Assembler.Asr(tempM.Operand, rmOperand, Const(16)); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand); + context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16); + } + + public static void EmitSigned16BitXPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand, int> elementAction) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = GetOutputGpr(context, rd); + Operand rnOperand = GetInputGpr(context, rn); + Operand rmOperand = GetInputGpr(context, rm); + + context.Arm64Assembler.Sxth(tempN.Operand, rnOperand); + context.Arm64Assembler.Asr(tempM.Operand, rmOperand, Const(16)); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 0); + context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand); + + context.Arm64Assembler.Asr(tempN.Operand, rnOperand, Const(16)); + context.Arm64Assembler.Sxth(tempM.Operand, rmOperand); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 1); + context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16); + } + + public static void EmitSigned8BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction) + { + Emit8BitPair(context, rd, rn, rm, elementAction, unsigned: false); + } + + public static void EmitUnsigned16BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = GetOutputGpr(context, rd); + Operand rnOperand = GetInputGpr(context, rn); + Operand rmOperand = GetInputGpr(context, rm); + + context.Arm64Assembler.Uxth(tempN.Operand, rnOperand); + context.Arm64Assembler.Uxth(tempM.Operand, rmOperand); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand); + context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand); + + context.Arm64Assembler.Lsr(tempN.Operand, rnOperand, Const(16)); + context.Arm64Assembler.Lsr(tempM.Operand, rmOperand, Const(16)); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand); + context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16); + } + + public static void EmitUnsigned16BitXPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand, int> elementAction) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = GetOutputGpr(context, rd); + Operand rnOperand = GetInputGpr(context, rn); + Operand rmOperand = GetInputGpr(context, rm); + + context.Arm64Assembler.Uxth(tempN.Operand, rnOperand); + context.Arm64Assembler.Lsr(tempM.Operand, rmOperand, Const(16)); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 0); + context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand); + + context.Arm64Assembler.Lsr(tempN.Operand, rnOperand, Const(16)); + context.Arm64Assembler.Uxth(tempM.Operand, rmOperand); + elementAction(tempD.Operand, tempN.Operand, tempM.Operand, 1); + context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16); + } + + public static void EmitUnsigned8BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction) + { + Emit8BitPair(context, rd, rn, rm, elementAction, unsigned: true); + } + + private static void Emit8BitPair(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> elementAction, bool unsigned) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = GetOutputGpr(context, rd); + Operand rnOperand = GetInputGpr(context, rn); + Operand rmOperand = GetInputGpr(context, rm); + + for (int b = 0; b < 4; b++) + { + if (unsigned) + { + context.Arm64Assembler.Ubfx(tempN.Operand, rnOperand, b * 8, 8); + context.Arm64Assembler.Ubfx(tempM.Operand, rmOperand, b * 8, 8); + } + else + { + context.Arm64Assembler.Sbfx(tempN.Operand, rnOperand, b * 8, 8); + context.Arm64Assembler.Sbfx(tempM.Operand, rmOperand, b * 8, 8); + } + + elementAction(tempD.Operand, tempN.Operand, tempM.Operand); + + if (b == 0) + { + context.Arm64Assembler.Uxtb(tempD2.Operand, tempD.Operand); + } + else if (b < 3) + { + context.Arm64Assembler.Uxtb(tempD.Operand, tempD.Operand); + context.Arm64Assembler.Orr(tempD2.Operand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, b * 8); + } + else + { + context.Arm64Assembler.Orr(rdOperand, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 24); + } + } + } + + public static uint CombineV(uint low4, uint high1, uint size) + { + return size == 3 ? CombineV(low4, high1) : CombineVF(high1, low4); + } + + public static uint CombineV(uint low4, uint high1) + { + return low4 | (high1 << 4); + } + + public static uint CombineVF(uint low1, uint high4) + { + return low1 | (high4 << 1); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitCrc32.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitCrc32.cs new file mode 100644 index 00000000..ee634188 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitCrc32.cs @@ -0,0 +1,26 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using System; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitCrc32 + { + public static void Crc32(CodeGenContext context, uint rd, uint rn, uint rm, uint sz) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Crc32(rdOperand, rnOperand, rmOperand, Math.Min(2, sz)); + } + + public static void Crc32c(CodeGenContext context, uint rd, uint rn, uint rm, uint sz) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Crc32c(rdOperand, rnOperand, rmOperand, Math.Min(2, sz)); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitDivide.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitDivide.cs new file mode 100644 index 00000000..31c96dc8 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitDivide.cs @@ -0,0 +1,25 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitDivide + { + public static void Sdiv(CodeGenContext context, uint rd, uint rn, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Sdiv(rdOperand, rnOperand, rmOperand); + } + + public static void Udiv(CodeGenContext context, uint rd, uint rn, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Udiv(rdOperand, rnOperand, rmOperand); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitExtension.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitExtension.cs new file mode 100644 index 00000000..dafe2974 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitExtension.cs @@ -0,0 +1,191 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitExtension + { + public static void Sxtab(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate) + { + EmitRotated(context, ArmExtensionType.Sxtb, rd, rn, rm, rotate); + } + + public static void Sxtab16(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate) + { + EmitExtendAccumulate8(context, rd, rn, rm, rotate, unsigned: false); + } + + public static void Sxtah(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate) + { + EmitRotated(context, ArmExtensionType.Sxth, rd, rn, rm, rotate); + } + + public static void Sxtb(CodeGenContext context, uint rd, uint rm, uint rotate) + { + EmitRotated(context, context.Arm64Assembler.Sxtb, rd, rm, rotate); + } + + public static void Sxtb16(CodeGenContext context, uint rd, uint rm, uint rotate) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempRegister2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + if (rotate != 0) + { + context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8)); + context.Arm64Assembler.And(rdOperand, tempRegister.Operand, InstEmitCommon.Const(0xff00ff)); + } + else + { + context.Arm64Assembler.And(rdOperand, rmOperand, InstEmitCommon.Const(0xff00ff)); + } + + // Sign-extend by broadcasting sign bits. + context.Arm64Assembler.And(tempRegister.Operand, rdOperand, InstEmitCommon.Const(0x800080)); + context.Arm64Assembler.Lsl(tempRegister2.Operand, tempRegister.Operand, InstEmitCommon.Const(9)); + context.Arm64Assembler.Sub(tempRegister.Operand, tempRegister2.Operand, tempRegister.Operand); + context.Arm64Assembler.Orr(rdOperand, rdOperand, tempRegister.Operand); + } + + public static void Sxth(CodeGenContext context, uint rd, uint rm, uint rotate) + { + EmitRotated(context, context.Arm64Assembler.Sxth, rd, rm, rotate); + } + + public static void Uxtab(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate) + { + EmitRotated(context, ArmExtensionType.Uxtb, rd, rn, rm, rotate); + } + + public static void Uxtab16(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate) + { + EmitExtendAccumulate8(context, rd, rn, rm, rotate, unsigned: true); + } + + public static void Uxtah(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate) + { + EmitRotated(context, ArmExtensionType.Uxth, rd, rn, rm, rotate); + } + + public static void Uxtb(CodeGenContext context, uint rd, uint rm, uint rotate) + { + EmitRotated(context, context.Arm64Assembler.Uxtb, rd, rm, rotate); + } + + public static void Uxtb16(CodeGenContext context, uint rd, uint rm, uint rotate) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (rotate != 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8)); + context.Arm64Assembler.And(rdOperand, tempRegister.Operand, InstEmitCommon.Const(0xff00ff)); + } + else + { + context.Arm64Assembler.And(rdOperand, rmOperand, InstEmitCommon.Const(0xff00ff)); + } + } + + public static void Uxth(CodeGenContext context, uint rd, uint rm, uint rotate) + { + EmitRotated(context, context.Arm64Assembler.Uxth, rd, rm, rotate); + } + + private static void EmitRotated(CodeGenContext context, Action<Operand, Operand> action, uint rd, uint rm, uint rotate) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (rotate != 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8)); + action(rdOperand, tempRegister.Operand); + } + else + { + action(rdOperand, rmOperand); + } + } + + private static void EmitRotated(CodeGenContext context, ArmExtensionType extensionType, uint rd, uint rn, uint rm, uint rotate) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (rotate != 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8)); + context.Arm64Assembler.Add(rdOperand, rnOperand, tempRegister.Operand, extensionType); + } + else + { + context.Arm64Assembler.Add(rdOperand, rnOperand, rmOperand, extensionType); + } + } + + private static void EmitExtendAccumulate8(CodeGenContext context, uint rd, uint rn, uint rm, uint rotate, bool unsigned) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (rotate != 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)rotate * 8)); + + EmitExtendAccumulate8Core(context, rdOperand, rnOperand, tempRegister.Operand, unsigned); + } + else + { + EmitExtendAccumulate8Core(context, rdOperand, rnOperand, rmOperand, unsigned); + } + } + + private static void EmitExtendAccumulate8Core(CodeGenContext context, Operand rd, Operand rn, Operand rm, bool unsigned) + { + using ScopedRegister tempD = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempD2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + if (unsigned) + { + context.Arm64Assembler.Uxth(tempN.Operand, rn); + } + else + { + context.Arm64Assembler.Sxth(tempN.Operand, rn); + } + + context.Arm64Assembler.Add(tempD.Operand, tempN.Operand, rm, unsigned ? ArmExtensionType.Uxtb : ArmExtensionType.Sxtb); + context.Arm64Assembler.Uxth(tempD2.Operand, tempD.Operand); + + if (unsigned) + { + context.Arm64Assembler.Lsr(tempN.Operand, rn, InstEmitCommon.Const(16)); + } + else + { + context.Arm64Assembler.Asr(tempN.Operand, rn, InstEmitCommon.Const(16)); + } + + context.Arm64Assembler.Lsr(tempD.Operand, rm, InstEmitCommon.Const(16)); + context.Arm64Assembler.Add(tempD.Operand, tempN.Operand, tempD.Operand, unsigned ? ArmExtensionType.Uxtb : ArmExtensionType.Sxtb); + context.Arm64Assembler.Orr(rd, tempD2.Operand, tempD.Operand, ArmShiftType.Lsl, 16); + } + } +} 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); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitGE.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitGE.cs new file mode 100644 index 00000000..dffcc511 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitGE.cs @@ -0,0 +1,265 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitGE + { + public static void Sadd16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: true, add: true, unsigned: false); + } + + public static void Sadd8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: false, add: true, unsigned: false); + } + + public static void Sasx(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAsxSax(context, rd, rn, rm, isAsx: true, unsigned: false); + } + + public static void Sel(CodeGenContext context, uint rd, uint rn, uint rm) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister geFlags = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + ExtractGEFlags(context, geFlags.Operand); + + // Broadcast compact GE flags (one bit to one byte, 0b1111 -> 0x1010101). + context.Arm64Assembler.Mov(tempRegister.Operand, 0x204081u); + context.Arm64Assembler.Mul(geFlags.Operand, geFlags.Operand, tempRegister.Operand); + context.Arm64Assembler.And(geFlags.Operand, geFlags.Operand, InstEmitCommon.Const(0x1010101)); + + // Build mask from expanded flags (0x1010101 -> 0xFFFFFFFF). + context.Arm64Assembler.Lsl(tempRegister.Operand, geFlags.Operand, InstEmitCommon.Const(8)); + context.Arm64Assembler.Sub(geFlags.Operand, tempRegister.Operand, geFlags.Operand); + + // Result = (n & mask) | (m & ~mask). + context.Arm64Assembler.And(tempRegister.Operand, geFlags.Operand, rnOperand); + context.Arm64Assembler.Bic(rdOperand, rmOperand, geFlags.Operand); + context.Arm64Assembler.Orr(rdOperand, rdOperand, tempRegister.Operand); + } + + public static void Ssax(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAsxSax(context, rd, rn, rm, isAsx: false, unsigned: false); + } + + public static void Ssub16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: true, add: false, unsigned: false); + } + + public static void Ssub8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: false, add: false, unsigned: false); + } + + public static void Uadd16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: true, add: true, unsigned: true); + } + + public static void Uadd8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: false, add: true, unsigned: true); + } + + public static void Uasx(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAsxSax(context, rd, rn, rm, isAsx: true, unsigned: true); + } + + public static void Usax(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAsxSax(context, rd, rn, rm, isAsx: false, unsigned: true); + } + + public static void Usub16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: true, add: false, unsigned: true); + } + + public static void Usub8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSub(context, rd, rn, rm, is16Bit: false, add: false, unsigned: true); + } + + private static void EmitAddSub(CodeGenContext context, uint rd, uint rn, uint rm, bool is16Bit, bool add, bool unsigned) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister geFlags = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + int e = 0; + + void Emit(Operand d, Operand n, Operand m) + { + if (add) + { + context.Arm64Assembler.Add(d, n, m); + } + else + { + context.Arm64Assembler.Sub(d, n, m); + } + + if (unsigned && add) + { + if (e == 0) + { + context.Arm64Assembler.Lsr(geFlags.Operand, d, InstEmitCommon.Const(is16Bit ? 16 : 8)); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Lsr(tempRegister.Operand, d, InstEmitCommon.Const(is16Bit ? 16 : 8)); + context.Arm64Assembler.Orr(geFlags.Operand, geFlags.Operand, tempRegister.Operand, ArmShiftType.Lsl, e); + } + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mvn(tempRegister.Operand, d); + + if (e == 0) + { + context.Arm64Assembler.Lsr(geFlags.Operand, tempRegister.Operand, InstEmitCommon.Const(31)); + } + else + { + context.Arm64Assembler.Lsr(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(31)); + context.Arm64Assembler.Orr(geFlags.Operand, geFlags.Operand, tempRegister.Operand, ArmShiftType.Lsl, e); + } + } + + e += is16Bit ? 2 : 1; + } + + if (is16Bit) + { + if (unsigned) + { + InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, Emit); + } + else + { + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, Emit); + } + + // Duplicate bits. + context.Arm64Assembler.Orr(geFlags.Operand, geFlags.Operand, geFlags.Operand, ArmShiftType.Lsl, 1); + } + else + { + if (unsigned) + { + InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, Emit); + } + else + { + InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, Emit); + } + } + + UpdateGEFlags(context, geFlags.Operand); + } + + private static void EmitAsxSax(CodeGenContext context, uint rd, uint rn, uint rm, bool isAsx, bool unsigned) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister geFlags = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + void Emit(Operand d, Operand n, Operand m, int e) + { + bool add = e == (isAsx ? 1 : 0); + + if (add) + { + context.Arm64Assembler.Add(d, n, m); + } + else + { + context.Arm64Assembler.Sub(d, n, m); + } + + if (unsigned && add) + { + if (e == 0) + { + context.Arm64Assembler.Lsr(geFlags.Operand, d, InstEmitCommon.Const(16)); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Lsr(tempRegister.Operand, d, InstEmitCommon.Const(16)); + context.Arm64Assembler.Orr(geFlags.Operand, geFlags.Operand, tempRegister.Operand, ArmShiftType.Lsl, e * 2); + } + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mvn(tempRegister.Operand, d); + + if (e == 0) + { + context.Arm64Assembler.Lsr(geFlags.Operand, tempRegister.Operand, InstEmitCommon.Const(31)); + } + else + { + context.Arm64Assembler.Lsr(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(31)); + context.Arm64Assembler.Orr(geFlags.Operand, geFlags.Operand, tempRegister.Operand, ArmShiftType.Lsl, e * 2); + } + } + } + + if (unsigned) + { + InstEmitCommon.EmitUnsigned16BitXPair(context, rd, rn, rm, Emit); + } + else + { + InstEmitCommon.EmitSigned16BitXPair(context, rd, rn, rm, Emit); + } + + // Duplicate bits. + context.Arm64Assembler.Orr(geFlags.Operand, geFlags.Operand, geFlags.Operand, ArmShiftType.Lsl, 1); + + UpdateGEFlags(context, geFlags.Operand); + } + + public static void UpdateGEFlags(CodeGenContext context, Operand flags) + { + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + context.Arm64Assembler.Bfi(tempRegister.Operand, flags, 16, 4); + context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + } + + public static void ExtractGEFlags(CodeGenContext context, Operand flags) + { + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + context.Arm64Assembler.LdrRiUn(flags, ctx, NativeContextOffsets.FlagsBaseOffset); + context.Arm64Assembler.Ubfx(flags, flags, 16, 4); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitHalve.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitHalve.cs new file mode 100644 index 00000000..567acfbf --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitHalve.cs @@ -0,0 +1,178 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitHalve + { + public static void Shadd16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHadd(context, rd, rn, rm, 0x7fff7fff, unsigned: false); + } + + public static void Shadd8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHadd(context, rd, rn, rm, 0x7f7f7f7f, unsigned: false); + } + + public static void Shsub16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHsub(context, rd, rn, rm, 0x7fff7fff, unsigned: false); + } + + public static void Shsub8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHsub(context, rd, rn, rm, 0x7f7f7f7f, unsigned: false); + } + + public static void Shasx(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Sub(d, n, m); + } + else + { + context.Arm64Assembler.Add(d, n, m); + } + + context.Arm64Assembler.Lsr(d, d, InstEmitCommon.Const(1)); + }); + } + + public static void Shsax(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Add(d, n, m); + } + else + { + context.Arm64Assembler.Sub(d, n, m); + } + + context.Arm64Assembler.Lsr(d, d, InstEmitCommon.Const(1)); + }); + } + + public static void Uhadd16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHadd(context, rd, rn, rm, 0x7fff7fff, unsigned: true); + } + + public static void Uhadd8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHadd(context, rd, rn, rm, 0x7f7f7f7f, unsigned: true); + } + + public static void Uhasx(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitUnsigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Sub(d, n, m); + } + else + { + context.Arm64Assembler.Add(d, n, m); + } + + context.Arm64Assembler.Lsr(d, d, InstEmitCommon.Const(1)); + }); + } + + public static void Uhsax(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitUnsigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Add(d, n, m); + } + else + { + context.Arm64Assembler.Sub(d, n, m); + } + + context.Arm64Assembler.Lsr(d, d, InstEmitCommon.Const(1)); + }); + } + + public static void Uhsub16(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHsub(context, rd, rn, rm, 0x7fff7fff, unsigned: true); + } + + public static void Uhsub8(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitHsub(context, rd, rn, rm, 0x7f7f7f7f, unsigned: true); + } + + private static void EmitHadd(CodeGenContext context, uint rd, uint rn, uint rm, int mask, bool unsigned) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister res = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister carry = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + // This relies on the equality x+y == ((x&y) << 1) + (x^y). + // Note that x^y always contains the LSB of the result. + // Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>1). + // We mask by 0x7F/0x7FFF to remove the LSB so that it doesn't leak into the field below. + + context.Arm64Assembler.And(res.Operand, rmOperand, rnOperand); + context.Arm64Assembler.Eor(carry.Operand, rmOperand, rnOperand); + context.Arm64Assembler.Lsr(rdOperand, carry.Operand, InstEmitCommon.Const(1)); + context.Arm64Assembler.And(rdOperand, rdOperand, InstEmitCommon.Const(mask)); + context.Arm64Assembler.Add(rdOperand, rdOperand, res.Operand); + + if (!unsigned) + { + // Propagates the sign bit from (x^y)>>1 upwards by one. + context.Arm64Assembler.And(carry.Operand, carry.Operand, InstEmitCommon.Const(~mask)); + context.Arm64Assembler.Eor(rdOperand, rdOperand, carry.Operand); + } + } + + private static void EmitHsub(CodeGenContext context, uint rd, uint rn, uint rm, int mask, bool unsigned) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister carry = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister left = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister right = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + // This relies on the equality x-y == (x^y) - (((x^y)&y) << 1). + // Note that x^y always contains the LSB of the result. + // Since we want to calculate (x+y)/2, we can instead calculate ((x^y)>>1) - ((x^y)&y). + + context.Arm64Assembler.Eor(carry.Operand, rmOperand, rnOperand); + context.Arm64Assembler.Lsr(left.Operand, carry.Operand, InstEmitCommon.Const(1)); + context.Arm64Assembler.And(right.Operand, carry.Operand, rmOperand); + + // We must now perform a partitioned subtraction. + // We can do this because minuend contains 7/15 bit fields. + // We use the extra bit in minuend as a bit to borrow from; we set this bit. + // We invert this bit at the end as this tells us if that bit was borrowed from. + + context.Arm64Assembler.Orr(rdOperand, left.Operand, InstEmitCommon.Const(~mask)); + context.Arm64Assembler.Sub(rdOperand, rdOperand, right.Operand); + context.Arm64Assembler.Eor(rdOperand, rdOperand, InstEmitCommon.Const(~mask)); + + if (!unsigned) + { + // We then sign extend the result into this bit. + context.Arm64Assembler.And(carry.Operand, carry.Operand, InstEmitCommon.Const(~mask)); + context.Arm64Assembler.Eor(rdOperand, rdOperand, carry.Operand); + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs new file mode 100644 index 00000000..6ab4b949 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMemory.cs @@ -0,0 +1,1172 @@ +using ARMeilleure.Memory; +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; +using System.Diagnostics; +using System.Numerics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitMemory + { + private enum PrefetchType : uint + { + Pld = 0, + Pli = 1, + Pst = 2, + } + + public static void Lda(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldar); + } + + public static void Ldab(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldarb); + } + + public static void Ldaex(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldaxr); + } + + public static void Ldaexb(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldaxrb); + } + + public static void Ldaexd(CodeGenContext context, uint rt, uint rt2, uint rn) + { + EmitMemoryDWordInstruction(context, rt, rt2, rn, isStore: false, context.Arm64Assembler.Ldaxp); + } + + public static void Ldaexh(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldaxrh); + } + + public static void Ldah(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldarh); + } + + public static void LdcI(CodeGenContext context, uint rn, int imm, bool p, bool u, bool w) + { + // TODO. + } + + public static void LdcL(CodeGenContext context, uint imm, bool p, bool u, bool w) + { + // TODO. + } + + public static void Ldm(CodeGenContext context, uint rn, uint registerList, bool w) + { + Operand baseAddress = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, baseAddress); + + EmitMemoryMultipleInstructionCore( + context, + tempRegister.Operand, + registerList, + isStore: false, + context.Arm64Assembler.LdrRiUn, + context.Arm64Assembler.LdpRiUn); + + if (w) + { + Operand offset = InstEmitCommon.Const(BitOperations.PopCount(registerList) * 4); + + WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, true, ArmShiftType.Lsl, 0); + } + } + + public static void Ldmda(CodeGenContext context, uint rn, uint registerList, bool w) + { + EmitMemoryMultipleDaInstruction(context, rn, registerList, w, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.LdpRiUn); + } + + public static void Ldmdb(CodeGenContext context, uint rn, uint registerList, bool w) + { + EmitMemoryMultipleDbInstruction(context, rn, registerList, w, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.LdpRiUn); + } + + public static void Ldmib(CodeGenContext context, uint rn, uint registerList, bool w) + { + EmitMemoryMultipleIbInstruction(context, rn, registerList, w, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.LdpRiUn); + } + + public static void LdrI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 2, p, u, w, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.Ldur); + } + + public static void LdrL(CodeGenContext context, uint rt, uint imm, bool p, bool u, bool w) + { + EmitMemoryLiteralInstruction(context, rt, imm, 2, p, u, w, context.Arm64Assembler.LdrRiUn); + } + + public static void LdrR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.Ldur); + } + + public static void LdrbI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 0, p, u, w, isStore: false, context.Arm64Assembler.LdrbRiUn, context.Arm64Assembler.Ldurb); + } + + public static void LdrbL(CodeGenContext context, uint rt, uint imm, bool p, bool u, bool w) + { + EmitMemoryLiteralInstruction(context, rt, imm, 0, p, u, w, context.Arm64Assembler.LdrbRiUn); + } + + public static void LdrbR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: false, context.Arm64Assembler.LdrbRiUn, context.Arm64Assembler.Ldurb); + } + + public static void LdrbtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 0, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrbRiUn, context.Arm64Assembler.Ldurb); + } + + public static void LdrbtR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrbRiUn, context.Arm64Assembler.Ldurb); + } + + public static void LdrdI(CodeGenContext context, uint rt, uint rt2, uint rn, uint imm, bool p, bool u, bool w) + { + EmitMemoryDWordInstructionI(context, rt, rt2, rn, imm, p, u, w, isStore: false, context.Arm64Assembler.LdpRiUn); + } + + public static void LdrdL(CodeGenContext context, uint rt, uint rt2, uint imm, bool p, bool u, bool w) + { + EmitMemoryDWordLiteralInstruction(context, rt, rt2, imm, p, u, w, context.Arm64Assembler.LdpRiUn); + } + + public static void LdrdR(CodeGenContext context, uint rt, uint rt2, uint rn, uint rm, bool p, bool u, bool w) + { + EmitMemoryDWordInstructionR(context, rt, rt2, rn, rm, p, u, w, isStore: false, context.Arm64Assembler.LdpRiUn); + } + + public static void Ldrex(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldaxr); + } + + public static void Ldrexb(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldaxrb); + } + + public static void Ldrexd(CodeGenContext context, uint rt, uint rt2, uint rn) + { + EmitMemoryDWordInstruction(context, rt, rt2, rn, isStore: false, context.Arm64Assembler.Ldaxp); + } + + public static void Ldrexh(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: false, context.Arm64Assembler.Ldaxrh); + } + + public static void LdrhI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 1, p, u, w, isStore: false, context.Arm64Assembler.LdrhRiUn, context.Arm64Assembler.Ldurh); + } + + public static void LdrhL(CodeGenContext context, uint rt, uint imm, bool p, bool u, bool w) + { + EmitMemoryLiteralInstruction(context, rt, imm, 1, p, u, w, context.Arm64Assembler.LdrhRiUn); + } + + public static void LdrhR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: false, context.Arm64Assembler.LdrhRiUn, context.Arm64Assembler.Ldurh); + } + + public static void LdrhtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 1, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrhRiUn, context.Arm64Assembler.Ldurh); + } + + public static void LdrhtR(CodeGenContext context, uint rt, uint rn, uint rm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, 0, 0, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrhRiUn, context.Arm64Assembler.Ldurh); + } + + public static void LdrsbI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 0, p, u, w, isStore: false, context.Arm64Assembler.LdrsbRiUn, context.Arm64Assembler.Ldursb); + } + + public static void LdrsbL(CodeGenContext context, uint rt, uint imm, bool p, bool u, bool w) + { + EmitMemoryLiteralInstruction(context, rt, imm, 0, p, u, w, context.Arm64Assembler.LdrsbRiUn); + } + + public static void LdrsbR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: false, context.Arm64Assembler.LdrsbRiUn, context.Arm64Assembler.Ldursb); + } + + public static void LdrsbtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 0, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrsbRiUn, context.Arm64Assembler.Ldursb); + } + + public static void LdrsbtR(CodeGenContext context, uint rt, uint rn, uint rm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, 0, 0, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrsbRiUn, context.Arm64Assembler.Ldursb); + } + + public static void LdrshI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 1, p, u, w, isStore: false, context.Arm64Assembler.LdrshRiUn, context.Arm64Assembler.Ldursh); + } + + public static void LdrshL(CodeGenContext context, uint rt, uint imm, bool p, bool u, bool w) + { + EmitMemoryLiteralInstruction(context, rt, imm, 1, p, u, w, context.Arm64Assembler.LdrshRiUn); + } + + public static void LdrshR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: false, context.Arm64Assembler.LdrshRiUn, context.Arm64Assembler.Ldursh); + } + + public static void LdrshtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 1, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrshRiUn, context.Arm64Assembler.Ldursh); + } + + public static void LdrshtR(CodeGenContext context, uint rt, uint rn, uint rm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, 0, 0, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrshRiUn, context.Arm64Assembler.Ldursh); + } + + public static void LdrtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 2, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.Ldur); + } + + public static void LdrtR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, !postIndex, u, false, isStore: false, context.Arm64Assembler.LdrRiUn, context.Arm64Assembler.Ldur); + } + + public static void PldI(CodeGenContext context, uint rn, uint imm, bool u, bool r) + { + EmitMemoryPrefetchInstruction(context, rn, imm, u, r ? PrefetchType.Pld : PrefetchType.Pst); + } + + public static void PldL(CodeGenContext context, uint imm, bool u) + { + EmitMemoryPrefetchLiteralInstruction(context, imm, u, PrefetchType.Pld); + } + + public static void PldR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5, bool u, bool r) + { + EmitMemoryPrefetchInstruction(context, rn, rm, u, sType, imm5, r ? PrefetchType.Pld : PrefetchType.Pst); + } + + public static void PliI(CodeGenContext context, uint rn, uint imm, bool u) + { + EmitMemoryPrefetchInstruction(context, rn, imm, u, PrefetchType.Pli); + } + + public static void PliL(CodeGenContext context, uint imm, bool u) + { + EmitMemoryPrefetchLiteralInstruction(context, imm, u, PrefetchType.Pli); + } + + public static void PliR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5, bool u) + { + EmitMemoryPrefetchInstruction(context, rn, rm, u, sType, imm5, PrefetchType.Pli); + } + + public static void Stc(CodeGenContext context, uint rn, int imm, bool p, bool u, bool w) + { + // TODO. + } + + public static void Stl(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: true, context.Arm64Assembler.Stlr); + } + + public static void Stlb(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: true, context.Arm64Assembler.Stlrb); + } + + public static void Stlex(CodeGenContext context, uint rd, uint rt, uint rn) + { + EmitMemoryStrexInstruction(context, rd, rt, rn, context.Arm64Assembler.Stlxr); + } + + public static void Stlexb(CodeGenContext context, uint rd, uint rt, uint rn) + { + EmitMemoryStrexInstruction(context, rd, rt, rn, context.Arm64Assembler.Stlxrb); + } + + public static void Stlexd(CodeGenContext context, uint rd, uint rt, uint rt2, uint rn) + { + EmitMemoryDWordStrexInstruction(context, rd, rt, rt2, rn, context.Arm64Assembler.Stlxp); + } + + public static void Stlexh(CodeGenContext context, uint rd, uint rt, uint rn) + { + EmitMemoryStrexInstruction(context, rd, rt, rn, context.Arm64Assembler.Stlxrh); + } + + public static void Stlh(CodeGenContext context, uint rt, uint rn) + { + EmitMemoryInstruction(context, rt, rn, isStore: true, context.Arm64Assembler.Stlrh); + } + + public static void Stm(CodeGenContext context, uint rn, uint registerList, bool w) + { + Operand baseAddress = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, baseAddress); + + EmitMemoryMultipleInstructionCore( + context, + tempRegister.Operand, + registerList, + isStore: true, + context.Arm64Assembler.StrRiUn, + context.Arm64Assembler.StpRiUn); + + if (w) + { + Operand offset = InstEmitCommon.Const(BitOperations.PopCount(registerList) * 4); + + WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, true, ArmShiftType.Lsl, 0); + } + } + + public static void Stmda(CodeGenContext context, uint rn, uint registerList, bool w) + { + EmitMemoryMultipleDaInstruction(context, rn, registerList, w, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.StpRiUn); + } + + public static void Stmdb(CodeGenContext context, uint rn, uint registerList, bool w) + { + EmitMemoryMultipleDbInstruction(context, rn, registerList, w, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.StpRiUn); + } + + public static void Stmib(CodeGenContext context, uint rn, uint registerList, bool w) + { + EmitMemoryMultipleIbInstruction(context, rn, registerList, w, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.StpRiUn); + } + + public static void StrI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 2, p, u, w, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.Stur); + } + + public static void StrR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.Stur); + } + + public static void StrbI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 0, p, u, w, isStore: true, context.Arm64Assembler.StrbRiUn, context.Arm64Assembler.Sturb); + } + + public static void StrbR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: true, context.Arm64Assembler.StrbRiUn, context.Arm64Assembler.Sturb); + } + + public static void StrbtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 0, !postIndex, u, false, isStore: true, context.Arm64Assembler.StrbRiUn, context.Arm64Assembler.Sturb); + } + + public static void StrbtR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, !postIndex, u, false, isStore: true, context.Arm64Assembler.StrbRiUn, context.Arm64Assembler.Sturb); + } + + public static void StrdI(CodeGenContext context, uint rt, uint rt2, uint rn, uint imm, bool p, bool u, bool w) + { + EmitMemoryDWordInstructionI(context, rt, rt2, rn, imm, p, u, w, isStore: true, context.Arm64Assembler.StpRiUn); + } + + public static void StrdR(CodeGenContext context, uint rt, uint rt2, uint rn, uint rm, bool p, bool u, bool w) + { + EmitMemoryDWordInstructionR(context, rt, rt2, rn, rm, p, u, w, isStore: true, context.Arm64Assembler.StpRiUn); + } + + public static void Strex(CodeGenContext context, uint rd, uint rt, uint rn) + { + EmitMemoryStrexInstruction(context, rd, rt, rn, context.Arm64Assembler.Stlxr); + } + + public static void Strexb(CodeGenContext context, uint rd, uint rt, uint rn) + { + EmitMemoryStrexInstruction(context, rd, rt, rn, context.Arm64Assembler.Stlxrb); + } + + public static void Strexd(CodeGenContext context, uint rd, uint rt, uint rt2, uint rn) + { + EmitMemoryDWordStrexInstruction(context, rd, rt, rt2, rn, context.Arm64Assembler.Stlxp); + } + + public static void Strexh(CodeGenContext context, uint rd, uint rt, uint rn) + { + EmitMemoryStrexInstruction(context, rd, rt, rn, context.Arm64Assembler.Stlxrh); + } + + public static void StrhI(CodeGenContext context, uint rt, uint rn, int imm, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, imm, 1, p, u, w, isStore: true, context.Arm64Assembler.StrhRiUn, context.Arm64Assembler.Sturh); + } + + public static void StrhR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool p, bool u, bool w) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, p, u, w, isStore: true, context.Arm64Assembler.StrhRiUn, context.Arm64Assembler.Sturh); + } + + public static void StrhtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 1, !postIndex, u, false, isStore: true, context.Arm64Assembler.StrhRiUn, context.Arm64Assembler.Sturh); + } + + public static void StrhtR(CodeGenContext context, uint rt, uint rn, uint rm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, 0, 0, !postIndex, u, false, isStore: true, context.Arm64Assembler.StrhRiUn, context.Arm64Assembler.Sturh); + } + + public static void StrtI(CodeGenContext context, uint rt, uint rn, int imm, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, imm, 2, !postIndex, u, false, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.Stur); + } + + public static void StrtR(CodeGenContext context, uint rt, uint rn, uint rm, uint sType, uint imm5, bool postIndex, bool u) + { + EmitMemoryInstruction(context, rt, rn, rm, sType, imm5, !postIndex, u, false, isStore: true, context.Arm64Assembler.StrRiUn, context.Arm64Assembler.Stur); + } + + private static void EmitMemoryMultipleDaInstruction( + CodeGenContext context, + uint rn, + uint registerList, + bool w, + bool isStore, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, Operand, int> writeInstPair) + { + Operand baseAddress = InstEmitCommon.GetInputGpr(context, rn); + Operand offset; + + if (registerList != 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + offset = InstEmitCommon.Const(BitOperations.PopCount(registerList) * 4 - 4); + + WriteAddShiftOffset(context.Arm64Assembler, tempRegister.Operand, baseAddress, offset, false, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, tempRegister.Operand); + + EmitMemoryMultipleInstructionCore( + context, + tempRegister.Operand, + registerList, + isStore, + writeInst, + writeInstPair); + } + + if (w) + { + offset = InstEmitCommon.Const(BitOperations.PopCount(registerList) * 4); + + WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, false, ArmShiftType.Lsl, 0); + } + } + + private static void EmitMemoryMultipleDbInstruction( + CodeGenContext context, + uint rn, + uint registerList, + bool w, + bool isStore, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, Operand, int> writeInstPair) + { + Operand baseAddress = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand offset = InstEmitCommon.Const(BitOperations.PopCount(registerList) * 4); + + bool writesToRn = (registerList & (1u << (int)rn)) != 0; + + if (w && !writesToRn) + { + WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, false, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, baseAddress); + } + else + { + WriteAddShiftOffset(context.Arm64Assembler, tempRegister.Operand, baseAddress, offset, false, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, tempRegister.Operand); + } + + EmitMemoryMultipleInstructionCore( + context, + tempRegister.Operand, + registerList, + isStore, + writeInst, + writeInstPair); + + if (w && writesToRn) + { + WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, false, ArmShiftType.Lsl, 0); + } + } + + private static void EmitMemoryMultipleIbInstruction( + CodeGenContext context, + uint rn, + uint registerList, + bool w, + bool isStore, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, Operand, int> writeInstPair) + { + Operand baseAddress = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand offset = InstEmitCommon.Const(4); + + WriteAddShiftOffset(context.Arm64Assembler, tempRegister.Operand, baseAddress, offset, true, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, tempRegister.Operand); + + EmitMemoryMultipleInstructionCore( + context, + tempRegister.Operand, + registerList, + isStore, + writeInst, + writeInstPair); + + if (w) + { + offset = InstEmitCommon.Const(BitOperations.PopCount(registerList) * 4); + + WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, true, ArmShiftType.Lsl, 0); + } + } + + private static void EmitMemoryMultipleInstructionCore( + CodeGenContext context, + Operand baseAddress, + uint registerList, + bool isStore, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, Operand, int> writeInstPair) + { + uint registers = registerList; + int offs = 0; + + while (registers != 0) + { + int regIndex = BitOperations.TrailingZeroCount(registers); + + registers &= ~(1u << regIndex); + + Operand rt = isStore + ? InstEmitCommon.GetInputGpr(context, (uint)regIndex) + : InstEmitCommon.GetOutputGpr(context, (uint)regIndex); + + int regIndex2 = BitOperations.TrailingZeroCount(registers); + if (regIndex2 < 32) + { + registers &= ~(1u << regIndex2); + + Operand rt2 = isStore + ? InstEmitCommon.GetInputGpr(context, (uint)regIndex2) + : InstEmitCommon.GetOutputGpr(context, (uint)regIndex2); + + writeInstPair(rt, rt2, baseAddress, offs); + + offs += 8; + } + else + { + writeInst(rt, baseAddress, offs); + + offs += 4; + } + } + } + + private static void EmitMemoryInstruction( + CodeGenContext context, + uint rt, + uint rn, + int imm, + int scale, + bool p, + bool u, + bool w, + bool isStore, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, int> writeInstUnscaled) + { + Operand rtOperand = isStore ? InstEmitCommon.GetInputGpr(context, rt) : InstEmitCommon.GetOutputGpr(context, rt); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand offset = InstEmitCommon.Const(imm); + + EmitMemoryInstruction(context, writeInst, writeInstUnscaled, rtOperand, rnOperand, offset, scale, p, u, w); + } + + private static void EmitMemoryInstruction( + CodeGenContext context, + uint rt, + uint rn, + uint rm, + uint sType, + uint imm5, + bool p, + bool u, + bool w, + bool isStore, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, int> writeInstUnscaled) + { + Operand rtOperand = isStore ? InstEmitCommon.GetInputGpr(context, rt) : InstEmitCommon.GetOutputGpr(context, rt); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + EmitMemoryInstruction(context, writeInst, writeInstUnscaled, rtOperand, rnOperand, rmOperand, 0, p, u, w, (ArmShiftType)sType, (int)imm5); + } + + private static void EmitMemoryInstruction(CodeGenContext context, uint rt, uint rn, bool isStore, Action<Operand, Operand> action) + { + Operand rtOperand = isStore ? InstEmitCommon.GetInputGpr(context, rt) : InstEmitCommon.GetOutputGpr(context, rt); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, rnOperand); + + action(rtOperand, tempRegister.Operand); + } + + private static void EmitMemoryDWordInstruction(CodeGenContext context, uint rt, uint rt2, uint rn, bool isStore, Action<Operand, Operand, Operand> action) + { + Operand rtOperand = isStore ? InstEmitCommon.GetInputGpr(context, rt) : InstEmitCommon.GetOutputGpr(context, rt); + Operand rt2Operand = isStore ? InstEmitCommon.GetInputGpr(context, rt2) : InstEmitCommon.GetOutputGpr(context, rt2); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, rnOperand); + + action(rtOperand, rt2Operand, tempRegister.Operand); + } + + private static void EmitMemoryDWordInstructionI( + CodeGenContext context, + uint rt, + uint rt2, + uint rn, + uint imm, + bool p, + bool u, + bool w, + bool isStore, + Action<Operand, Operand, Operand, int> action) + { + Operand rtOperand = isStore ? InstEmitCommon.GetInputGpr(context, rt) : InstEmitCommon.GetOutputGpr(context, rt); + Operand rt2Operand = isStore ? InstEmitCommon.GetInputGpr(context, rt2) : InstEmitCommon.GetOutputGpr(context, rt2); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand offset = InstEmitCommon.Const((int)imm); + + EmitMemoryDWordInstruction(context, rtOperand, rt2Operand, rnOperand, offset, p, u, w, action); + } + + private static void EmitMemoryDWordInstructionR( + CodeGenContext context, + uint rt, + uint rt2, + uint rn, + uint rm, + bool p, + bool u, + bool w, + bool isStore, + Action<Operand, Operand, Operand, int> action) + { + Operand rtOperand = isStore ? InstEmitCommon.GetInputGpr(context, rt) : InstEmitCommon.GetOutputGpr(context, rt); + Operand rt2Operand = isStore ? InstEmitCommon.GetInputGpr(context, rt2) : InstEmitCommon.GetOutputGpr(context, rt2); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + EmitMemoryDWordInstruction(context, rtOperand, rt2Operand, rnOperand, rmOperand, p, u, w, action); + } + + private static void EmitMemoryDWordInstruction( + CodeGenContext context, + Operand rt, + Operand rt2, + Operand baseAddress, + Operand offset, + bool index, + bool add, + bool wBack, + Action<Operand, Operand, Operand, int> action) + { + Assembler asm = context.Arm64Assembler; + RegisterAllocator regAlloc = context.RegisterAllocator; + + if (index && !wBack) + { + // Offset. + + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + int signedOffs = add ? offset.AsInt32() : -offset.AsInt32(); + int offs = 0; + + if (offset.Kind == OperandKind.Constant && offset.Value == 0) + { + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + } + else if (offset.Kind == OperandKind.Constant && CanFoldDWordOffset(context.MemoryManagerType, signedOffs)) + { + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + offs = signedOffs; + } + else + { + WriteAddShiftOffset(asm, tempRegister.Operand, baseAddress, offset, add, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, tempRegister.Operand); + } + + action(rt, rt2, tempRegister.Operand, offs); + } + else if (context.IsThumb ? !index && wBack : !index && !wBack) + { + // Post-indexed. + + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + + action(rt, rt2, tempRegister.Operand, 0); + + WriteAddShiftOffset(asm, baseAddress, baseAddress, offset, add, ArmShiftType.Lsl, 0); + } + else if (index && wBack) + { + // Pre-indexed. + + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + if (rt.Value == baseAddress.Value) + { + // If Rt and Rn are the same register, ensure we perform the write back after the read/write. + + WriteAddShiftOffset(asm, tempRegister.Operand, baseAddress, offset, add, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, tempRegister.Operand); + + action(rt, rt2, tempRegister.Operand, 0); + + context.Arm64Assembler.Mov(baseAddress, tempRegister.Operand); + } + else + { + WriteAddShiftOffset(asm, baseAddress, baseAddress, offset, add, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + + action(rt, rt2, tempRegister.Operand, 0); + } + } + } + + private static void EmitMemoryStrexInstruction(CodeGenContext context, uint rd, uint rt, uint rn, Action<Operand, Operand, Operand> action) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, rnOperand); + + action(rdOperand, rtOperand, tempRegister.Operand); + } + + private static void EmitMemoryDWordStrexInstruction(CodeGenContext context, uint rd, uint rt, uint rt2, uint rn, Action<Operand, Operand, Operand, Operand> action) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + Operand rt2Operand = InstEmitCommon.GetInputGpr(context, rt2); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, rnOperand); + + action(rdOperand, rtOperand, rt2Operand, tempRegister.Operand); + } + + private static void EmitMemoryInstruction( + CodeGenContext context, + Action<Operand, Operand, int> writeInst, + Action<Operand, Operand, int> writeInstUnscaled, + Operand rt, + Operand baseAddress, + Operand offset, + int scale, + bool index, + bool add, + bool wBack, + ArmShiftType shiftType = ArmShiftType.Lsl, + int shift = 0) + { + Assembler asm = context.Arm64Assembler; + RegisterAllocator regAlloc = context.RegisterAllocator; + + if (index && !wBack) + { + // Offset. + + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + int signedOffs = add ? offset.AsInt32() : -offset.AsInt32(); + int offs = 0; + bool unscaled = false; + + if (offset.Kind == OperandKind.Constant && offset.Value == 0) + { + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + } + else if (offset.Kind == OperandKind.Constant && shift == 0 && CanFoldOffset(context.MemoryManagerType, signedOffs, scale, writeInstUnscaled != null, out unscaled)) + { + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + offs = signedOffs; + } + else + { + WriteAddShiftOffset(asm, tempRegister.Operand, baseAddress, offset, add, shiftType, shift); + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, tempRegister.Operand); + } + + if (unscaled) + { + writeInstUnscaled(rt, tempRegister.Operand, offs); + } + else + { + writeInst(rt, tempRegister.Operand, offs); + } + } + else if (context.IsThumb ? !index && wBack : !index && !wBack) + { + // Post-indexed. + + if (rt.Type == offset.Type && rt.Value == offset.Value) + { + // If Rt and Rm are the same register, we must ensure we add the register offset (Rm) + // before the value is loaded, otherwise we will be adding the wrong value. + + if (rt.Type != baseAddress.Type || rt.Value != baseAddress.Value) + { + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + WriteAddShiftOffset(asm, baseAddress, baseAddress, offset, add, shiftType, shift); + + writeInst(rt, tempRegister.Operand, 0); + } + else + { + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + using ScopedRegister tempRegister2 = regAlloc.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + WriteAddShiftOffset(asm, tempRegister2.Operand, baseAddress, offset, add, shiftType, shift); + + writeInst(rt, tempRegister.Operand, 0); + + asm.Mov(baseAddress, tempRegister2.Operand); + } + } + else + { + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + + writeInst(rt, tempRegister.Operand, 0); + + WriteAddShiftOffset(asm, baseAddress, baseAddress, offset, add, shiftType, shift); + } + } + else if (index && wBack) + { + // Pre-indexed. + + using ScopedRegister tempRegister = regAlloc.AllocateTempGprRegisterScoped(); + + if (rt.Value == baseAddress.Value) + { + // If Rt and Rn are the same register, ensure we perform the write back after the read/write. + + WriteAddShiftOffset(asm, tempRegister.Operand, baseAddress, offset, add, shiftType, shift); + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, tempRegister.Operand); + + writeInst(rt, tempRegister.Operand, 0); + + context.Arm64Assembler.Mov(baseAddress, tempRegister.Operand); + } + else + { + WriteAddShiftOffset(asm, baseAddress, baseAddress, offset, add, shiftType, shift); + WriteAddressTranslation(context.MemoryManagerType, regAlloc, asm, tempRegister.Operand, baseAddress); + + writeInst(rt, tempRegister.Operand, 0); + } + } + else + { + Debug.Fail($"Invalid pre-index and write-back combination."); + } + } + + private static void EmitMemoryLiteralInstruction(CodeGenContext context, uint rt, uint imm, int scale, bool p, bool u, bool w, Action<Operand, Operand, int> action) + { + if (!p || w) + { + EmitMemoryInstruction(context, rt, RegisterUtils.PcRegister, (int)imm, scale, p, u, w, isStore: false, action, null); + + return; + } + + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + uint targetAddress = context.Pc & ~3u; + + if (u) + { + targetAddress += imm; + } + else + { + targetAddress -= imm; + } + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, targetAddress); + + action(rtOperand, tempRegister.Operand, 0); + } + + private static void EmitMemoryDWordLiteralInstruction(CodeGenContext context, uint rt, uint rt2, uint imm, bool p, bool u, bool w, Action<Operand, Operand, Operand, int> action) + { + if (!p || w) + { + EmitMemoryDWordInstructionI(context, rt, rt2, RegisterUtils.PcRegister, imm, p, u, w, isStore: false, action); + + return; + } + + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + Operand rt2Operand = InstEmitCommon.GetOutputGpr(context, rt2); + uint targetAddress = context.Pc & ~3u; + + if (u) + { + targetAddress += imm; + } + else + { + targetAddress -= imm; + } + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, targetAddress); + + action(rtOperand, rt2Operand, tempRegister.Operand, 0); + } + + private static void EmitMemoryPrefetchInstruction(CodeGenContext context, uint rn, uint imm, bool u, PrefetchType type) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + int signedOffs = u ? (int)imm : -(int)imm; + int offs = 0; + bool unscaled = false; + + if (imm == 0) + { + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, rnOperand); + } + else if (CanFoldOffset(context.MemoryManagerType, signedOffs, 3, true, out unscaled)) + { + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, rnOperand); + offs = signedOffs; + } + else + { + WriteAddShiftOffset(context.Arm64Assembler, tempRegister.Operand, rnOperand, InstEmitCommon.Const((int)imm), u, ArmShiftType.Lsl, 0); + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, tempRegister.Operand); + } + + if (unscaled) + { + context.Arm64Assembler.Prfum(tempRegister.Operand, offs, (uint)type, 0, 0); + } + else + { + context.Arm64Assembler.PrfmI(tempRegister.Operand, offs, (uint)type, 0, 0); + } + } + + private static void EmitMemoryPrefetchInstruction(CodeGenContext context, uint rn, uint rm, bool u, uint sType, uint shift, PrefetchType type) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddShiftOffset(context.Arm64Assembler, tempRegister.Operand, rnOperand, rmOperand, u, (ArmShiftType)sType, (int)shift); + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, tempRegister.Operand); + + context.Arm64Assembler.PrfmI(tempRegister.Operand, 0, (uint)type, 0, 0); + } + + private static void EmitMemoryPrefetchLiteralInstruction(CodeGenContext context, uint imm, bool u, PrefetchType type) + { + uint targetAddress = context.Pc & ~3u; + + if (u) + { + targetAddress += imm; + } + else + { + targetAddress -= imm; + } + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, targetAddress); + + context.Arm64Assembler.PrfmI(tempRegister.Operand, 0, (uint)type, 0, 0); + } + + public static bool CanFoldOffset(MemoryManagerType mmType, int offset, int scale, bool hasUnscaled, out bool unscaled) + { + if (mmType != MemoryManagerType.HostMappedUnsafe) + { + unscaled = false; + + return false; + } + + int mask = (1 << scale) - 1; + + if ((offset & mask) == 0 && offset >= 0 && offset < 0x1000) + { + // We can use the unsigned, scaled encoding. + + unscaled = false; + + return true; + } + + // Check if we can use the signed, unscaled encoding. + + unscaled = hasUnscaled && offset >= -0x100 && offset < 0x100; + + return unscaled; + } + + private static bool CanFoldDWordOffset(MemoryManagerType mmType, int offset) + { + if (mmType != MemoryManagerType.HostMappedUnsafe) + { + return false; + } + + return offset >= 0 && offset < 0x40 && (offset & 3) == 0; + } + + private static void WriteAddressTranslation(MemoryManagerType mmType, RegisterAllocator regAlloc, in Assembler asm, Operand destination, uint guestAddress) + { + asm.Mov(destination, guestAddress); + + WriteAddressTranslation(mmType, regAlloc, asm, destination, destination); + } + + public static void WriteAddressTranslation(MemoryManagerType mmType, RegisterAllocator regAlloc, in Assembler asm, Operand destination, Operand guestAddress) + { + Operand destination64 = new(destination.Kind, OperandType.I64, destination.Value); + Operand basePointer = new(regAlloc.FixedPageTableRegister, RegisterType.Integer, OperandType.I64); + + if (mmType == MemoryManagerType.HostMapped || mmType == MemoryManagerType.HostMappedUnsafe) + { + // We don't need to mask the address for the safe mode, since it is already naturally limited to 32-bit + // and can never reach out of the guest address space. + + asm.Add(destination64, basePointer, guestAddress); + } + else + { + throw new NotImplementedException(mmType.ToString()); + } + } + + public static void WriteAddShiftOffset(in Assembler asm, Operand rd, Operand rn, Operand offset, bool add, ArmShiftType shiftType, int shift) + { + Debug.Assert(offset.Kind != OperandKind.Constant || offset.AsInt32() >= 0); + + if (shiftType == ArmShiftType.Ror) + { + asm.Ror(rd, rn, InstEmitCommon.Const(shift & 31)); + + if (add) + { + asm.Add(rd, rd, offset, ArmShiftType.Lsl, 0); + } + else + { + asm.Sub(rd, rd, offset, ArmShiftType.Lsl, 0); + } + } + else + { + if (add) + { + asm.Add(rd, rn, offset, shiftType, shift); + } + else + { + asm.Sub(rd, rn, offset, shiftType, shift); + } + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs new file mode 100644 index 00000000..88850cb3 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs @@ -0,0 +1,350 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitMove + { + public static void MvnI(CodeGenContext context, uint rd, uint imm, bool immRotated, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + + if (s) + { + 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(1 << 29)); + } + else + { + context.Arm64Assembler.Bfc(flagsRegister.Operand, 29, 1); + } + } + + context.Arm64Assembler.Mov(rdOperand, ~imm); + context.Arm64Assembler.Tst(rdOperand, rdOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + else + { + context.Arm64Assembler.Mov(rdOperand, ~imm); + } + } + + public static void MvnR(CodeGenContext context, uint rd, uint rm, uint sType, uint imm5, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + ScopedRegister flagsRegister = default; + + if (s) + { + flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand); + } + else + { + rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType); + } + + context.Arm64Assembler.Mvn(rdOperand, rmOperand); + + if (s) + { + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + flagsRegister.Dispose(); + + context.SetNzcvModified(); + } + } + + public static void MvnRr(CodeGenContext context, uint rd, uint rm, uint sType, uint rs, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + ScopedRegister flagsRegister = default; + + if (s) + { + flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = InstEmitAlu.GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand); + } + else + { + rmOperand = InstEmitAlu.GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType); + } + + context.Arm64Assembler.Mvn(rdOperand, rmOperand); + + if (s) + { + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + flagsRegister.Dispose(); + + context.SetNzcvModified(); + } + } + + public static void MovI(CodeGenContext context, uint rd, uint imm, bool immRotated, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + + if (s) + { + 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); + } + } + + context.Arm64Assembler.Mov(rdOperand, imm); + context.Arm64Assembler.Tst(rdOperand, rdOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + else + { + context.Arm64Assembler.Mov(rdOperand, imm); + } + } + + public static void MovR(CodeGenContext context, uint rd, uint rm, uint sType, uint imm5, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (InstEmitAlu.CanShift(sType, imm5) && !s) + { + if (imm5 != 0) + { + switch ((ArmShiftType)sType) + { + case ArmShiftType.Lsl: + context.Arm64Assembler.Lsl(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + case ArmShiftType.Lsr: + context.Arm64Assembler.Lsr(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + case ArmShiftType.Asr: + context.Arm64Assembler.Asr(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + case ArmShiftType.Ror: + context.Arm64Assembler.Ror(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + } + } + else + { + context.Arm64Assembler.Mov(rdOperand, rmOperand); + } + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + ScopedRegister flagsRegister = default; + + if (s) + { + flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand); + } + else + { + rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, null); + } + + context.Arm64Assembler.Mov(rdOperand, rmOperand); + + if (s) + { + context.Arm64Assembler.Tst(rdOperand, rdOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + flagsRegister.Dispose(); + + context.SetNzcvModified(); + } + } + } + + public static void MovR(CodeGenContext context, uint cond, uint rd, uint rm, uint sType, uint imm5, bool s) + { + if (context.ConsumeSkipNextInstruction()) + { + return; + } + + if ((ArmCondition)cond >= ArmCondition.Al || s) + { + MovR(context, rd, rm, sType, imm5, s); + + return; + } + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (InstEmitAlu.CanShift(sType, imm5)) + { + if (imm5 != 0) + { + switch ((ArmShiftType)sType) + { + case ArmShiftType.Lsl: + context.Arm64Assembler.Lsl(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + case ArmShiftType.Lsr: + context.Arm64Assembler.Lsr(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + case ArmShiftType.Asr: + context.Arm64Assembler.Asr(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + case ArmShiftType.Ror: + context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5)); + break; + } + + context.Arm64Assembler.Csel(rdOperand, tempRegister.Operand, rdOperand, (ArmCondition)cond); + } + else + { + Operand other = rdOperand; + + InstInfo nextInstruction = context.PeekNextInstruction(); + + if (nextInstruction.Name == InstName.MovR) + { + // If this instruction is followed by another move with the inverse condition, + // we can just put it into the second operand of the CSEL instruction and skip the next move. + + InstCondb28w4Sb20w1Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 nextInst = new(nextInstruction.Encoding); + + if (nextInst.Rd == rd && + nextInst.S == 0 && + nextInst.Stype == 0 && + nextInst.Imm5 == 0 && + nextInst.Cond == (cond ^ 1u) && + nextInst.Rm != RegisterUtils.PcRegister) + { + other = InstEmitCommon.GetInputGpr(context, nextInst.Rm); + context.SetSkipNextInstruction(); + } + } + + context.Arm64Assembler.Csel(rdOperand, rmOperand, other, (ArmCondition)cond); + } + } + else + { + rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, null); + + context.Arm64Assembler.Csel(rdOperand, rmOperand, rdOperand, (ArmCondition)cond); + } + } + + public static void MovRr(CodeGenContext context, uint rd, uint rm, uint sType, uint rs, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs); + + if (!s) + { + InstEmitAlu.GetMShiftedByReg(context, rdOperand, rmOperand, rsOperand, sType); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = InstEmitAlu.GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand); + + context.Arm64Assembler.Mov(rdOperand, rmOperand); + context.Arm64Assembler.Tst(rdOperand, rdOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + } + + public static void Movt(CodeGenContext context, uint rd, uint imm) + { + Operand rdOperand = InstEmitCommon.GetInputGpr(context, rd); + + context.Arm64Assembler.Movk(rdOperand, (int)imm, 1); + } + + public static void Pkh(CodeGenContext context, uint rd, uint rn, uint rm, bool tb, uint imm5) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (!tb && imm5 == 0) + { + context.Arm64Assembler.Extr(rdOperand, rnOperand, rmOperand, 16); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + if (tb) + { + context.Arm64Assembler.Asr(tempRegister.Operand, rmOperand, InstEmitCommon.Const(imm5 == 0 ? 31 : (int)imm5)); + context.Arm64Assembler.Extr(rdOperand, tempRegister.Operand, rnOperand, 16); + } + else + { + context.Arm64Assembler.Lsl(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5)); + context.Arm64Assembler.Extr(rdOperand, rnOperand, tempRegister.Operand, 16); + } + } + + context.Arm64Assembler.Ror(rdOperand, rdOperand, InstEmitCommon.Const(16)); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMultiply.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMultiply.cs new file mode 100644 index 00000000..042ab815 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMultiply.cs @@ -0,0 +1,603 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using System; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitMultiply + { + public static void Mla(CodeGenContext context, uint rd, uint rn, uint rm, uint ra) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + context.Arm64Assembler.Madd(rdOperand, rnOperand, rmOperand, raOperand); + } + + public static void Mls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + context.Arm64Assembler.Msub(rdOperand, rnOperand, rmOperand, raOperand); + } + + public static void Mul(CodeGenContext context, uint rd, uint rn, uint rm, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (s) + { + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + context.Arm64Assembler.Mul(rdOperand, rnOperand, rmOperand); + context.Arm64Assembler.Tst(rdOperand, rdOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + else + { + context.Arm64Assembler.Mul(rdOperand, rnOperand, rmOperand); + } + } + + public static void Smlabb(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool nHigh, bool mHigh) + { + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh); + SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); + + context.Arm64Assembler.Sxtw(tempA64, raOperand); + context.Arm64Assembler.Smaddl(tempN.Operand, tempN.Operand, tempM.Operand, tempA64); + + CheckResultOverflow(context, tempM64, tempN.Operand); + + context.Arm64Assembler.Mov(rdOperand, tempN.Operand); + } + + public static void Smlad(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x) + { + EmitSmladSmlsd(context, rd, rn, rm, ra, x, add: true); + } + + public static void Smlal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) + { + EmitMultiplyAddLong(context, context.Arm64Assembler.Smaddl, rdLo, rdHi, rn, rm, s); + } + + public static void Smlalbb(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool nHigh, bool mHigh) + { + Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); + Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); + Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); + + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh); + SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); + + Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); + + context.Arm64Assembler.Lsl(tempA64, rdHiOperand64, InstEmitCommon.Const(32)); + context.Arm64Assembler.Orr(tempA64, tempA64, rdLoOperand); + + context.Arm64Assembler.Smaddl(rdLoOperand64, tempN.Operand, tempM.Operand, tempA64); + + if (rdLo != rdHi) + { + context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); + } + + context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. + } + + public static void Smlald(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x) + { + EmitSmlaldSmlsld(context, rdLo, rdHi, rn, rm, x, add: true); + } + + public static void Smlawb(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool mHigh) + { + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); + + context.Arm64Assembler.Sxtw(tempA64, raOperand); + context.Arm64Assembler.Lsl(tempA64, tempA64, InstEmitCommon.Const(16)); + context.Arm64Assembler.Smaddl(tempN.Operand, rnOperand, tempM.Operand, tempA64); + context.Arm64Assembler.Asr(tempN64, tempN64, InstEmitCommon.Const(16)); + + CheckResultOverflow(context, tempM64, tempN.Operand); + + context.Arm64Assembler.Mov(rdOperand, tempN.Operand); + } + + public static void Smlsd(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x) + { + EmitSmladSmlsd(context, rd, rn, rm, ra, x, add: false); + } + + public static void Smlsld(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x) + { + EmitSmlaldSmlsld(context, rdLo, rdHi, rn, rm, x, add: false); + } + + public static void Smmla(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r) + { + EmitSmmlaSmmls(context, rd, rn, rm, ra, r, add: true); + } + + public static void Smmls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r) + { + EmitSmmlaSmmls(context, rd, rn, rm, ra, r, add: false); + } + + public static void Smmul(CodeGenContext context, uint rd, uint rn, uint rm, bool r) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); + + context.Arm64Assembler.Smull(rdOperand64, rnOperand, rmOperand); + + if (r) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempRegister.Operand, 0x80000000u); + context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempRegister.Operand); + } + + context.Arm64Assembler.Lsr(rdOperand64, rdOperand64, InstEmitCommon.Const(32)); + } + + public static void Smuad(CodeGenContext context, uint rd, uint rn, uint rm, bool x) + { + EmitSmuadSmusd(context, rd, rn, rm, x, add: true); + } + + public static void Smulbb(CodeGenContext context, uint rd, uint rn, uint rm, bool nHigh, bool mHigh) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); + + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh); + SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); + + context.Arm64Assembler.Smull(rdOperand64, tempN.Operand, tempM.Operand); + + context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend. + } + + public static void Smull(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) + { + EmitMultiplyLong(context, context.Arm64Assembler.Smull, rdLo, rdHi, rn, rm, s); + } + + public static void Smulwb(CodeGenContext context, uint rd, uint rn, uint rm, bool mHigh) + { + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); + + context.Arm64Assembler.Smull(tempN.Operand, rnOperand, tempM.Operand); + context.Arm64Assembler.Asr(tempN64, tempN64, InstEmitCommon.Const(16)); + + CheckResultOverflow(context, tempM64, tempN.Operand); + + context.Arm64Assembler.Mov(rdOperand, tempN.Operand); + } + + public static void Smusd(CodeGenContext context, uint rd, uint rn, uint rm, bool x) + { + EmitSmuadSmusd(context, rd, rn, rm, x, add: false); + } + + public static void Umaal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm) + { + Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); + Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); + Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); + + if (rdLo == rdHi) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempRegister64 = new(OperandKind.Register, OperandType.I64, tempRegister.Operand.Value); + + context.Arm64Assembler.Umaddl(tempRegister64, rnOperand, rmOperand, rdLoOperand64); + context.Arm64Assembler.Add(rdLoOperand64, tempRegister64, rdHiOperand64); + } + else + { + context.Arm64Assembler.Umaddl(rdLoOperand64, rnOperand, rmOperand, rdLoOperand64); + context.Arm64Assembler.Add(rdLoOperand64, rdLoOperand64, rdHiOperand64); + } + + if (rdLo != rdHi) + { + context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); + } + + context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. + } + + public static void Umlal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) + { + EmitMultiplyAddLong(context, context.Arm64Assembler.Umaddl, rdLo, rdHi, rn, rm, s); + } + + public static void Umull(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) + { + EmitMultiplyLong(context, context.Arm64Assembler.Umull, rdLo, rdHi, rn, rm, s); + } + + private static void EmitMultiplyLong(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rdLo, uint rdHi, uint rn, uint rm, bool s) + { + Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); + Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); + Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); + + if (s) + { + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + action(rdLoOperand64, rnOperand, rmOperand); + context.Arm64Assembler.Tst(rdLoOperand64, rdLoOperand64); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + } + else + { + action(rdLoOperand64, rnOperand, rmOperand); + } + + if (rdLo != rdHi) + { + context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); + } + + context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. + } + + private static void EmitMultiplyAddLong(CodeGenContext context, Action<Operand, Operand, Operand, Operand> action, uint rdLo, uint rdHi, uint rn, uint rm, bool s) + { + Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); + Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); + Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); + + using ScopedRegister raRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand raOperand64 = new(OperandKind.Register, OperandType.I64, raRegister.Operand.Value); + + context.Arm64Assembler.Lsl(raOperand64, rdHiOperand64, InstEmitCommon.Const(32)); + context.Arm64Assembler.Orr(raOperand64, raOperand64, rdLoOperand); + + if (s) + { + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + action(rdLoOperand64, rnOperand, rmOperand, raOperand64); + context.Arm64Assembler.Tst(rdLoOperand64, rdLoOperand64); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + else + { + action(rdLoOperand64, rnOperand, rmOperand, raOperand64); + } + + if (rdLo != rdHi) + { + context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); + } + + context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. + } + + private static void EmitSmladSmlsd(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x, bool add) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); + + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); + + ScopedRegister swapTemp = default; + + if (x) + { + swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16)); + + rmOperand = swapTemp.Operand; + } + + context.Arm64Assembler.Sxth(tempN64, rnOperand); + context.Arm64Assembler.Sxth(tempM64, rmOperand); + context.Arm64Assembler.Sxtw(tempA64, raOperand); + + context.Arm64Assembler.Mul(rdOperand64, tempN64, tempM64); + + context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16)); + context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16)); + + if (add) + { + context.Arm64Assembler.Smaddl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); + } + else + { + context.Arm64Assembler.Smsubl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); + } + + context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempA64); + + CheckResultOverflow(context, tempM64, rdOperand64); + + context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend. + + if (x) + { + swapTemp.Dispose(); + } + } + + private static void EmitSmlaldSmlsld(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x, bool add) + { + Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); + Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); + Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); + + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); + + ScopedRegister swapTemp = default; + + if (x) + { + swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16)); + + rmOperand = swapTemp.Operand; + } + + context.Arm64Assembler.Sxth(tempN64, rnOperand); + context.Arm64Assembler.Sxth(tempM64, rmOperand); + + context.Arm64Assembler.Mul(rdLoOperand64, tempN64, tempM64); + + context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16)); + context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16)); + + if (add) + { + context.Arm64Assembler.Smaddl(rdLoOperand64, tempN.Operand, tempM.Operand, rdLoOperand64); + } + else + { + context.Arm64Assembler.Smsubl(rdLoOperand64, tempN.Operand, tempM.Operand, rdLoOperand64); + } + + context.Arm64Assembler.Lsl(tempA64, rdHiOperand64, InstEmitCommon.Const(32)); + context.Arm64Assembler.Orr(tempA64, tempA64, rdLoOperand); + + context.Arm64Assembler.Add(rdLoOperand64, rdLoOperand64, tempA64); + + if (rdLo != rdHi) + { + context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); + } + + context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. + + if (x) + { + swapTemp.Dispose(); + } + } + + private static void EmitSmmlaSmmls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r, bool add) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); + + Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); + Operand raOperand64 = new(OperandKind.Register, OperandType.I64, raOperand.Value); + + using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); + + context.Arm64Assembler.Lsl(tempA64, raOperand64, InstEmitCommon.Const(32)); + + if (add) + { + context.Arm64Assembler.Smaddl(rdOperand64, rnOperand, rmOperand, tempA64); + } + else + { + context.Arm64Assembler.Smsubl(rdOperand64, rnOperand, rmOperand, tempA64); + } + + if (r) + { + context.Arm64Assembler.Mov(tempA.Operand, 0x80000000u); + context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempA64); + } + + context.Arm64Assembler.Lsr(rdOperand64, rdOperand64, InstEmitCommon.Const(32)); + } + + private static void EmitSmuadSmusd(CodeGenContext context, uint rd, uint rn, uint rm, bool x, bool add) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); + + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + + ScopedRegister swapTemp = default; + + if (x) + { + swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16)); + + rmOperand = swapTemp.Operand; + } + + context.Arm64Assembler.Sxth(tempN64, rnOperand); + context.Arm64Assembler.Sxth(tempM64, rmOperand); + + context.Arm64Assembler.Mul(rdOperand64, tempN64, tempM64); + + context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16)); + context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16)); + + if (add) + { + context.Arm64Assembler.Smaddl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); + } + else + { + context.Arm64Assembler.Smsubl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); + } + + context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend. + + if (x) + { + swapTemp.Dispose(); + } + } + + private static void SelectSignedHalfword(CodeGenContext context, Operand dest, Operand source, bool high) + { + if (high) + { + context.Arm64Assembler.Asr(dest, source, InstEmitCommon.Const(16)); + } + else + { + context.Arm64Assembler.Sxth(dest, source); + } + } + + private static void CheckResultOverflow(CodeGenContext context, Operand temp64, Operand result) + { + context.Arm64Assembler.Sxtw(temp64, result); + context.Arm64Assembler.Sub(temp64, temp64, result); + + int branchIndex = context.CodeWriter.InstructionPointer; + + context.Arm64Assembler.Cbz(temp64, 0); + + // Set Q flag if we had an overflow. + InstEmitSaturate.SetQFlag(context); + + int delta = context.CodeWriter.InstructionPointer - branchIndex; + context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5)); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonArithmetic.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonArithmetic.cs new file mode 100644 index 00000000..a59f00fc --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonArithmetic.cs @@ -0,0 +1,344 @@ +using System; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonArithmetic + { + public static void Vaba(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Uaba : context.Arm64Assembler.Saba, null); + } + + public static void Vabal(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.Uabal : context.Arm64Assembler.Sabal); + } + + public static void VabdF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FabdV, context.Arm64Assembler.FabdVH); + } + + public static void VabdI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Uabd : context.Arm64Assembler.Sabd, null); + } + + public static void Vabdl(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.Uabdl : context.Arm64Assembler.Sabdl); + } + + public static void Vabs(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FabsSingleAndDouble, context.Arm64Assembler.FabsHalf); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.AbsV); + } + } + + public static void VaddF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FaddSingleAndDouble, context.Arm64Assembler.FaddHalf); + } + + public static void VaddI(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.AddV, context.Arm64Assembler.AddS); + } + + public static void Vaddhn(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryNarrow(context, rd, rn, rm, size, context.Arm64Assembler.Addhn); + } + + public static void Vaddl(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.Uaddl : context.Arm64Assembler.Saddl); + } + + public static void Vaddw(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryWide(context, rd, rn, rm, size, u ? context.Arm64Assembler.Uaddw : context.Arm64Assembler.Saddw); + } + + public static void VfmaF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRdF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FmlaVecSingleAndDouble, context.Arm64Assembler.FmlaVecHalf); + } + + public static void VfmsF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRdF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FmlsVecSingleAndDouble, context.Arm64Assembler.FmlsVecHalf); + } + + public static void Vhadd(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Uhadd : context.Arm64Assembler.Shadd, null); + } + + public static void Vhsub(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Uhsub : context.Arm64Assembler.Shsub, null); + } + + public static void Vmaxnm(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FmaxnmSingleAndDouble, context.Arm64Assembler.FmaxnmHalf); + } + + public static void VmaxF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FmaxSingleAndDouble, context.Arm64Assembler.FmaxHalf); + } + + public static void VmaxI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Umax : context.Arm64Assembler.Smax, null); + } + + public static void Vminnm(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FminnmSingleAndDouble, context.Arm64Assembler.FminnmHalf); + } + + public static void VminF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FminSingleAndDouble, context.Arm64Assembler.FminHalf); + } + + public static void VminI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Umin : context.Arm64Assembler.Smin, null); + } + + public static void VmlaF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryMulNegRdF(context, rd, rn, rm, sz, q, negProduct: false); + } + + public static void VmlaI(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, size, q, context.Arm64Assembler.MlaVec); + } + + public static void VmlaS(CodeGenContext context, uint rd, uint rn, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorTernaryMulNegRdByScalarAnyF(context, rd, rn, rm, size, q, negProduct: false); + } + else + { + InstEmitNeonCommon.EmitVectorTernaryRdByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.MlaElt); + } + } + + public static void VmlalI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorTernaryRdLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.UmlalVec : context.Arm64Assembler.SmlalVec); + } + + public static void VmlalS(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorTernaryRdLongByScalar(context, rd, rn, rm, size, u ? context.Arm64Assembler.UmlalElt : context.Arm64Assembler.SmlalElt); + } + + public static void VmlsF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryMulNegRdF(context, rd, rn, rm, sz, q, negProduct: true); + } + + public static void VmlsI(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, size, q, context.Arm64Assembler.MlsVec); + } + + public static void VmlsS(CodeGenContext context, uint rd, uint rn, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorTernaryMulNegRdByScalarAnyF(context, rd, rn, rm, size, q, negProduct: true); + } + else + { + InstEmitNeonCommon.EmitVectorTernaryRdByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.MlsElt); + } + } + + public static void VmlslI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorTernaryRdLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.UmlslVec : context.Arm64Assembler.SmlslVec); + } + + public static void VmlslS(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorTernaryRdLongByScalar(context, rd, rn, rm, size, u ? context.Arm64Assembler.UmlslElt : context.Arm64Assembler.SmlslElt); + } + + public static void VmulF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FmulVecSingleAndDouble, context.Arm64Assembler.FmulVecHalf); + } + + public static void VmulI(CodeGenContext context, uint rd, uint rn, uint rm, bool op, uint size, uint q) + { + if (op) + { + // TODO: Feature check, emulation if not supported. + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.Pmul, null); + } + else + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.MulVec, null); + } + } + + public static void VmulS(CodeGenContext context, uint rd, uint rn, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorBinaryByScalarAnyF(context, rd, rn, rm, size, q, context.Arm64Assembler.FmulElt2regElementSingleAndDouble, context.Arm64Assembler.FmulElt2regElementHalf); + } + else + { + InstEmitNeonCommon.EmitVectorBinaryByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.MulElt); + } + } + + public static void VmullI(CodeGenContext context, uint rd, uint rn, uint rm, bool op, bool u, uint size) + { + if (op) + { + // TODO: Feature check, emulation if not supported. + + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size == 2 ? 3 : size, context.Arm64Assembler.Pmull); + } + else + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.UmullVec : context.Arm64Assembler.SmullVec); + } + } + + public static void VmullS(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLongByScalar(context, rd, rn, rm, size, u ? context.Arm64Assembler.UmullElt : context.Arm64Assembler.SmullElt); + } + + public static void Vneg(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FnegSingleAndDouble, context.Arm64Assembler.FnegHalf); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.NegV); + } + } + + public static void Vpadal(CodeGenContext context, uint rd, uint rm, bool op, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryRd(context, rd, rm, size, q, op ? context.Arm64Assembler.Uadalp : context.Arm64Assembler.Sadalp); + } + + public static void VpaddF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FaddpVecSingleAndDouble, context.Arm64Assembler.FaddpVecHalf); + } + + public static void VpaddI(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.AddpVec, null); + } + + public static void Vpaddl(CodeGenContext context, uint rd, uint rm, bool op, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, op ? context.Arm64Assembler.Uaddlp : context.Arm64Assembler.Saddlp); + } + + public static void VpmaxF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FmaxpVecSingleAndDouble, context.Arm64Assembler.FmaxpVecHalf); + } + + public static void VpmaxI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Umaxp : context.Arm64Assembler.Smaxp, null); + } + + public static void VpminF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FminpVecSingleAndDouble, context.Arm64Assembler.FminpVecHalf); + } + + public static void VpminI(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Uminp : context.Arm64Assembler.Sminp, null); + } + + public static void Vrecpe(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrecpeV, context.Arm64Assembler.FrecpeVH); + } + else + { + throw new NotImplementedException(); + } + } + + public static void Vrecps(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FrecpsV, context.Arm64Assembler.FrecpsVH); + } + + public static void Vrsqrte(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrsqrteV, context.Arm64Assembler.FrsqrteVH); + } + else + { + throw new NotImplementedException(); + } + } + + public static void Vrsqrts(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FrsqrtsV, context.Arm64Assembler.FrsqrtsVH); + } + + public static void VsubF(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FsubSingleAndDouble, context.Arm64Assembler.FsubHalf); + } + + public static void VsubI(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.SubV, context.Arm64Assembler.SubS); + } + + public static void Vsubhn(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryNarrow(context, rd, rn, rm, size, context.Arm64Assembler.Subhn); + } + + public static void Vsubl(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, u ? context.Arm64Assembler.Usubl : context.Arm64Assembler.Ssubl); + } + + public static void Vsubw(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryWide(context, rd, rn, rm, size, u ? context.Arm64Assembler.Usubw : context.Arm64Assembler.Ssubw); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonBit.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonBit.cs new file mode 100644 index 00000000..9601f4c2 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonBit.cs @@ -0,0 +1,35 @@ +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonBit + { + public static void Vcls(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.Cls); + } + + public static void Vclz(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.Clz); + } + + public static void Vcnt(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.Cnt); + } + + public static void Vrev16(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.Rev16); + } + + public static void Vrev32(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.Rev32); + } + + public static void Vrev64(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.Rev64); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs new file mode 100644 index 00000000..dce6556e --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs @@ -0,0 +1,1513 @@ + +using Ryujinx.Cpu.LightningJit.CodeGen; +using System; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonCommon + { + public static ScopedRegister MoveScalarToSide(CodeGenContext context, uint srcReg, bool isFP32, bool forceAllocation = false) + { + int shift = isFP32 ? 2 : 1; + uint mask = isFP32 ? 3u : 1u; + uint elt = srcReg & mask; + + if (elt == 0 && !forceAllocation) + { + return new ScopedRegister(context.RegisterAllocator, context.RegisterAllocator.RemapFpRegister((int)(srcReg >> shift), isFP32), false); + } + + Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> shift)); + ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(isFP32); + + uint imm5 = GetImm5ForElementIndex(elt, isFP32); + + context.Arm64Assembler.DupEltScalarFromElement(tempRegister.Operand, source, imm5); + + return tempRegister; + } + + public static ScopedRegister Move16BitScalarToSide(CodeGenContext context, uint srcReg, bool top = false) + { + uint elt = srcReg & 3; + + Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> 2)); + ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(true); + + uint imm5 = GetImm5ForElementIndex((elt << 1) | (top ? 1u : 0u), 1); + + context.Arm64Assembler.DupEltScalarFromElement(tempRegister.Operand, source, imm5); + + return tempRegister; + } + + public static void MoveScalarToSide(CodeGenContext context, Operand dest, uint srcReg, bool isFP32) + { + int shift = isFP32 ? 2 : 1; + uint mask = isFP32 ? 3u : 1u; + uint elt = srcReg & mask; + + Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> shift)); + + uint imm5 = GetImm5ForElementIndex(elt, isFP32); + + context.Arm64Assembler.DupEltScalarFromElement(dest, source, imm5); + } + + public static ScopedRegister MoveScalarToSideIntoGpr(CodeGenContext context, uint srcReg, bool isFP32) + { + int shift = isFP32 ? 2 : 1; + uint mask = isFP32 ? 3u : 1u; + uint elt = srcReg & mask; + + Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> shift)); + ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Umov(tempRegister.Operand, source, (int)elt, isFP32 ? 2 : 3); + + return tempRegister; + } + + public static void InsertResult(CodeGenContext context, Operand source, uint dstReg, bool isFP32) + { + int shift = isFP32 ? 2 : 1; + uint mask = isFP32 ? 3u : 1u; + uint elt = dstReg & mask; + + uint imm5 = GetImm5ForElementIndex(elt, isFP32); + + Operand dest = context.RegisterAllocator.RemapSimdRegister((int)(dstReg >> shift)); + + context.Arm64Assembler.InsElt(dest, source, 0, imm5); + } + + public static void Insert16BitResult(CodeGenContext context, Operand source, uint dstReg, bool top = false) + { + uint elt = dstReg & 3u; + + uint imm5 = GetImm5ForElementIndex((elt << 1) | (top ? 1u : 0u), 1); + + Operand dest = context.RegisterAllocator.RemapSimdRegister((int)(dstReg >> 2)); + + context.Arm64Assembler.InsElt(dest, source, 0, imm5); + } + + public static void InsertResultFromGpr(CodeGenContext context, Operand source, uint dstReg, bool isFP32) + { + int shift = isFP32 ? 2 : 1; + uint mask = isFP32 ? 3u : 1u; + uint elt = dstReg & mask; + + uint imm5 = GetImm5ForElementIndex(elt, isFP32); + + Operand dest = context.RegisterAllocator.RemapSimdRegister((int)(dstReg >> shift)); + + context.Arm64Assembler.InsGen(dest, source, imm5); + } + + public static uint GetImm5ForElementIndex(uint elt, bool isFP32) + { + return isFP32 ? (4u | (elt << 3)) : (8u | (elt << 4)); + } + + public static uint GetImm5ForElementIndex(uint elt, uint size) + { + return (1u << (int)size) | (elt << ((int)size + 1)); + } + + public static void EmitScalarUnaryF(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + action(tempRegister.Operand, rmReg.Operand, size ^ 2u); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarUnaryF(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint> action, Action<Operand, Operand> actionHalf) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + if (size == 1) + { + actionHalf(tempRegister.Operand, rmReg.Operand); + } + else + { + action(tempRegister.Operand, rmReg.Operand, size & 1); + } + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarUnaryToGprTempF( + CodeGenContext context, + uint rd, + uint rm, + uint size, + uint sf, + Action<Operand, Operand, uint, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + action(tempRegister.Operand, rmReg.Operand, size ^ 2u, sf); + + InsertResultFromGpr(context, tempRegister.Operand, rd, sf == 0); + } + + public static void EmitScalarUnaryFromGprTempF( + CodeGenContext context, + uint rd, + uint rm, + uint size, + uint sf, + Action<Operand, Operand, uint, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rmReg = MoveScalarToSideIntoGpr(context, rm, sf == 0); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + action(tempRegister.Operand, rmReg.Operand, size ^ 2u, sf); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarUnaryFixedF(CodeGenContext context, uint rd, uint rm, uint fbits, uint size, bool is16Bit, Action<Operand, Operand, uint, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + (uint immb, uint immh) = GetImmbImmh(fbits, size); + + using ScopedRegister rmReg = is16Bit ? Move16BitScalarToSide(context, rm) : MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + action(tempRegister.Operand, rmReg.Operand, immb, immh); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarBinaryF(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg); + + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size ^ 2u); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarBinaryShift( + CodeGenContext context, + uint rd, + uint rm, + uint shift, + uint size, + bool isShl, + Action<Operand, Operand, uint, uint> action) + { + bool singleRegs = size != 3; + + (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl); + + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + action(tempRegister.Operand, rmReg.Operand, immb, immh); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarTernaryRdF(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, singleRegs); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size ^ 2u); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarTernaryRdF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + Action<Operand, Operand, Operand, Operand, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rdReg = MoveScalarToSide(context, rd, singleRegs); + using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rdReg, rnReg, rmReg); + + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, rdReg.Operand, size ^ 2u); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitScalarTernaryMulNegRdF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + bool negD, + bool negProduct) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, singleRegs); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister productRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + uint ftype = size ^ 2u; + + context.Arm64Assembler.FmulFloat(productRegister.Operand, rnReg.Operand, rmReg.Operand, ftype); + + if (negD) + { + context.Arm64Assembler.FnegFloat(tempRegister.Operand, tempRegister.Operand, ftype); + } + + if (negProduct) + { + context.Arm64Assembler.FnegFloat(productRegister.Operand, productRegister.Operand, ftype); + } + + context.Arm64Assembler.FaddFloat(tempRegister.Operand, tempRegister.Operand, productRegister.Operand, ftype); + + InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void EmitVectorUnary(CodeGenContext context, uint rd, uint rm, Action<Operand, Operand> action) + { + Debug.Assert(((rd | rm) & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rmOperand); + } + + public static void EmitVectorUnary(CodeGenContext context, uint rd, uint rm, uint q, Action<Operand, Operand, uint> action) + { + if (q == 0) + { + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + action(tempRegister.Operand, rmReg.Operand, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rmOperand, q); + } + } + + public static void EmitVectorUnary(CodeGenContext context, uint rd, uint rm, uint size, uint q, Action<Operand, Operand, uint, uint> action) + { + Debug.Assert(size < 3); + + if (q == 0) + { + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + action(tempRegister.Operand, rmReg.Operand, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rmOperand, size, q); + } + } + + public static void EmitVectorUnaryLong(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint, uint> action) + { + Debug.Assert((rd & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rm & 1; + + action(rdOperand, rmOperand, size, q); + } + + public static void EmitVectorUnaryNarrow(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint, uint> action) + { + Debug.Assert((rm & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rd & 1; + + if (q == 0) + { + // Writing to the lower half would clear the higher bits, we don't want that, so use a temp register and move the element. + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + action(tempRegister.Operand, rmOperand, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + action(rdOperand, rmOperand, size, q); + } + } + + public static void EmitVectorBinary(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> action) + { + Debug.Assert(((rd | rn | rm) & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rnOperand, rmOperand); + } + + public static void EmitVectorBinary(CodeGenContext context, uint rd, uint rn, uint rm, uint q, Action<Operand, Operand, Operand, uint> action) + { + if (q == 0) + { + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg); + + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rnOperand, rmOperand, q); + } + } + + public static void EmitVectorBinary( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, Operand, uint, uint> action, + Action<Operand, Operand, Operand, uint> actionScalar) + { + Debug.Assert(size <= 3); + + if (q == 0) + { + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg); + + if (size == 3) + { + actionScalar(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size); + } + else + { + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rnOperand, rmOperand, size, q); + } + } + + public static void EmitVectorBinaryRd(CodeGenContext context, uint rd, uint rm, uint size, uint q, Action<Operand, Operand, uint, uint> action) + { + Debug.Assert(size < 3); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + action(tempRegister.Operand, rmReg.Operand, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + + public static void EmitVectorBinaryShift( + CodeGenContext context, + uint rd, + uint rm, + uint shift, + uint size, + uint q, + bool isShl, + Action<Operand, Operand, uint, uint, uint> action, + Action<Operand, Operand, uint, uint> actionScalar) + { + (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl); + + if (q == 0) + { + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + if (size == 3) + { + actionScalar(tempRegister.Operand, rmReg.Operand, immb, immh); + } + else + { + action(tempRegister.Operand, rmReg.Operand, immb, immh, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rmOperand, immb, immh, q); + } + } + + public static void EmitVectorBinaryLong(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint, uint> action) + { + Debug.Assert((rd & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + if ((rn & 1) == (rm & 1)) + { + // Both inputs are on the same side of the vector, so we can use the variant that selects the half. + + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rn & 1; + + action(rdOperand, rnOperand, rmOperand, size, q); + } + else + { + // Inputs are on different sides of the vector, we have to move them. + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + action(rdOperand, rnReg.Operand, rmReg.Operand, size, 0); + } + } + + public static void EmitVectorBinaryLongShift( + CodeGenContext context, + uint rd, + uint rn, + uint shift, + uint size, + bool isShl, + Action<Operand, Operand, uint, uint, uint> action) + { + (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl); + + Debug.Assert((rd & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + + uint q = rn & 1; + + action(rdOperand, rnOperand, immb, immh, q); + } + + public static void EmitVectorBinaryLongByScalar( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action) + { + Debug.Assert((rd & 1) == 0); + + (uint h, uint l, uint m) = GetIndexForReg(ref rm, size); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rn & 1; + + action(rdOperand, rnOperand, h, rmOperand, m, l, size, q); + } + + public static void EmitVectorBinaryNarrow( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + Action<Operand, Operand, Operand, uint, uint> action) + { + Debug.Assert((rn & 1) == 0); + Debug.Assert((rm & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rd & 1; + + if (q == 0) + { + // Writing to the lower half would clear the higher bits, we don't want that, so use a temp register and move the element. + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + action(tempRegister.Operand, rnOperand, rmOperand, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + action(rdOperand, rnOperand, rmOperand, size, q); + } + } + + public static void EmitVectorBinaryNarrowShift( + CodeGenContext context, + uint rd, + uint rm, + uint shift, + uint size, + bool isShl, + Action<Operand, Operand, uint, uint, uint> action) + { + (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl); + + Debug.Assert((rm & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rd & 1; + + if (q == 0) + { + // Writing to the lower half would clear the higher bits, we don't want that, so use a temp register and move the element. + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + action(tempRegister.Operand, rmOperand, immb, immh, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + action(rdOperand, rmOperand, immb, immh, q); + } + } + + public static void EmitVectorBinaryWide(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint, uint> action) + { + Debug.Assert(((rd | rn) & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rm & 1; + + action(rdOperand, rnOperand, rmOperand, size, q); + } + + public static void EmitVectorBinaryByScalar( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action) + { + EmitVectorByScalarCore(context, rd, rn, rm, size, q, action, isTernary: false); + } + + public static void EmitVectorTernaryRd(CodeGenContext context, uint rd, uint rn, uint rm, uint q, Action<Operand, Operand, Operand, uint> action) + { + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rnOperand, rmOperand, q); + } + } + + public static void EmitVectorTernaryRd(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q, Action<Operand, Operand, Operand, uint, uint> action) + { + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rnOperand, rmOperand, size, q); + } + } + + public static void EmitVectorTernaryRdLong(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint, uint> action) + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + if ((rn & 1) == (rm & 1)) + { + // Both inputs are on the same side of the vector, so we can use the variant that selects the half. + + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rn & 1; + + action(rdOperand, rnOperand, rmOperand, size, q); + } + else + { + // Inputs are on different sides of the vector, we have to move them. + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + action(rdOperand, rnReg.Operand, rmReg.Operand, size, 0); + } + } + + public static void EmitVectorTernaryRdLongByScalar( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action) + { + (uint h, uint l, uint m) = GetIndexForReg(ref rm, size); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint q = rn & 1; + + action(rdOperand, rnOperand, h, rmOperand, m, l, size, q); + } + + public static void EmitVectorTernaryRdShift( + CodeGenContext context, + uint rd, + uint rm, + uint shift, + uint size, + uint q, + bool isShl, + Action<Operand, Operand, uint, uint, uint> action, + Action<Operand, Operand, uint, uint> actionScalar) + { + (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + if (size == 3) + { + actionScalar(tempRegister.Operand, rmReg.Operand, immb, immh); + } + else + { + action(tempRegister.Operand, rmReg.Operand, immb, immh, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rmOperand, immb, immh, q); + } + } + + public static void EmitVectorTernaryRdByScalar( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action) + { + EmitVectorByScalarCore(context, rd, rn, rm, size, q, action, isTernary: true); + } + + private static void EmitVectorByScalarCore( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action, + bool isTernary) + { + (uint h, uint l, uint m) = GetIndexForReg(ref rm, size); + + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (q == 0) + { + if (isTernary) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + + action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg); + + action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, size, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + + action(rdOperand, rnOperand, h, rmOperand, m, l, size, q); + } + } + + public static void EmitVectorUnaryF( + CodeGenContext context, + uint rd, + uint rm, + uint sz, + uint q, + Action<Operand, Operand, uint, uint> action, + Action<Operand, Operand, uint> actionHalf) + { + Debug.Assert(sz == 0 || sz == 1); + + if (q == 0) + { + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + if (sz == 1) + { + actionHalf(tempRegister.Operand, rmReg.Operand, q); + } + else + { + action(tempRegister.Operand, rmReg.Operand, 0, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (sz == 1) + { + actionHalf(rdOperand, rmOperand, q); + } + else + { + action(rdOperand, rmOperand, 0, q); + } + } + } + + public static void EmitVectorUnaryAnyF( + CodeGenContext context, + uint rd, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, uint> action, + Action<Operand, Operand, uint> actionHalf) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + Debug.Assert(size != 3 || q == 1); + + if (q == 0) + { + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + if (size == 1) + { + actionHalf(tempRegister.Operand, rmReg.Operand, q); + } + else + { + action(tempRegister.Operand, rmReg.Operand, size ^ 2u, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (size == 1) + { + actionHalf(rdOperand, rmOperand, q); + } + else + { + action(rdOperand, rmOperand, size ^ 2u, q); + } + } + } + + public static void EmitVectorUnaryFixedAnyF( + CodeGenContext context, + uint rd, + uint rm, + uint fbits, + uint size, + uint q, + Action<Operand, Operand, uint, uint, uint> action) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + Debug.Assert(size != 3 || q == 1); + + (uint immb, uint immh) = GetImmbImmh(fbits, size); + + if (q == 0) + { + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg); + + action(tempRegister.Operand, rmReg.Operand, immb, immh, q); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + action(rdOperand, rmOperand, immb, immh, q); + } + } + + public static void EmitVectorBinaryF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint sz, + uint q, + Action<Operand, Operand, Operand, uint, uint> action, + Action<Operand, Operand, Operand, uint> actionHalf) + { + Debug.Assert(sz == 0 || sz == 1); + + if (q == 0) + { + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg); + + if (sz == 1) + { + actionHalf(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q); + } + else + { + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, 0, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (sz == 1) + { + actionHalf(rdOperand, rnOperand, rmOperand, q); + } + else + { + action(rdOperand, rnOperand, rmOperand, 0, q); + } + } + } + + public static void EmitVectorBinaryByScalarAnyF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action, + Action<Operand, Operand, uint, Operand, uint, uint, uint> actionHalf) + { + EmitVectorByScalarAnyFCore(context, rd, rn, rm, size, q, action, actionHalf, isTernary: false); + } + + public static void EmitVectorTernaryRdF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint sz, + uint q, + Action<Operand, Operand, Operand, uint, uint> action, + Action<Operand, Operand, Operand, uint> actionHalf) + { + Debug.Assert(sz == 0 || sz == 1); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + if (sz == 1) + { + actionHalf(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q); + } + else + { + action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, 0, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (sz == 1) + { + actionHalf(rdOperand, rnOperand, rmOperand, q); + } + else + { + action(rdOperand, rnOperand, rmOperand, 0, q); + } + } + } + + public static void EmitVectorTernaryMulNegRdF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint sz, + uint q, + bool negProduct) + { + Debug.Assert(sz == 0 || sz == 1); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = MoveScalarToSide(context, rm, false); + + EmitMulNegVector(context, tempRegister.Operand, rnReg.Operand, rmReg.Operand, sz, q, negProduct); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + EmitMulNegVector(context, rdOperand, rnOperand, rmOperand, sz, q, negProduct); + } + } + + private static void EmitMulNegVector( + CodeGenContext context, + Operand rd, + Operand rn, + Operand rm, + uint sz, + uint q, + bool negProduct) + { + using ScopedRegister productRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (sz == 1) + { + context.Arm64Assembler.FmulVecHalf(productRegister.Operand, rn, rm, q); + + if (negProduct) + { + context.Arm64Assembler.FnegHalf(productRegister.Operand, productRegister.Operand, q); + } + + context.Arm64Assembler.FaddHalf(rd, rd, productRegister.Operand, q); + } + else + { + context.Arm64Assembler.FmulVecSingleAndDouble(productRegister.Operand, rn, rm, 0, q); + + if (negProduct) + { + context.Arm64Assembler.FnegSingleAndDouble(productRegister.Operand, productRegister.Operand, 0, q); + } + + context.Arm64Assembler.FaddSingleAndDouble(rd, rd, productRegister.Operand, 0, q); + } + } + + public static void EmitVectorTernaryRdByScalarAnyF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action, + Action<Operand, Operand, uint, Operand, uint, uint, uint> actionHalf) + { + EmitVectorByScalarAnyFCore(context, rd, rn, rm, size, q, action, actionHalf, isTernary: true); + } + + private static void EmitVectorByScalarAnyFCore( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action, + Action<Operand, Operand, uint, Operand, uint, uint, uint> actionHalf, + bool isTernary) + { + (uint h, uint l, uint m) = GetIndexForReg(ref rm, size); + + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (q == 0) + { + if (isTernary) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + + if (size == 1) + { + actionHalf(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, q); + } + else + { + action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, 0, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + + using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg); + + if (size == 1) + { + actionHalf(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, q); + } + else + { + action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, 0, q); + } + + InsertResult(context, tempRegister.Operand, rd, false); + } + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + + if (size == 1) + { + actionHalf(rdOperand, rnOperand, h, rmOperand, m, l, q); + } + else + { + action(rdOperand, rnOperand, h, rmOperand, m, l, 0, q); + } + } + } + + public static void EmitVectorTernaryMulNegRdByScalarAnyF( + CodeGenContext context, + uint rd, + uint rn, + uint rm, + uint size, + uint q, + bool negProduct) + { + (uint h, uint l, uint m) = GetIndexForReg(ref rm, size); + + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + MoveScalarToSide(context, tempRegister.Operand, rd, false); + + using ScopedRegister rnReg = MoveScalarToSide(context, rn, false); + + EmitMulNegVectorByScalar(context, tempRegister.Operand, rnReg.Operand, rmOperand, h, l, m, size, q, negProduct); + + InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + + EmitMulNegVectorByScalar(context, rdOperand, rnOperand, rmOperand, h, l, m, size, q, negProduct); + } + } + + private static void EmitMulNegVectorByScalar( + CodeGenContext context, + Operand rd, + Operand rn, + Operand rm, + uint h, + uint l, + uint m, + uint sz, + uint q, + bool negProduct) + { + using ScopedRegister productRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (sz == 1) + { + context.Arm64Assembler.FmulElt2regElementHalf(productRegister.Operand, rn, h, rm, m, l, q); + + if (negProduct) + { + context.Arm64Assembler.FnegHalf(productRegister.Operand, productRegister.Operand, q); + } + + context.Arm64Assembler.FaddHalf(rd, rd, productRegister.Operand, q); + } + else + { + context.Arm64Assembler.FmulElt2regElementSingleAndDouble(productRegister.Operand, rn, h, rm, m, l, 0, q); + + if (negProduct) + { + context.Arm64Assembler.FnegSingleAndDouble(productRegister.Operand, productRegister.Operand, 0, q); + } + + context.Arm64Assembler.FaddSingleAndDouble(rd, rd, productRegister.Operand, 0, q); + } + } + + private static (uint, uint, uint) GetIndexForReg(ref uint reg, uint size) + { + int shift = (int)(size + 2); + uint index = reg >> shift; + reg &= (1u << shift) - 1; + index |= (reg & 1) << (5 - shift); + + uint h, l, m; + + if (size == 1) + { + Debug.Assert((index >> 3) == 0); + + m = index & 1; + l = (index >> 1) & 1; + h = index >> 2; + } + else + { + Debug.Assert(size == 2); + Debug.Assert((index >> 2) == 0); + + m = 0; + l = index & 1; + h = (index >> 1) & 1; + } + + return (h, l, m); + } + + private static (uint, uint) GetImmbImmh(uint value, uint size) + { + Debug.Assert(value > 0 && value <= (8u << (int)size)); + + uint imm = (8u << (int)size) | ((8u << (int)size) - value); + + Debug.Assert((imm >> 7) == 0); + + uint immb = imm & 7; + uint immh = imm >> 3; + + return (immb, immh); + } + + public static (uint, uint) GetImmbImmhForShift(uint value, uint size, bool isShl) + { + if (isShl) + { + Debug.Assert(value >= 0 && value < (8u << (int)size)); + + uint imm = (8u << (int)size) | (value & (0x3fu >> (int)(3 - size))); + + Debug.Assert((imm >> 7) == 0); + + uint immb = imm & 7; + uint immh = imm >> 3; + + return (immb, immh); + } + else + { + return GetImmbImmh(value, size); + } + } + + public static uint GetSizeFromImm6(uint imm6) + { + if ((imm6 & 0b100000) != 0) + { + return 2; + } + else if ((imm6 & 0b10000) != 0) + { + return 1; + } + else + { + Debug.Assert((imm6 & 0b1000) != 0); + + return 0; + } + } + + public static uint GetSizeFromImm7(uint imm7) + { + if ((imm7 & 0b1000000) != 0) + { + return 3; + } + else if ((imm7 & 0b100000) != 0) + { + return 2; + } + else if ((imm7 & 0b10000) != 0) + { + return 1; + } + else + { + Debug.Assert((imm7 & 0b1000) != 0); + + return 0; + } + } + + public static ScopedRegister PickSimdRegister(RegisterAllocator registerAllocator, ScopedRegister option1) + { + if (option1.IsAllocated) + { + return option1; + } + + return registerAllocator.AllocateTempSimdRegisterScoped(); + } + + public static ScopedRegister PickSimdRegister(RegisterAllocator registerAllocator, ScopedRegister option1, ScopedRegister option2) + { + if (option1.IsAllocated) + { + return option1; + } + else if (option2.IsAllocated) + { + return option2; + } + + return registerAllocator.AllocateTempSimdRegisterScoped(); + } + + public static ScopedRegister PickSimdRegister(RegisterAllocator registerAllocator, ScopedRegister option1, ScopedRegister option2, ScopedRegister option3) + { + if (option1.IsAllocated) + { + return option1; + } + else if (option2.IsAllocated) + { + return option2; + } + else if (option3.IsAllocated) + { + return option3; + } + + return registerAllocator.AllocateTempSimdRegisterScoped(); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCompare.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCompare.cs new file mode 100644 index 00000000..8da99385 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCompare.cs @@ -0,0 +1,126 @@ +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonCompare + { + public static void Vacge(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FacgeV, context.Arm64Assembler.FacgeVH); + } + + public static void Vacgt(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FacgtV, context.Arm64Assembler.FacgtVH); + } + + public static void VceqI(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcmeqZeroV, context.Arm64Assembler.FcmeqZeroVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.CmeqZeroV); + } + } + + public static void VceqR(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.CmeqRegV, context.Arm64Assembler.CmeqRegS); + } + + public static void VceqFR(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FcmeqRegV, context.Arm64Assembler.FcmeqRegVH); + } + + public static void VcgeI(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcmgeZeroV, context.Arm64Assembler.FcmgeZeroVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.CmgeZeroV); + } + } + + public static void VcgeR(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary( + context, + rd, + rn, + rm, + size, + q, + u ? context.Arm64Assembler.CmhsV : context.Arm64Assembler.CmgeRegV, + u ? context.Arm64Assembler.CmhsS : context.Arm64Assembler.CmgeRegS); + } + + public static void VcgeFR(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FcmgeRegV, context.Arm64Assembler.FcmgeRegVH); + } + + public static void VcgtI(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcmgtZeroV, context.Arm64Assembler.FcmgtZeroVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.CmgtZeroV); + } + } + + public static void VcgtR(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary( + context, + rd, + rn, + rm, + size, + q, + u ? context.Arm64Assembler.CmhiV : context.Arm64Assembler.CmgtRegV, + u ? context.Arm64Assembler.CmhiS : context.Arm64Assembler.CmgtRegS); + } + + public static void VcgtFR(CodeGenContext context, uint rd, uint rn, uint rm, uint sz, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryF(context, rd, rn, rm, sz, q, context.Arm64Assembler.FcmgtRegV, context.Arm64Assembler.FcmgtRegVH); + } + + public static void VcleI(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcmleV, context.Arm64Assembler.FcmleVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.CmleV); + } + } + + public static void VcltI(CodeGenContext context, uint rd, uint rm, bool f, uint size, uint q) + { + if (f) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcmltV, context.Arm64Assembler.FcmltVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.CmltV); + } + } + + public static void Vtst(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.CmtstV, context.Arm64Assembler.CmtstS); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonConvert.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonConvert.cs new file mode 100644 index 00000000..81fce678 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonConvert.cs @@ -0,0 +1,137 @@ +using System; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonConvert + { + public static void Vcvta(CodeGenContext context, uint rd, uint rm, bool op, uint size, uint q) + { + if (op) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtauV, context.Arm64Assembler.FcvtauVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtasV, context.Arm64Assembler.FcvtasVH); + } + } + + public static void Vcvtm(CodeGenContext context, uint rd, uint rm, bool op, uint size, uint q) + { + if (op) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtmuV, context.Arm64Assembler.FcvtmuVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtmsV, context.Arm64Assembler.FcvtmsVH); + } + } + + public static void Vcvtn(CodeGenContext context, uint rd, uint rm, bool op, uint size, uint q) + { + if (op) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtnuV, context.Arm64Assembler.FcvtnuVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtnsV, context.Arm64Assembler.FcvtnsVH); + } + } + + public static void Vcvtp(CodeGenContext context, uint rd, uint rm, bool op, uint size, uint q) + { + if (op) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtpuV, context.Arm64Assembler.FcvtpuVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtpsV, context.Arm64Assembler.FcvtpsVH); + } + } + + public static void VcvtHs(CodeGenContext context, uint rd, uint rm, bool op) + { + bool halfToSingle = op; + if (halfToSingle) + { + // Half to single. + + InstEmitNeonCommon.EmitVectorUnaryLong(context, rd, rm, 0, context.Arm64Assembler.Fcvtl); + } + else + { + // Single to half. + + InstEmitNeonCommon.EmitVectorUnaryNarrow(context, rd, rm, 0, context.Arm64Assembler.Fcvtn); + } + } + + public static void VcvtIs(CodeGenContext context, uint rd, uint rm, uint op, uint size, uint q) + { + Debug.Assert(op >> 2 == 0); + + bool unsigned = (op & 1) != 0; + bool toInteger = (op >> 1) != 0; + + if (toInteger) + { + if (unsigned) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtzuIntV, context.Arm64Assembler.FcvtzuIntVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FcvtzsIntV, context.Arm64Assembler.FcvtzsIntVH); + } + } + else + { + if (unsigned) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.UcvtfIntV, context.Arm64Assembler.UcvtfIntVH); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.ScvtfIntV, context.Arm64Assembler.ScvtfIntVH); + } + } + } + + public static void VcvtXs(CodeGenContext context, uint rd, uint rm, uint imm6, uint op, bool u, uint q) + { + Debug.Assert(op >> 2 == 0); + + bool unsigned = u; + bool toFixed = (op & 1) != 0; + uint size = 1 + (op >> 1); + uint fbits = Math.Clamp(64u - imm6, 1, 8u << (int)size); + + if (toFixed) + { + if (unsigned) + { + InstEmitNeonCommon.EmitVectorUnaryFixedAnyF(context, rd, rm, fbits, size, q, context.Arm64Assembler.FcvtzuFixV); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryFixedAnyF(context, rd, rm, fbits, size, q, context.Arm64Assembler.FcvtzsFixV); + } + } + else + { + if (unsigned) + { + InstEmitNeonCommon.EmitVectorUnaryFixedAnyF(context, rd, rm, fbits, size, q, context.Arm64Assembler.UcvtfFixV); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryFixedAnyF(context, rd, rm, fbits, size, q, context.Arm64Assembler.ScvtfFixV); + } + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCrypto.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCrypto.cs new file mode 100644 index 00000000..a36ae82c --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCrypto.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonCrypto + { + public static void Aesd(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 0); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Aesd); + } + + public static void Aese(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 0); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Aese); + } + + public static void Aesimc(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 0); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Aesimc); + } + + public static void Aesmc(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 0); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Aesmc); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonHash.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonHash.cs new file mode 100644 index 00000000..57090ac8 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonHash.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonHash + { + public static void Sha1c(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha1c); + } + + public static void Sha1h(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 2); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Sha1h); + } + + public static void Sha1m(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha1m); + } + + public static void Sha1p(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha1p); + } + + public static void Sha1su0(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha1su0); + } + + public static void Sha1su1(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 2); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Sha1su1); + } + + public static void Sha256h(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha256h); + } + + public static void Sha256h2(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha256h2); + } + + public static void Sha256su0(CodeGenContext context, uint rd, uint rm, uint size) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(size == 2); + + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, context.Arm64Assembler.Sha256su0); + } + + public static void Sha256su1(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + // TODO: Feature check, emulation if not supported. + + Debug.Assert(q == 1); + + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, context.Arm64Assembler.Sha256su1); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonLogical.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonLogical.cs new file mode 100644 index 00000000..af2e54cc --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonLogical.cs @@ -0,0 +1,79 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonLogical + { + public static void VandR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, q, context.Arm64Assembler.And); + } + + public static void VbicI(CodeGenContext context, uint rd, uint cmode, uint imm8, uint q) + { + EmitMovi(context, rd, cmode, imm8, 1, q); + } + + public static void VbicR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, q, context.Arm64Assembler.BicReg); + } + + public static void VbifR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, q, context.Arm64Assembler.Bif); + } + + public static void VbitR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, q, context.Arm64Assembler.Bit); + } + + public static void VbslR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, q, context.Arm64Assembler.Bsl); + } + + public static void VeorR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, q, context.Arm64Assembler.Eor); + } + + public static void VornR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, q, context.Arm64Assembler.Orn); + } + + public static void VorrI(CodeGenContext context, uint rd, uint cmode, uint imm8, uint q) + { + EmitMovi(context, rd, cmode, imm8, 0, q); + } + + public static void VorrR(CodeGenContext context, uint rd, uint rn, uint rm, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, q, context.Arm64Assembler.OrrReg); + } + + private static void EmitMovi(CodeGenContext context, uint rd, uint cmode, uint imm8, uint op, uint q) + { + (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) = InstEmitNeonMove.Split(imm8); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + InstEmitNeonCommon.MoveScalarToSide(context, tempRegister.Operand, rd, false); + + context.Arm64Assembler.Movi(tempRegister.Operand, h, g, f, e, d, cmode, c, b, a, op, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + context.Arm64Assembler.Movi(rdOperand, h, g, f, e, d, cmode, c, b, a, op, q); + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonMemory.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonMemory.cs new file mode 100644 index 00000000..e77dc0a2 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonMemory.cs @@ -0,0 +1,797 @@ +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 InstEmitNeonMemory + { + public static void Vld11(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + + EmitMemory1234InstructionCore(context, rn, rm, 1 << (int)size, (address) => + { + EmitMemoryLoad1234SingleInstruction(context, address, rd, index, size, 1, 1, context.Arm64Assembler.Ld1SnglAsNoPostIndex); + }); + } + + public static void Vld1A(CodeGenContext context, uint rd, uint rn, uint rm, uint a, uint t, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 1 << (int)size, (address) => + { + EmitMemoryLoad1SingleReplicateInstruction(context, address, rd, size, t + 1, 1, context.Arm64Assembler.Ld1rAsNoPostIndex); + }); + } + + public static void Vld1M(CodeGenContext context, uint rd, uint rn, uint rm, uint registersCount, uint align, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 8 * (int)registersCount, (address) => + { + EmitMemoryLoad1234MultipleInstruction(context, address, rd, size, registersCount, 1, context.Arm64Assembler.Ld1MultAsNoPostIndex); + }); + } + + public static void Vld21(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + uint step = size > 0 && (indexAlign & (1u << (int)size)) != 0 ? 2u : 1u; + + EmitMemory1234InstructionCore(context, rn, rm, 2 * (1 << (int)size), (address) => + { + EmitMemoryLoad1234SingleInstruction(context, address, rd, index, size, 2, step, context.Arm64Assembler.Ld2SnglAsNoPostIndex); + }); + } + + public static void Vld2A(CodeGenContext context, uint rd, uint rn, uint rm, uint a, uint t, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 2 * (1 << (int)size), (address) => + { + EmitMemoryLoad234SingleReplicateInstruction(context, address, rd, size, 2, t + 1, context.Arm64Assembler.Ld2rAsNoPostIndex); + }); + } + + public static void Vld2M(CodeGenContext context, uint rd, uint rn, uint rm, uint type, uint align, uint size) + { + uint step = (type & 1) + 1; + + EmitMemory1234InstructionCore(context, rn, rm, 16, (address) => + { + EmitMemoryLoad1234MultipleInstruction(context, address, rd, size, 2, step, context.Arm64Assembler.Ld2MultAsNoPostIndex); + }); + } + + public static void Vld2M(CodeGenContext context, uint rd, uint rn, uint rm, uint align, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 32, (address) => + { + EmitMemoryLoad1234Multiple2x2Instruction(context, address, rd, size, context.Arm64Assembler.Ld2MultAsNoPostIndex); + }); + } + + public static void Vld31(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + uint step = size > 0 && (indexAlign & (1u << (int)size)) != 0 ? 2u : 1u; + + EmitMemory1234InstructionCore(context, rn, rm, 3 * (1 << (int)size), (address) => + { + EmitMemoryLoad1234SingleInstruction(context, address, rd, index, size, 3, step, context.Arm64Assembler.Ld3SnglAsNoPostIndex); + }); + } + + public static void Vld3A(CodeGenContext context, uint rd, uint rn, uint rm, uint a, uint t, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 3 * (1 << (int)size), (address) => + { + EmitMemoryLoad234SingleReplicateInstruction(context, address, rd, size, 3, t + 1, context.Arm64Assembler.Ld3rAsNoPostIndex); + }); + } + + public static void Vld3M(CodeGenContext context, uint rd, uint rn, uint rm, uint type, uint align, uint size) + { + uint step = (type & 1) + 1; + + EmitMemory1234InstructionCore(context, rn, rm, 24, (address) => + { + EmitMemoryLoad1234MultipleInstruction(context, address, rd, size, 3, step, context.Arm64Assembler.Ld3MultAsNoPostIndex); + }); + } + + public static void Vld41(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + uint step = size > 0 && (indexAlign & (1u << (int)size)) != 0 ? 2u : 1u; + + EmitMemory1234InstructionCore(context, rn, rm, 4 * (1 << (int)size), (address) => + { + EmitMemoryLoad1234SingleInstruction(context, address, rd, index, size, 4, step, context.Arm64Assembler.Ld4SnglAsNoPostIndex); + }); + } + + public static void Vld4A(CodeGenContext context, uint rd, uint rn, uint rm, uint a, uint t, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 4 * (1 << (int)size), (address) => + { + EmitMemoryLoad234SingleReplicateInstruction(context, address, rd, size, 4, t + 1, context.Arm64Assembler.Ld4rAsNoPostIndex); + }); + } + + public static void Vld4M(CodeGenContext context, uint rd, uint rn, uint rm, uint type, uint align, uint size) + { + uint step = (type & 1) + 1; + + EmitMemory1234InstructionCore(context, rn, rm, 32, (address) => + { + EmitMemoryLoad1234MultipleInstruction(context, address, rd, size, 4, step, context.Arm64Assembler.Ld4MultAsNoPostIndex); + }); + } + + public static void Vldm(CodeGenContext context, uint rd, uint rn, uint registerCount, bool u, bool w, bool singleRegs) + { + EmitMemoryMultipleInstruction(context, rd, rn, registerCount, u, w, singleRegs, isStore: false); + } + + public static void Vldr(CodeGenContext context, uint rd, uint rn, uint imm8, bool u, uint size) + { + EmitMemoryInstruction(context, rd, rn, imm8, u, size, isStore: false); + } + + public static void Vst11(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + + EmitMemory1234InstructionCore(context, rn, rm, 1 << (int)size, (address) => + { + EmitMemoryStore1234SingleInstruction(context, address, rd, index, size, 1, 1, context.Arm64Assembler.St1SnglAsNoPostIndex); + }); + } + + public static void Vst1M(CodeGenContext context, uint rd, uint rn, uint rm, uint registersCount, uint align, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 8 * (int)registersCount, (address) => + { + EmitMemoryStore1234MultipleInstruction(context, address, rd, size, registersCount, 1, context.Arm64Assembler.St1MultAsNoPostIndex); + }); + } + + public static void Vst21(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + uint step = size > 0 && (indexAlign & (1u << (int)size)) != 0 ? 2u : 1u; + + EmitMemory1234InstructionCore(context, rn, rm, 2 * (1 << (int)size), (address) => + { + EmitMemoryStore1234SingleInstruction(context, address, rd, index, size, 2, step, context.Arm64Assembler.St2SnglAsNoPostIndex); + }); + } + + public static void Vst2M(CodeGenContext context, uint rd, uint rn, uint rm, uint type, uint align, uint size) + { + uint step = (type & 1) + 1; + + EmitMemory1234InstructionCore(context, rn, rm, 16, (address) => + { + EmitMemoryStore1234MultipleInstruction(context, address, rd, size, 2, step, context.Arm64Assembler.St2MultAsNoPostIndex); + }); + } + + public static void Vst2M(CodeGenContext context, uint rd, uint rn, uint rm, uint align, uint size) + { + EmitMemory1234InstructionCore(context, rn, rm, 32, (address) => + { + EmitMemoryStore1234Multiple2x2Instruction(context, address, rd, size, context.Arm64Assembler.St2MultAsNoPostIndex); + }); + } + + public static void Vst31(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + uint step = size > 0 && (indexAlign & (1u << (int)size)) != 0 ? 2u : 1u; + + EmitMemory1234InstructionCore(context, rn, rm, 3 * (1 << (int)size), (address) => + { + EmitMemoryStore1234SingleInstruction(context, address, rd, index, size, 3, step, context.Arm64Assembler.St3SnglAsNoPostIndex); + }); + } + + public static void Vst3M(CodeGenContext context, uint rd, uint rn, uint rm, uint type, uint align, uint size) + { + uint step = (type & 1) + 1; + + EmitMemory1234InstructionCore(context, rn, rm, 24, (address) => + { + EmitMemoryStore1234MultipleInstruction(context, address, rd, size, 3, step, context.Arm64Assembler.St3MultAsNoPostIndex); + }); + } + + public static void Vst41(CodeGenContext context, uint rd, uint rn, uint rm, uint indexAlign, uint size) + { + uint index = indexAlign >> ((int)size + 1); + uint step = size > 0 && (indexAlign & (1u << (int)size)) != 0 ? 2u : 1u; + + EmitMemory1234InstructionCore(context, rn, rm, 4 * (1 << (int)size), (address) => + { + EmitMemoryStore1234SingleInstruction(context, address, rd, index, size, 4, step, context.Arm64Assembler.St4SnglAsNoPostIndex); + }); + } + + public static void Vst4M(CodeGenContext context, uint rd, uint rn, uint rm, uint type, uint align, uint size) + { + uint step = (type & 1) + 1; + + EmitMemory1234InstructionCore(context, rn, rm, 32, (address) => + { + EmitMemoryStore1234MultipleInstruction(context, address, rd, size, 4, step, context.Arm64Assembler.St4MultAsNoPostIndex); + }); + } + + public static void Vstm(CodeGenContext context, uint rd, uint rn, uint registerCount, bool u, bool w, bool singleRegs) + { + EmitMemoryMultipleInstruction(context, rd, rn, registerCount, u, w, singleRegs, isStore: true); + } + + public static void Vstr(CodeGenContext context, uint rd, uint rn, uint imm8, bool u, uint size) + { + EmitMemoryInstruction(context, rd, rn, imm8, u, size, isStore: true); + } + + private static void EmitMemoryMultipleInstruction( + CodeGenContext context, + uint rd, + uint rn, + uint registerCount, + bool add, + bool wBack, + bool singleRegs, + bool isStore) + { + Operand baseAddress = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand offset = InstEmitCommon.Const((int)registerCount * (singleRegs ? 4 : 8)); + + if (!add) + { + if (wBack) + { + InstEmitMemory.WriteAddShiftOffset(context.Arm64Assembler, baseAddress, baseAddress, offset, false, ArmShiftType.Lsl, 0); + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, baseAddress); + } + else + { + InstEmitMemory.WriteAddShiftOffset(context.Arm64Assembler, tempRegister.Operand, baseAddress, offset, false, ArmShiftType.Lsl, 0); + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, tempRegister.Operand); + } + } + else + { + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, tempRegister.Operand, baseAddress); + } + + EmitMemoryMultipleInstructionCore(context, tempRegister.Operand, rd, registerCount, singleRegs, isStore); + + if (add && wBack) + { + context.Arm64Assembler.Add(baseAddress, baseAddress, offset); + } + } + + private static void EmitMemoryMultipleInstructionCore(CodeGenContext context, Operand baseAddress, uint rd, uint registerCount, bool singleRegs, bool isStore) + { + int offs = 0; + uint r = rd; + uint upperBound = Math.Min(rd + registerCount, 32u); + uint regMask = singleRegs ? 3u : 1u; + + // Read/write misaligned elements first. + + for (; (r & regMask) != 0 && r < upperBound; r++) + { + EmitMemoryInstruction(context, baseAddress, r, offs, singleRegs, isStore); + + offs += singleRegs ? 4 : 8; + } + + // Read/write aligned, full vectors. + + while (upperBound - r >= (singleRegs ? 4 : 2)) + { + int qIndex = (int)(r >> (singleRegs ? 2 : 1)); + + Operand rtOperand = context.RegisterAllocator.RemapSimdRegister(qIndex); + + if (upperBound - r >= (singleRegs ? 8 : 4) && (offs & 0xf) == 0) + { + Operand rt2Operand = context.RegisterAllocator.RemapSimdRegister(qIndex + 1); + + if (isStore) + { + context.Arm64Assembler.StpRiUn(rtOperand, rt2Operand, baseAddress, offs); + } + else + { + context.Arm64Assembler.LdpRiUn(rtOperand, rt2Operand, baseAddress, offs); + } + + r += singleRegs ? 8u : 4u; + offs += 32; + } + else + { + if ((offs & 0xf) == 0) + { + if (isStore) + { + context.Arm64Assembler.StrRiUn(rtOperand, baseAddress, offs); + } + else + { + context.Arm64Assembler.LdrRiUn(rtOperand, baseAddress, offs); + } + } + else + { + if (isStore) + { + context.Arm64Assembler.Stur(rtOperand, baseAddress, offs); + } + else + { + context.Arm64Assembler.Ldur(rtOperand, baseAddress, offs); + } + } + + r += singleRegs ? 4u : 2u; + offs += 16; + } + } + + // Read/write last misaligned elements. + + for (; r < upperBound; r++) + { + EmitMemoryInstruction(context, baseAddress, r, offs, singleRegs, isStore); + + offs += singleRegs ? 4 : 8; + } + } + + private static void EmitMemoryInstruction(CodeGenContext context, Operand baseAddress, uint r, int offs, bool singleRegs, bool isStore) + { + if (isStore) + { + using ScopedRegister tempRegister = InstEmitNeonCommon.MoveScalarToSide(context, r, singleRegs); + + context.Arm64Assembler.StrRiUn(tempRegister.Operand, baseAddress, offs); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(singleRegs); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, baseAddress, offs); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, r, singleRegs); + } + } + + private static void EmitMemoryInstruction(CodeGenContext context, uint rd, uint rn, uint imm8, bool add, uint size, bool isStore) + { + bool singleRegs = size != 3; + int offs = (int)imm8; + + if (size == 1) + { + offs <<= 1; + } + else + { + offs <<= 2; + } + + using ScopedRegister address = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + if (rn == RegisterUtils.PcRegister) + { + if (!add) + { + offs = -offs; + } + + context.Arm64Assembler.Mov(address.Operand, (context.Pc & ~3u) + (uint)offs); + + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, address.Operand, address.Operand); + + offs = 0; + } + else + { + Operand rnOperand = context.RegisterAllocator.RemapGprRegister((int)rn); + + if (InstEmitMemory.CanFoldOffset(context.MemoryManagerType, add ? offs : -offs, (int)size, true, out _)) + { + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, address.Operand, rnOperand); + + if (!add) + { + offs = -offs; + } + } + else + { + InstEmitMemory.WriteAddShiftOffset(context.Arm64Assembler, address.Operand, rnOperand, InstEmitCommon.Const(offs), add, ArmShiftType.Lsl, 0); + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, address.Operand, address.Operand); + + offs = 0; + } + } + + if ((size == 3 && (offs & 7) != 0) || offs < 0) + { + if (isStore) + { + using ScopedRegister tempRegister = InstEmitNeonCommon.MoveScalarToSide(context, rd, singleRegs); + + context.Arm64Assembler.Stur(tempRegister.Operand, address.Operand, offs, size); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(singleRegs); + + context.Arm64Assembler.Ldur(tempRegister.Operand, address.Operand, offs, size); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + } + else + { + if (isStore) + { + using ScopedRegister tempRegister = InstEmitNeonCommon.MoveScalarToSide(context, rd, singleRegs); + + context.Arm64Assembler.StrRiUn(tempRegister.Operand, address.Operand, offs, size); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(singleRegs); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, address.Operand, offs, size); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + } + } + + private static void EmitMemory1234InstructionCore(CodeGenContext context, uint rn, uint rm, int bytes, Action<Operand> callback) + { + bool wBack = rm != RegisterUtils.PcRegister; + bool registerIndex = rm != RegisterUtils.PcRegister && rm != RegisterUtils.SpRegister; + + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister address = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitMemory.WriteAddressTranslation(context.MemoryManagerType, context.RegisterAllocator, context.Arm64Assembler, address.Operand, rnOperand); + + callback(address.Operand); + + if (wBack) + { + if (registerIndex) + { + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + context.Arm64Assembler.Add(rnOperand, rnOperand, rmOperand); + } + else + { + context.Arm64Assembler.Add(rnOperand, rnOperand, InstEmitCommon.Const(bytes)); + } + } + } + + private static void EmitMemoryLoad1234SingleInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint index, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + MoveDoublewordsToQuadwordsLower(context, rd, registerCount, step, tempRegisters); + + action(tempRegisters[0].Operand, baseAddress, index, size); + + MoveQuadwordsLowerToDoublewords(context, rd, registerCount, step, tempRegisters); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryLoad1SingleReplicateInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint> action) + { + if ((rd & 1) == 0 && registerCount == 2) + { + action(context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)), baseAddress, size, 1); + } + else + { + uint vecsCount = (registerCount + 1) >> 1; + + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)vecsCount); + + action(tempRegisters[0].Operand, baseAddress, size, registerCount > 1 ? 1u : 0u); + + MoveQuadwordsToDoublewords(context, rd, registerCount, step, tempRegisters); + + FreeSequentialRegisters(tempRegisters); + } + } + + private static void EmitMemoryLoad234SingleReplicateInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + action(tempRegisters[0].Operand, baseAddress, size, 0u); + + MoveQuadwordsLowerToDoublewords(context, rd, registerCount, step, tempRegisters); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryLoad1234MultipleInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + action(tempRegisters[0].Operand, baseAddress, size, 0); + + MoveQuadwordsLowerToDoublewords(context, rd, registerCount, step, tempRegisters); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryLoad1234MultipleInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + action(tempRegisters[0].Operand, baseAddress, registerCount, size, 0); + + MoveQuadwordsLowerToDoublewords(context, rd, registerCount, step, tempRegisters); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryLoad1234Multiple2x2Instruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + Action<Operand, Operand, uint, uint> action) + { + if ((rd & 1) == 0) + { + action(context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1), 2), baseAddress, size, 1); + } + else + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, 2); + + action(tempRegisters[0].Operand, baseAddress, size, 1); + + MoveQuadwordsToDoublewords2x2(context, rd, tempRegisters); + + FreeSequentialRegisters(tempRegisters); + } + } + + private static void EmitMemoryStore1234SingleInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint index, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + MoveDoublewordsToQuadwordsLower(context, rd, registerCount, step, tempRegisters); + + action(tempRegisters[0].Operand, baseAddress, index, size); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryStore1234MultipleInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + MoveDoublewordsToQuadwordsLower(context, rd, registerCount, step, tempRegisters); + + action(tempRegisters[0].Operand, baseAddress, size, 0); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryStore1234MultipleInstruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + uint registerCount, + uint step, + Action<Operand, Operand, uint, uint, uint> action) + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, (int)registerCount); + + MoveDoublewordsToQuadwordsLower(context, rd, registerCount, step, tempRegisters); + + action(tempRegisters[0].Operand, baseAddress, registerCount, size, 0); + + FreeSequentialRegisters(tempRegisters); + } + + private static void EmitMemoryStore1234Multiple2x2Instruction( + CodeGenContext context, + Operand baseAddress, + uint rd, + uint size, + Action<Operand, Operand, uint, uint> action) + { + if ((rd & 1) == 0) + { + action(context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1), 2), baseAddress, size, 1); + } + else + { + ScopedRegister[] tempRegisters = AllocateSequentialRegisters(context, 2); + + MoveDoublewordsToQuadwords2x2(context, rd, tempRegisters); + + action(tempRegisters[0].Operand, baseAddress, size, 1); + + FreeSequentialRegisters(tempRegisters); + } + } + + private static ScopedRegister[] AllocateSequentialRegisters(CodeGenContext context, int count) + { + ScopedRegister[] registers = new ScopedRegister[count]; + + for (int index = 0; index < count; index++) + { + registers[index] = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + } + + AssertSequentialRegisters(registers); + + return registers; + } + + private static void FreeSequentialRegisters(ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 0; index < registers.Length; index++) + { + registers[index].Dispose(); + } + } + + [Conditional("DEBUG")] + private static void AssertSequentialRegisters(ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 1; index < registers.Length; index++) + { + Debug.Assert(registers[index].Operand.GetRegister().Index == registers[0].Operand.GetRegister().Index + index); + } + } + + private static void MoveQuadwordsLowerToDoublewords(CodeGenContext context, uint rd, uint registerCount, uint step, ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 0; index < registerCount; index++) + { + uint r = rd + (uint)index * step; + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(r >> 1)); + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(r & 1u, false); + context.Arm64Assembler.InsElt(rdOperand, registers[index].Operand, 0, imm5); + } + } + + private static void MoveDoublewordsToQuadwordsLower(CodeGenContext context, uint rd, uint registerCount, uint step, ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 0; index < registerCount; index++) + { + uint r = rd + (uint)index * step; + + InstEmitNeonCommon.MoveScalarToSide(context, registers[index].Operand, r, false); + } + } + + private static void MoveDoublewordsToQuadwords2x2(CodeGenContext context, uint rd, ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 0; index < 2; index++) + { + uint r = rd + (uint)index * 2; + uint r2 = r + 1; + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(r >> 1)); + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(0, false); + context.Arm64Assembler.InsElt(registers[index].Operand, rdOperand, (r & 1u) << 3, imm5); + + rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(r2 >> 1)); + imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(1, false); + context.Arm64Assembler.InsElt(registers[index].Operand, rdOperand, (r2 & 1u) << 3, imm5); + } + } + + private static void MoveQuadwordsToDoublewords(CodeGenContext context, uint rd, uint registerCount, uint step, ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 0; index < registerCount; index++) + { + uint r = rd + (uint)index * step; + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(r >> 1)); + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(r & 1u, false); + context.Arm64Assembler.InsElt(rdOperand, registers[index >> 1].Operand, ((uint)index & 1u) << 3, imm5); + } + } + + private static void MoveQuadwordsToDoublewords2x2(CodeGenContext context, uint rd, ReadOnlySpan<ScopedRegister> registers) + { + for (int index = 0; index < 2; index++) + { + uint r = rd + (uint)index * 2; + uint r2 = r + 1; + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(r >> 1)); + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(r & 1u, false); + context.Arm64Assembler.InsElt(rdOperand, registers[index].Operand, 0, imm5); + + rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(r2 >> 1)); + imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(r2 & 1u, false); + context.Arm64Assembler.InsElt(rdOperand, registers[index].Operand, 1u << 3, imm5); + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonMove.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonMove.cs new file mode 100644 index 00000000..08a0673a --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonMove.cs @@ -0,0 +1,665 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using System; +using System.Diagnostics; +using System.Numerics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonMove + { + public static void VdupR(CodeGenContext context, uint rd, uint rt, uint b, uint e, uint q) + { + uint size = 2 - (e | (b << 1)); + + Debug.Assert(size < 3); + + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(0, size); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.DupGen(tempRegister.Operand, rtOperand, imm5, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Debug.Assert((rd & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + context.Arm64Assembler.DupGen(rdOperand, rtOperand, imm5, q); + } + } + + public static void VdupS(CodeGenContext context, uint rd, uint rm, uint imm4, uint q) + { + uint size = (uint)BitOperations.TrailingZeroCount(imm4); + + Debug.Assert(size < 3); + + uint index = imm4 >> (int)(size + 1); + + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(index | ((rm & 1) << (int)(3 - size)), size); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.DupEltVectorFromElement(tempRegister.Operand, rmOperand, imm5, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Debug.Assert((rd & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + context.Arm64Assembler.DupEltVectorFromElement(rdOperand, rmOperand, imm5, q); + } + } + + public static void Vext(CodeGenContext context, uint rd, uint rn, uint rm, uint imm4, uint q) + { + if (q == 0) + { + using ScopedRegister rnReg = InstEmitNeonCommon.MoveScalarToSide(context, rn, false); + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister = InstEmitNeonCommon.PickSimdRegister(context.RegisterAllocator, rnReg, rmReg); + + context.Arm64Assembler.Ext(tempRegister.Operand, rnReg.Operand, imm4, rmReg.Operand, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Debug.Assert(((rd | rn | rm) & 1) == 0); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + context.Arm64Assembler.Ext(rdOperand, rnOperand, imm4, rmOperand, q); + } + } + + public static void Vmovl(CodeGenContext context, uint rd, uint rm, bool u, uint imm3h) + { + uint size = (uint)BitOperations.TrailingZeroCount(imm3h); + Debug.Assert(size < 3); + + InstEmitNeonCommon.EmitVectorBinaryLongShift( + context, + rd, + rm, + 0, + size, + isShl: true, + u ? context.Arm64Assembler.Ushll : context.Arm64Assembler.Sshll); + } + + public static void Vmovn(CodeGenContext context, uint rd, uint rm, uint size) + { + Debug.Assert(size < 3); + + InstEmitNeonCommon.EmitVectorUnaryNarrow(context, rd, rm, size, context.Arm64Assembler.Xtn); + } + + public static void Vmovx(CodeGenContext context, uint rd, uint rm) + { + InstEmitNeonCommon.EmitScalarBinaryShift(context, rd, rm, 16, 2, isShl: false, context.Arm64Assembler.UshrS); + } + + public static void VmovD(CodeGenContext context, uint rt, uint rt2, uint rm, bool op) + { + Operand rmReg = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + uint top = rm & 1; + uint ftype = top + 1; + + if (op) + { + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + Operand rt2Operand = InstEmitCommon.GetOutputGpr(context, rt2); + + Operand rtOperand64 = new(OperandKind.Register, OperandType.I64, rtOperand.Value); + Operand rt2Operand64 = new(OperandKind.Register, OperandType.I64, rt2Operand.Value); + + context.Arm64Assembler.FmovFloatGen(rtOperand64, rmReg, ftype, 1, 0, top); + + context.Arm64Assembler.Lsr(rt2Operand64, rtOperand64, InstEmitCommon.Const(32)); + context.Arm64Assembler.Mov(rtOperand, rtOperand); // Zero-extend. + } + else + { + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + Operand rt2Operand = InstEmitCommon.GetInputGpr(context, rt2); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempRegister64 = new(OperandKind.Register, OperandType.I64, tempRegister.Operand.Value); + + context.Arm64Assembler.Lsl(tempRegister64, rt2Operand, InstEmitCommon.Const(32)); + context.Arm64Assembler.Orr(tempRegister64, tempRegister64, rtOperand); + + if (top == 0) + { + // Doing FMOV on Rm directly would clear the high bits if we are moving to the bottom. + + using ScopedRegister tempRegister2 = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.FmovFloatGen(tempRegister2.Operand, tempRegister64, ftype, 1, 1, top); + + InstEmitNeonCommon.InsertResult(context, tempRegister2.Operand, rm, false); + } + else + { + context.Arm64Assembler.FmovFloatGen(rmReg, tempRegister64, ftype, 1, 1, top); + } + } + } + + public static void VmovH(CodeGenContext context, uint rt, uint rn, bool op) + { + if (op) + { + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + + using ScopedRegister tempRegister = InstEmitNeonCommon.MoveScalarToSide(context, rn, true); + + context.Arm64Assembler.FmovFloatGen(rtOperand, tempRegister.Operand, 3, 0, 0, 0); + } + else + { + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.FmovFloatGen(tempRegister.Operand, rtOperand, 3, 0, 1, 0); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rn, true); + } + } + + public static void VmovI(CodeGenContext context, uint rd, uint op, uint cmode, uint imm8, uint q) + { + (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) = Split(imm8); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.Movi(tempRegister.Operand, h, g, f, e, d, cmode, c, b, a, op, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + context.Arm64Assembler.Movi(rdOperand, h, g, f, e, d, cmode, c, b, a, op, q); + } + } + + public static void VmovFI(CodeGenContext context, uint rd, uint imm8, uint size) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.FmovFloatImm(tempRegister.Operand, imm8, size ^ 2u); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, size != 3); + } + + public static void VmovR(CodeGenContext context, uint rd, uint rm, uint size) + { + bool singleRegister = size == 2; + + int shift = singleRegister ? 2 : 1; + uint mask = singleRegister ? 3u : 1u; + uint dstElt = rd & mask; + uint srcElt = rm & mask; + + uint imm4 = srcElt << (singleRegister ? 2 : 3); + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(dstElt, singleRegister); + + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> shift)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> shift)); + + context.Arm64Assembler.InsElt(rdOperand, rmOperand, imm4, imm5); + } + + public static void VmovRs(CodeGenContext context, uint rd, uint rt, uint opc1, uint opc2) + { + uint index; + uint size; + + if ((opc1 & 2u) != 0) + { + index = opc2 | ((opc1 & 1u) << 2); + size = 0; + } + else if ((opc2 & 1u) != 0) + { + index = (opc2 >> 1) | ((opc1 & 1u) << 1); + size = 1; + } + else + { + Debug.Assert(opc1 == 0 || opc1 == 1); + Debug.Assert(opc2 == 0); + + index = opc1 & 1u; + size = 2; + } + + index |= (rd & 1u) << (int)(3 - size); + + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + + Operand rdReg = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + context.Arm64Assembler.InsGen(rdReg, rtOperand, InstEmitNeonCommon.GetImm5ForElementIndex(index, size)); + } + + public static void VmovS(CodeGenContext context, uint rt, uint rn, bool op) + { + if (op) + { + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + + using ScopedRegister tempRegister = InstEmitNeonCommon.MoveScalarToSide(context, rn, true); + + context.Arm64Assembler.FmovFloatGen(rtOperand, tempRegister.Operand, 0, 0, 0, 0); + } + else + { + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.FmovFloatGen(tempRegister.Operand, rtOperand, 0, 0, 1, 0); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rn, true); + } + } + + public static void VmovSr(CodeGenContext context, uint rt, uint rn, bool u, uint opc1, uint opc2) + { + uint index; + uint size; + + if ((opc1 & 2u) != 0) + { + index = opc2 | ((opc1 & 1u) << 2); + size = 0; + } + else if ((opc2 & 1u) != 0) + { + index = (opc2 >> 1) | ((opc1 & 1u) << 1); + size = 1; + } + else + { + Debug.Assert(opc1 == 0 || opc1 == 1); + Debug.Assert(opc2 == 0); + Debug.Assert(!u); + + index = opc1 & 1u; + size = 2; + } + + index |= (rn & 1u) << (int)(3 - size); + + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + + Operand rnReg = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1)); + + if (u || size > 1) + { + context.Arm64Assembler.Umov(rtOperand, rnReg, (int)index, (int)size); + } + else + { + context.Arm64Assembler.Smov(rtOperand, rnReg, (int)index, (int)size); + } + } + + public static void VmovSs(CodeGenContext context, uint rt, uint rt2, uint rm, bool op) + { + if ((rm & 1) == 0) + { + // If we are moving an aligned pair of single-precision registers, + // we can just move a single double-precision register. + + VmovD(context, rt, rt2, rm >> 1, op); + + return; + } + + if (op) + { + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + Operand rt2Operand = InstEmitCommon.GetOutputGpr(context, rt2); + + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, true); + using ScopedRegister rmReg2 = InstEmitNeonCommon.MoveScalarToSide(context, rm + 1, true); + + context.Arm64Assembler.FmovFloatGen(rtOperand, rmReg.Operand, 0, 0, 0, 0); + context.Arm64Assembler.FmovFloatGen(rt2Operand, rmReg2.Operand, 0, 0, 0, 0); + } + else + { + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + Operand rt2Operand = InstEmitCommon.GetInputGpr(context, rt2); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.FmovFloatGen(tempRegister.Operand, rtOperand, 0, 0, 1, 0); + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rm, true); + + context.Arm64Assembler.FmovFloatGen(tempRegister.Operand, rt2Operand, 0, 0, 1, 0); + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rm + 1, true); + } + } + + public static void VmvnI(CodeGenContext context, uint rd, uint cmode, uint imm8, uint q) + { + (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) = Split(imm8); + + if (q == 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + context.Arm64Assembler.Mvni(tempRegister.Operand, h, g, f, e, d, cmode, c, b, a, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + + context.Arm64Assembler.Mvni(rdOperand, h, g, f, e, d, cmode, c, b, a, q); + } + } + + public static void VmvnR(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, q, context.Arm64Assembler.Not); + } + + public static void Vswp(CodeGenContext context, uint rd, uint rm, uint q) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (q == 0) + { + InstEmitNeonCommon.MoveScalarToSide(context, tempRegister.Operand, rd, false); + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false); + + InstEmitNeonCommon.InsertResult(context, rmReg.Operand, rd, false); + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rm, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + context.Arm64Assembler.Orr(tempRegister.Operand, rdOperand, rdOperand); // Temp = Rd + context.Arm64Assembler.Orr(rdOperand, rmOperand, rmOperand); // Rd = Rm + context.Arm64Assembler.Orr(rmOperand, tempRegister.Operand, tempRegister.Operand); // Rm = Temp + } + } + + public static void Vtbl(CodeGenContext context, uint rd, uint rn, uint rm, bool op, uint len) + { + // On AArch64, TBL/TBX works with 128-bit vectors, while on AArch32 it works with 64-bit vectors. + // We must combine the 64-bit vectors into a larger 128-bit one in some cases. + + // TODO: Peephole optimization to combine adjacent TBL instructions? + + Debug.Assert(len <= 3); + + bool isTbl = !op; + + len = Math.Min(len, 31 - rn); + + bool rangeMismatch = !isTbl && (len & 1) == 0; + + using ScopedRegister indicesReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false, rangeMismatch); + + if (rangeMismatch) + { + // Force any index >= 8 * regs to be the maximum value, since on AArch64 we are working with a full vector, + // and the out of range value is 16 * regs, not 8 * regs. + + Debug.Assert(indicesReg.IsAllocated); + + using ScopedRegister tempRegister2 = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (len == 0) + { + (uint immb, uint immh) = InstEmitNeonCommon.GetImmbImmhForShift(3, 0, isShl: false); + + context.Arm64Assembler.UshrV(tempRegister2.Operand, indicesReg.Operand, immb, immh, 0); + context.Arm64Assembler.CmeqZeroV(tempRegister2.Operand, tempRegister2.Operand, 0, 0); + context.Arm64Assembler.Orn(indicesReg.Operand, indicesReg.Operand, tempRegister2.Operand, 0); + } + else + { + (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) = Split(8u * (len + 1)); + + context.Arm64Assembler.Movi(tempRegister2.Operand, h, g, f, e, d, 0xe, c, b, a, 0, 0); + context.Arm64Assembler.CmgeRegV(tempRegister2.Operand, indicesReg.Operand, tempRegister2.Operand, 0, 0); + context.Arm64Assembler.OrrReg(indicesReg.Operand, indicesReg.Operand, tempRegister2.Operand, 0); + } + } + + ScopedRegister tableReg1 = default; + ScopedRegister tableReg2 = default; + + switch (len) + { + case 0: + tableReg1 = MoveHalfToSideZeroUpper(context, rn); + break; + case 1: + tableReg1 = MoveDoublewords(context, rn, rn + 1); + break; + case 2: + tableReg1 = MoveDoublewords(context, rn, rn + 1, isOdd: true); + tableReg2 = MoveHalfToSideZeroUpper(context, rn + 2); + break; + case 3: + tableReg1 = MoveDoublewords(context, rn, rn + 1); + tableReg2 = MoveDoublewords(context, rn + 2, rn + 3); + break; + } + + // TBL works with consecutive registers, it is assumed that two consecutive calls to the register allocator + // will return consecutive registers. + + Debug.Assert(len < 2 || tableReg1.Operand.GetRegister().Index + 1 == tableReg2.Operand.GetRegister().Index); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (isTbl) + { + context.Arm64Assembler.Tbl(tempRegister.Operand, tableReg1.Operand, len >> 1, indicesReg.Operand, 0); + } + else + { + InstEmitNeonCommon.MoveScalarToSide(context, tempRegister.Operand, rd, false); + + context.Arm64Assembler.Tbx(tempRegister.Operand, tableReg1.Operand, len >> 1, indicesReg.Operand, 0); + } + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + + tableReg1.Dispose(); + + if (len > 1) + { + tableReg2.Dispose(); + } + } + + public static void Vtrn(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + EmitVectorBinaryInterleavedTrn(context, rd, rm, size, q, context.Arm64Assembler.Trn1, context.Arm64Assembler.Trn2); + } + + public static void Vuzp(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + EmitVectorBinaryInterleaved(context, rd, rm, size, q, context.Arm64Assembler.Uzp1, context.Arm64Assembler.Uzp2); + } + + public static void Vzip(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + EmitVectorBinaryInterleaved(context, rd, rm, size, q, context.Arm64Assembler.Zip1, context.Arm64Assembler.Zip2); + } + + public static (uint, uint, uint, uint, uint, uint, uint, uint) Split(uint imm8) + { + uint a = (imm8 >> 7) & 1; + uint b = (imm8 >> 6) & 1; + uint c = (imm8 >> 5) & 1; + uint d = (imm8 >> 4) & 1; + uint e = (imm8 >> 3) & 1; + uint f = (imm8 >> 2) & 1; + uint g = (imm8 >> 1) & 1; + uint h = imm8 & 1; + + return (a, b, c, d, e, f, g, h); + } + + private static ScopedRegister MoveHalfToSideZeroUpper(CodeGenContext context, uint srcReg) + { + uint elt = srcReg & 1u; + + Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> 1)); + ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(false); + + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(elt, false); + + context.Arm64Assembler.DupEltScalarFromElement(tempRegister.Operand, source, imm5); + + return tempRegister; + } + + private static ScopedRegister MoveDoublewords(CodeGenContext context, uint lowerReg, uint upperReg, bool isOdd = false) + { + if ((lowerReg & 1) == 0 && upperReg == lowerReg + 1 && !isOdd) + { + return new ScopedRegister(context.RegisterAllocator, context.RegisterAllocator.RemapSimdRegister((int)(lowerReg >> 1)), false); + } + + Operand lowerSrc = context.RegisterAllocator.RemapSimdRegister((int)(lowerReg >> 1)); + Operand upperSrc = context.RegisterAllocator.RemapSimdRegister((int)(upperReg >> 1)); + ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(false); + + uint imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(lowerReg & 1u, false); + + context.Arm64Assembler.DupEltScalarFromElement(tempRegister.Operand, lowerSrc, imm5); + + imm5 = InstEmitNeonCommon.GetImm5ForElementIndex(1, false); + + context.Arm64Assembler.InsElt(tempRegister.Operand, upperSrc, (upperReg & 1u) << 3, imm5); + + return tempRegister; + } + + private static void EmitVectorBinaryInterleavedTrn( + CodeGenContext context, + uint rd, + uint rm, + uint size, + uint q, + Action<Operand, Operand, Operand, uint, uint> action1, + Action<Operand, Operand, Operand, uint, uint> action2) + { + if (rd == rm) + { + // The behaviour when the registers are the same is "unpredictable" according to the manual. + + if (q == 0) + { + using ScopedRegister rdReg = InstEmitNeonCommon.MoveScalarToSide(context, rd, false); + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister1 = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + using ScopedRegister tempRegister2 = InstEmitNeonCommon.PickSimdRegister(context.RegisterAllocator, rdReg, rmReg); + + action1(tempRegister1.Operand, rdReg.Operand, rmReg.Operand, size, q); + action2(tempRegister2.Operand, rdReg.Operand, tempRegister1.Operand, size, q); + + InstEmitNeonCommon.InsertResult(context, tempRegister2.Operand, rd, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + action1(tempRegister.Operand, rdOperand, rmOperand, size, q); + action2(rmOperand, rdOperand, tempRegister.Operand, size, q); + } + } + else + { + EmitVectorBinaryInterleaved(context, rd, rm, size, q, action1, action2); + } + } + + private static void EmitVectorBinaryInterleaved( + CodeGenContext context, + uint rd, + uint rm, + uint size, + uint q, + Action<Operand, Operand, Operand, uint, uint> action1, + Action<Operand, Operand, Operand, uint, uint> action2) + { + if (q == 0) + { + using ScopedRegister rdReg = InstEmitNeonCommon.MoveScalarToSide(context, rd, false); + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false); + + using ScopedRegister tempRegister1 = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + using ScopedRegister tempRegister2 = InstEmitNeonCommon.PickSimdRegister(context.RegisterAllocator, rdReg, rmReg); + + action1(tempRegister1.Operand, rdReg.Operand, rmReg.Operand, size, q); + action2(tempRegister2.Operand, rdReg.Operand, rmReg.Operand, size, q); + + if (rd != rm) + { + InstEmitNeonCommon.InsertResult(context, tempRegister1.Operand, rd, false); + } + + InstEmitNeonCommon.InsertResult(context, tempRegister2.Operand, rm, false); + } + else + { + Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1)); + Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1)); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + action1(tempRegister.Operand, rdOperand, rmOperand, size, q); + action2(rmOperand, rdOperand, rmOperand, size, q); + + if (rd != rm) + { + context.Arm64Assembler.OrrReg(rdOperand, tempRegister.Operand, tempRegister.Operand, 1); + } + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonRound.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonRound.cs new file mode 100644 index 00000000..3c6ca65d --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonRound.cs @@ -0,0 +1,105 @@ +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonRound + { + public static void Vraddhn(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryNarrow(context, rd, rn, rm, size, context.Arm64Assembler.Raddhn); + } + + public static void Vrhadd(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, u ? context.Arm64Assembler.Urhadd : context.Arm64Assembler.Srhadd, null); + } + + public static void Vrshl(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary( + context, + rd, + rm, + rn, + size, + q, + u ? context.Arm64Assembler.UrshlV : context.Arm64Assembler.SrshlV, + u ? context.Arm64Assembler.UrshlS : context.Arm64Assembler.SrshlS); + } + + public static void Vrshr(CodeGenContext context, uint rd, uint rm, bool u, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = InstEmitNeonShift.GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryShift( + context, + rd, + rm, + shift, + size, + q, + isShl: false, + u ? context.Arm64Assembler.UrshrV : context.Arm64Assembler.SrshrV, + u ? context.Arm64Assembler.UrshrS : context.Arm64Assembler.SrshrS); + } + + public static void Vrshrn(CodeGenContext context, uint rd, uint rm, uint imm6) + { + uint size = InstEmitNeonCommon.GetSizeFromImm6(imm6); + uint shift = InstEmitNeonShift.GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.Rshrn); + } + + public static void Vrsra(CodeGenContext context, uint rd, uint rm, bool u, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = InstEmitNeonShift.GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorTernaryRdShift( + context, + rd, + rm, + shift, + size, + q, + isShl: false, + u ? context.Arm64Assembler.UrsraV : context.Arm64Assembler.SrsraV, + u ? context.Arm64Assembler.UrsraS : context.Arm64Assembler.SrsraS); + } + + public static void Vrsubhn(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryNarrow(context, rd, rn, rm, size, context.Arm64Assembler.Rsubhn); + } + + public static void Vrinta(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrintaSingleAndDouble, context.Arm64Assembler.FrintaHalf); + } + + public static void Vrintm(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrintmSingleAndDouble, context.Arm64Assembler.FrintmHalf); + } + + public static void Vrintn(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrintnSingleAndDouble, context.Arm64Assembler.FrintnHalf); + } + + public static void Vrintp(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrintpSingleAndDouble, context.Arm64Assembler.FrintpHalf); + } + + public static void Vrintx(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrintxSingleAndDouble, context.Arm64Assembler.FrintxHalf); + } + + public static void Vrintz(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnaryAnyF(context, rd, rm, size, q, context.Arm64Assembler.FrintzSingleAndDouble, context.Arm64Assembler.FrintzHalf); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonSaturate.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonSaturate.cs new file mode 100644 index 00000000..aeab726a --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonSaturate.cs @@ -0,0 +1,205 @@ +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonSaturate + { + public static void Vqabs(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.SqabsV); + } + + public static void Vqadd(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary( + context, + rd, + rn, + rm, + size, + q, + u ? context.Arm64Assembler.UqaddV : context.Arm64Assembler.SqaddV, + u ? context.Arm64Assembler.UqaddS : context.Arm64Assembler.SqaddS); + } + + public static void Vqdmlal(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, context.Arm64Assembler.SqdmlalVecV); + } + + public static void VqdmlalS(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLongByScalar(context, rd, rn, rm, size, context.Arm64Assembler.SqdmlalElt2regElement); + } + + public static void Vqdmlsl(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, context.Arm64Assembler.SqdmlslVecV); + } + + public static void VqdmlslS(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLongByScalar(context, rd, rn, rm, size, context.Arm64Assembler.SqdmlslElt2regElement); + } + + public static void Vqdmulh(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.SqdmulhVecV, context.Arm64Assembler.SqdmulhVecS); + } + + public static void VqdmulhS(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.SqdmulhElt2regElement); + } + + public static void Vqdmull(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLong(context, rd, rn, rm, size, context.Arm64Assembler.SqdmullVecV); + } + + public static void VqdmullS(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitVectorBinaryLongByScalar(context, rd, rn, rm, size, context.Arm64Assembler.SqdmullElt2regElement); + } + + public static void Vqmovn(CodeGenContext context, uint rd, uint rm, uint op, uint size) + { + if (op == 3) + { + InstEmitNeonCommon.EmitVectorUnaryNarrow(context, rd, rm, size, context.Arm64Assembler.UqxtnV); + } + else + { + InstEmitNeonCommon.EmitVectorUnaryNarrow(context, rd, rm, size, op == 1 ? context.Arm64Assembler.SqxtunV : context.Arm64Assembler.SqxtnV); + } + } + + public static void Vqneg(CodeGenContext context, uint rd, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorUnary(context, rd, rm, size, q, context.Arm64Assembler.SqnegV); + } + + public static void Vqrdmlah(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, size, q, context.Arm64Assembler.SqrdmlahVecV); + } + + public static void VqrdmlahS(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRdByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.SqrdmlahElt2regElement); + } + + public static void Vqrdmlsh(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRd(context, rd, rn, rm, size, q, context.Arm64Assembler.SqrdmlshVecV); + } + + public static void VqrdmlshS(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorTernaryRdByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.SqrdmlshElt2regElement); + } + + public static void Vqrdmulh(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rn, rm, size, q, context.Arm64Assembler.SqrdmulhVecV, context.Arm64Assembler.SqrdmulhVecS); + } + + public static void VqrdmulhS(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinaryByScalar(context, rd, rn, rm, size, q, context.Arm64Assembler.SqrdmulhElt2regElement); + } + + public static void Vqrshl(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rm, rn, size, q, context.Arm64Assembler.SqrshlV, context.Arm64Assembler.SqrshlS); + } + + public static void Vqrshrn(CodeGenContext context, uint rd, uint rm, bool u, uint op, uint imm6) + { + uint size = InstEmitNeonCommon.GetSizeFromImm6(imm6); + uint shift = InstEmitNeonShift.GetShiftRight(imm6, size); + + if (u && op == 0) + { + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.SqrshrunV); + } + else if (!u && op == 1) + { + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.SqrshrnV); + } + else + { + Debug.Assert(u && op == 1); // !u && op == 0 is the encoding for another instruction. + + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.UqrshrnV); + } + } + + public static void VqshlI(CodeGenContext context, uint rd, uint rm, bool u, uint op, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = InstEmitNeonShift.GetShiftLeft(imm6, size); + + if (u && op == 0) + { + InstEmitNeonCommon.EmitVectorBinaryShift(context, rd, rm, shift, size, q, isShl: true, context.Arm64Assembler.SqshluV, context.Arm64Assembler.SqshluS); + } + else if (!u && op == 1) + { + InstEmitNeonCommon.EmitVectorBinaryShift(context, rd, rm, shift, size, q, isShl: true, context.Arm64Assembler.SqshlImmV, context.Arm64Assembler.SqshlImmS); + } + else + { + Debug.Assert(u && op == 1); // !u && op == 0 is the encoding for another instruction. + + InstEmitNeonCommon.EmitVectorBinaryShift(context, rd, rm, shift, size, q, isShl: true, context.Arm64Assembler.UqshlImmV, context.Arm64Assembler.UqshlImmS); + } + } + + public static void VqshlR(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + if (u) + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rm, rn, size, q, context.Arm64Assembler.UqshlRegV, context.Arm64Assembler.UqshlRegS); + } + else + { + InstEmitNeonCommon.EmitVectorBinary(context, rd, rm, rn, size, q, context.Arm64Assembler.SqshlRegV, context.Arm64Assembler.SqshlRegS); + } + } + + public static void Vqshrn(CodeGenContext context, uint rd, uint rm, bool u, uint op, uint imm6) + { + uint size = InstEmitNeonCommon.GetSizeFromImm6(imm6); + uint shift = InstEmitNeonShift.GetShiftRight(imm6, size); + + if (u && op == 0) + { + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.SqshrunV); + } + else if (!u && op == 1) + { + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.SqshrnV); + } + else + { + Debug.Assert(u && op == 1); // !u && op == 0 is the encoding for another instruction. + + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.UqshrnV); + } + } + + public static void Vqsub(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary( + context, + rd, + rn, + rm, + size, + q, + u ? context.Arm64Assembler.UqsubV : context.Arm64Assembler.SqsubV, + u ? context.Arm64Assembler.UqsubS : context.Arm64Assembler.SqsubS); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonShift.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonShift.cs new file mode 100644 index 00000000..9f8d0bde --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonShift.cs @@ -0,0 +1,123 @@ +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonShift + { + public static void Vshll(CodeGenContext context, uint rd, uint rm, uint imm6, bool u) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6); + uint shift = GetShiftLeft(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryLongShift(context, rd, rm, shift, size, isShl: true, u ? context.Arm64Assembler.Ushll : context.Arm64Assembler.Sshll); + } + + public static void Vshll2(CodeGenContext context, uint rd, uint rm, uint size) + { + // Shift can't be encoded, so shift by value - 1 first, then first again by 1. + // Doesn't matter if we do a signed or unsigned shift in this case since all sign bits will be shifted out. + + uint shift = 8u << (int)size; + + InstEmitNeonCommon.EmitVectorBinaryLongShift(context, rd, rm, shift - 1, size, isShl: true, context.Arm64Assembler.Sshll); + InstEmitNeonCommon.EmitVectorBinaryLongShift(context, rd, rd, 1, size, isShl: true, context.Arm64Assembler.Sshll); + } + + public static void VshlI(CodeGenContext context, uint rd, uint rm, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = GetShiftLeft(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryShift(context, rd, rm, shift, size, q, isShl: true, context.Arm64Assembler.ShlV, context.Arm64Assembler.ShlS); + } + + public static void VshlR(CodeGenContext context, uint rd, uint rn, uint rm, bool u, uint size, uint q) + { + InstEmitNeonCommon.EmitVectorBinary( + context, + rd, + rm, + rn, + size, + q, + u ? context.Arm64Assembler.UshlV : context.Arm64Assembler.SshlV, + u ? context.Arm64Assembler.UshlS : context.Arm64Assembler.SshlS); + } + + public static void Vshr(CodeGenContext context, uint rd, uint rm, bool u, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryShift( + context, + rd, + rm, + shift, + size, + q, + isShl: false, + u ? context.Arm64Assembler.UshrV : context.Arm64Assembler.SshrV, + u ? context.Arm64Assembler.UshrS : context.Arm64Assembler.SshrS); + } + + public static void Vshrn(CodeGenContext context, uint rd, uint rm, uint imm6) + { + uint size = InstEmitNeonCommon.GetSizeFromImm6(imm6); + uint shift = GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryNarrowShift(context, rd, rm, shift, size, isShl: false, context.Arm64Assembler.Shrn); + } + + public static void Vsli(CodeGenContext context, uint rd, uint rm, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = GetShiftLeft(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryShift( + context, + rd, + rm, + shift, + size, + q, + isShl: true, + context.Arm64Assembler.SliV, + context.Arm64Assembler.SliS); + } + + public static void Vsra(CodeGenContext context, uint rd, uint rm, bool u, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorTernaryRdShift( + context, + rd, + rm, + shift, + size, + q, + isShl: false, + u ? context.Arm64Assembler.UsraV : context.Arm64Assembler.SsraV, + u ? context.Arm64Assembler.UsraS : context.Arm64Assembler.SsraS); + } + + public static void Vsri(CodeGenContext context, uint rd, uint rm, uint l, uint imm6, uint q) + { + uint size = InstEmitNeonCommon.GetSizeFromImm7(imm6 | (l << 6)); + uint shift = GetShiftRight(imm6, size); + + InstEmitNeonCommon.EmitVectorBinaryShift(context, rd, rm, shift, size, q, isShl: false, context.Arm64Assembler.SriV, context.Arm64Assembler.SriS); + } + + public static uint GetShiftLeft(uint imm6, uint size) + { + return size < 3 ? imm6 - (8u << (int)size) : imm6; + } + + public static uint GetShiftRight(uint imm6, uint size) + { + return (size == 3 ? 64u : (16u << (int)size)) - imm6; + ; + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonSystem.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonSystem.cs new file mode 100644 index 00000000..8c7bf91d --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonSystem.cs @@ -0,0 +1,77 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitNeonSystem + { + public static void Vmrs(CodeGenContext context, uint rt, uint reg) + { + if (context.ConsumeSkipNextInstruction()) + { + // This case means that we managed to combine a VCMP and VMRS instruction, + // so we have nothing to do here as FCMP/FCMPE already set PSTATE.NZCV. + context.SetNzcvModified(); + + return; + } + + if (reg == 1) + { + // FPSCR + + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + if (rt == RegisterUtils.PcRegister) + { + using ScopedRegister fpsrRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(fpsrRegister.Operand, ctx, NativeContextOffsets.FpFlagsBaseOffset); + context.Arm64Assembler.Lsr(fpsrRegister.Operand, fpsrRegister.Operand, InstEmitCommon.Const(28)); + + InstEmitCommon.RestoreNzcvFlags(context, fpsrRegister.Operand); + + context.SetNzcvModified(); + } + else + { + // FPSCR is a combination of the FPCR and FPSR registers. + // We also need to set the FPSR NZCV bits that no longer exist on AArch64. + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + + context.Arm64Assembler.MrsFpsr(rtOperand); + context.Arm64Assembler.MrsFpcr(tempRegister.Operand); + context.Arm64Assembler.Orr(rtOperand, rtOperand, tempRegister.Operand); + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FpFlagsBaseOffset); + context.Arm64Assembler.Bfc(tempRegister.Operand, 0, 28); + context.Arm64Assembler.Orr(rtOperand, rtOperand, tempRegister.Operand); + } + } + else + { + Operand rtOperand = InstEmitCommon.GetOutputGpr(context, rt); + + context.Arm64Assembler.Mov(rtOperand, 0u); + } + } + + public static void Vmsr(CodeGenContext context, uint rt, uint reg) + { + if (reg == 1) + { + // FPSCR + + // TODO: Do not set bits related to features that are not supported (like FP16)? + + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + + context.Arm64Assembler.MsrFpcr(rtOperand); + context.Arm64Assembler.MsrFpsr(rtOperand); + context.Arm64Assembler.StrRiUn(rtOperand, ctx, NativeContextOffsets.FpFlagsBaseOffset); + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs new file mode 100644 index 00000000..e2354f44 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs @@ -0,0 +1,452 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitSaturate + { + public static void Qadd(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSubSaturate(context, rd, rn, rm, doubling: false, add: true); + } + + public static void Qadd16(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Add(d, n, m); + EmitSaturateRange(context, d, d, 16, unsigned: false, setQ: false); + }); + } + + public static void Qadd8(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Add(d, n, m); + EmitSaturateRange(context, d, d, 8, unsigned: false, setQ: false); + }); + } + + public static void Qasx(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Sub(d, n, m); + } + else + { + context.Arm64Assembler.Add(d, n, m); + } + + EmitSaturateRange(context, d, d, 16, unsigned: false, setQ: false); + }); + } + + public static void Qdadd(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSubSaturate(context, rd, rn, rm, doubling: true, add: true); + } + + public static void Qdsub(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSubSaturate(context, rd, rn, rm, doubling: true, add: false); + } + + public static void Qsax(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Add(d, n, m); + } + else + { + context.Arm64Assembler.Sub(d, n, m); + } + + EmitSaturateRange(context, d, d, 16, unsigned: false, setQ: false); + }); + } + + public static void Qsub(CodeGenContext context, uint rd, uint rn, uint rm) + { + EmitAddSubSaturate(context, rd, rn, rm, doubling: false, add: false); + } + + public static void Qsub16(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Sub(d, n, m); + EmitSaturateRange(context, d, d, 16, unsigned: false, setQ: false); + }); + } + + public static void Qsub8(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Sub(d, n, m); + EmitSaturateRange(context, d, d, 8, unsigned: false, setQ: false); + }); + } + + public static void Ssat(CodeGenContext context, uint rd, uint imm, uint rn, bool sh, uint shift) + { + EmitSaturate(context, rd, imm + 1, rn, sh, shift, unsigned: false); + } + + public static void Ssat16(CodeGenContext context, uint rd, uint imm, uint rn) + { + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, (d, n) => + { + EmitSaturateRange(context, d, n, imm + 1, unsigned: false); + }); + } + + public static void Uqadd16(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Add(d, n, m); + EmitSaturateUnsignedRange(context, d, 16); + }); + } + + public static void Uqadd8(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Add(d, n, m); + EmitSaturateUnsignedRange(context, d, 8); + }); + } + + public static void Uqasx(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitUnsigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Sub(d, n, m); + } + else + { + context.Arm64Assembler.Add(d, n, m); + } + + EmitSaturateUnsignedRange(context, d, 16); + }); + } + + public static void Uqsax(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitUnsigned16BitXPair(context, rd, rn, rm, (d, n, m, e) => + { + if (e == 0) + { + context.Arm64Assembler.Add(d, n, m); + } + else + { + context.Arm64Assembler.Sub(d, n, m); + } + + EmitSaturateUnsignedRange(context, d, 16); + }); + } + + public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Sub(d, n, m); + EmitSaturateUnsignedRange(context, d, 16); + }); + } + + public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm) + { + InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) => + { + context.Arm64Assembler.Sub(d, n, m); + EmitSaturateUnsignedRange(context, d, 8); + }); + } + + public static void Usat(CodeGenContext context, uint rd, uint imm, uint rn, bool sh, uint shift) + { + EmitSaturate(context, rd, imm, rn, sh, shift, unsigned: true); + } + + public static void Usat16(CodeGenContext context, uint rd, uint imm, uint rn) + { + InstEmitCommon.EmitSigned16BitPair(context, rd, rn, (d, n) => + { + EmitSaturateRange(context, d, n, imm, unsigned: true); + }); + } + + private static void EmitAddSubSaturate(CodeGenContext context, uint rd, uint rn, uint rm, bool doubling, bool add) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); + Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); + + context.Arm64Assembler.Sxtw(tempN64, rnOperand); + context.Arm64Assembler.Sxtw(tempM64, rmOperand); + + if (doubling) + { + context.Arm64Assembler.Lsl(tempN64, tempN64, InstEmitCommon.Const(1)); + + EmitSaturateLongToInt(context, tempN64, tempN64); + } + + if (add) + { + context.Arm64Assembler.Add(tempN64, tempN64, tempM64); + } + else + { + context.Arm64Assembler.Sub(tempN64, tempN64, tempM64); + } + + EmitSaturateLongToInt(context, rdOperand, tempN64); + } + + private static void EmitSaturate(CodeGenContext context, uint rd, uint imm, uint rn, bool sh, uint shift, bool unsigned) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + if (sh && shift == 0) + { + shift = 31; + } + + if (shift != 0) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + if (sh) + { + context.Arm64Assembler.Asr(tempRegister.Operand, rnOperand, InstEmitCommon.Const((int)shift)); + } + else + { + context.Arm64Assembler.Lsl(tempRegister.Operand, rnOperand, InstEmitCommon.Const((int)shift)); + } + + EmitSaturateRange(context, rdOperand, tempRegister.Operand, imm, unsigned); + } + else + { + EmitSaturateRange(context, rdOperand, rnOperand, imm, unsigned); + } + } + + private static void EmitSaturateRange(CodeGenContext context, Operand result, Operand value, uint saturateTo, bool unsigned, bool setQ = true) + { + Debug.Assert(saturateTo <= 32); + Debug.Assert(!unsigned || saturateTo < 32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + ScopedRegister tempValue = default; + + bool resultValueOverlap = result.Value == value.Value; + + if (!unsigned && saturateTo == 32) + { + // No saturation possible for this case. + + if (!resultValueOverlap) + { + context.Arm64Assembler.Mov(result, value); + } + + return; + } + else if (saturateTo == 0) + { + // Result is always zero if we saturate 0 bits. + + context.Arm64Assembler.Mov(result, 0u); + + return; + } + + if (resultValueOverlap) + { + tempValue = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempValue.Operand, value); + value = tempValue.Operand; + } + + if (unsigned) + { + // Negative values always saturate (to zero). + // So we must always ignore the sign bit when masking, so that the truncated value will differ from the original one. + + context.Arm64Assembler.And(result, value, InstEmitCommon.Const((int)(uint.MaxValue >> (32 - (int)saturateTo)))); + } + else + { + context.Arm64Assembler.Sbfx(result, value, 0, (int)saturateTo); + } + + context.Arm64Assembler.Sub(tempRegister.Operand, value, result); + + int branchIndex = context.CodeWriter.InstructionPointer; + + // If the result is 0, the values are equal and we don't need saturation. + context.Arm64Assembler.Cbz(tempRegister.Operand, 0); + + // Saturate and set Q flag. + if (unsigned) + { + if (saturateTo == 31) + { + // Only saturation case possible when going from 32 bits signed to 32 or 31 bits unsigned + // is when the signed input is negative, as all positive values are representable on a 31 bits range. + + context.Arm64Assembler.Mov(result, 0u); + } + else + { + context.Arm64Assembler.Asr(result, value, InstEmitCommon.Const(31)); + context.Arm64Assembler.Mvn(result, result); + context.Arm64Assembler.Lsr(result, result, InstEmitCommon.Const(32 - (int)saturateTo)); + } + } + else + { + if (saturateTo == 1) + { + context.Arm64Assembler.Asr(result, value, InstEmitCommon.Const(31)); + } + else + { + context.Arm64Assembler.Mov(result, uint.MaxValue >> (33 - (int)saturateTo)); + context.Arm64Assembler.Eor(result, result, value, ArmShiftType.Asr, 31); + } + } + + if (setQ) + { + SetQFlag(context); + } + + int delta = context.CodeWriter.InstructionPointer - branchIndex; + context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5)); + + if (resultValueOverlap) + { + tempValue.Dispose(); + } + } + + private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo) + { + Debug.Assert(saturateTo <= 32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + if (saturateTo == 32) + { + // No saturation possible for this case. + + return; + } + else if (saturateTo == 0) + { + // Result is always zero if we saturate 0 bits. + + context.Arm64Assembler.Mov(value, 0u); + + return; + } + + context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo)); + + int branchIndex = context.CodeWriter.InstructionPointer; + + // If the result is 0, the values are equal and we don't need saturation. + context.Arm64Assembler.Cbz(tempRegister.Operand, 0); + + // Saturate. + context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo)); + + int delta = context.CodeWriter.InstructionPointer - branchIndex; + context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5)); + } + + private static void EmitSaturateLongToInt(CodeGenContext context, Operand result, Operand value) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + ScopedRegister tempValue = default; + + bool resultValueOverlap = result.Value == value.Value; + + if (resultValueOverlap) + { + tempValue = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand tempValue64 = new(OperandKind.Register, OperandType.I64, tempValue.Operand.Value); + + context.Arm64Assembler.Mov(tempValue64, value); + value = tempValue64; + } + + Operand temp64 = new(OperandKind.Register, OperandType.I64, tempRegister.Operand.Value); + Operand result64 = new(OperandKind.Register, OperandType.I64, result.Value); + + context.Arm64Assembler.Sxtw(result64, value); + context.Arm64Assembler.Sub(temp64, value, result64); + + int branchIndex = context.CodeWriter.InstructionPointer; + + // If the result is 0, the values are equal and we don't need saturation. + context.Arm64Assembler.Cbz(temp64, 0); + + // Saturate and set Q flag. + context.Arm64Assembler.Mov(result, uint.MaxValue >> 1); + context.Arm64Assembler.Eor(result64, result64, value, ArmShiftType.Asr, 63); + + SetQFlag(context); + + int delta = context.CodeWriter.InstructionPointer - branchIndex; + context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5)); + + context.Arm64Assembler.Mov(result, result); // Zero-extend. + + if (resultValueOverlap) + { + tempValue.Dispose(); + } + } + + public static void SetQFlag(CodeGenContext context) + { + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + context.Arm64Assembler.Orr(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(1 << 27)); + context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs new file mode 100644 index 00000000..be0976fd --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs @@ -0,0 +1,648 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitSystem + { + private delegate void SoftwareInterruptHandler(ulong address, int imm); + private delegate ulong Get64(); + private delegate bool GetBool(); + + private const int SpIndex = 31; + + public static void Bkpt(CodeGenContext context, uint imm) + { + context.AddPendingBkpt(imm); + + context.Arm64Assembler.B(0); + } + + public static void Cps(CodeGenContext context, uint imod, uint m, uint a, uint i, uint f, uint mode) + { + // NOP in user mode. + } + + public static void Dbg(CodeGenContext context, uint option) + { + // NOP in ARMv8. + } + + public static void Hlt(CodeGenContext context, uint imm) + { + } + + public static void Mcr(CodeGenContext context, uint encoding, uint coproc, uint opc1, uint rt, uint crn, uint crm, uint opc2) + { + if (coproc != 15 || opc1 != 0) + { + Udf(context, encoding, 0); + + return; + } + + Operand ctx = Register(context.RegisterAllocator.FixedContextRegister); + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + + switch (crn) + { + case 13: // Process and Thread Info. + if (crm == 0) + { + switch (opc2) + { + case 2: + context.Arm64Assembler.StrRiUn(rtOperand, ctx, NativeContextOffsets.TpidrEl0Offset); + return; + } + } + break; + } + } + + public static void Mcrr(CodeGenContext context, uint encoding, uint coproc, uint opc1, uint rt, uint crm) + { + if (coproc != 15 || opc1 != 0) + { + Udf(context, encoding, 0); + + return; + } + + // We don't have any system register that needs to be modified using a 64-bit value. + } + + public static void Mrc(CodeGenContext context, uint encoding, uint coproc, uint opc1, uint rt, uint crn, uint crm, uint opc2) + { + if (coproc != 15 || opc1 != 0) + { + Udf(context, encoding, 0); + + return; + } + + Operand ctx = Register(context.RegisterAllocator.FixedContextRegister); + Operand rtOperand = InstEmitCommon.GetInputGpr(context, rt); + bool hasValue = false; + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + Operand dest = rt == RegisterUtils.PcRegister ? tempRegister.Operand : rtOperand; + + switch (crn) + { + case 13: // Process and Thread Info. + if (crm == 0) + { + switch (opc2) + { + case 2: + context.Arm64Assembler.LdrRiUn(dest, ctx, NativeContextOffsets.TpidrEl0Offset); + hasValue = true; + break; + case 3: + context.Arm64Assembler.LdrRiUn(dest, ctx, NativeContextOffsets.TpidrroEl0Offset); + hasValue = true; + break; + } + } + break; + } + + if (rt == RegisterUtils.PcRegister) + { + context.Arm64Assembler.MsrNzcv(dest); + context.SetNzcvModified(); + } + else if (!hasValue) + { + context.Arm64Assembler.Mov(dest, 0u); + } + } + + public static void Mrrc(CodeGenContext context, uint encoding, uint coproc, uint opc1, uint rt, uint rt2, uint crm) + { + if (coproc != 15) + { + Udf(context, encoding, 0); + + return; + } + + switch (crm) + { + case 14: + switch (opc1) + { + case 0: + context.AddPendingReadCntpct(rt, rt2); + context.Arm64Assembler.B(0); + return; + } + break; + } + + // Unsupported system register. + context.Arm64Assembler.Mov(InstEmitCommon.GetOutputGpr(context, rt), 0u); + context.Arm64Assembler.Mov(InstEmitCommon.GetOutputGpr(context, rt2), 0u); + } + + public static void Mrs(CodeGenContext context, uint rd, bool r) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + + if (r) + { + // Reads SPSR, unpredictable in user mode. + + context.Arm64Assembler.Mov(rdOperand, 0u); + } + else + { + Operand ctx = Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + + // Copy GE flags to destination register. + context.Arm64Assembler.Ubfx(rdOperand, tempRegister.Operand, 16, 4); + + // Insert Q flag. + context.Arm64Assembler.And(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(1 << 27)); + context.Arm64Assembler.Orr(rdOperand, rdOperand, tempRegister.Operand); + + // Insert NZCV flags. + context.Arm64Assembler.MrsNzcv(tempRegister.Operand); + context.Arm64Assembler.Orr(rdOperand, rdOperand, tempRegister.Operand); + + // All other flags can't be accessed in user mode or have "unknown" values. + } + } + + public static void MrsBr(CodeGenContext context, uint rd, uint m1, bool r) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + + // Reads banked register, unpredictable in user mode. + + context.Arm64Assembler.Mov(rdOperand, 0u); + } + + public static void MsrBr(CodeGenContext context, uint rn, uint m1, bool r) + { + // Writes banked register, unpredictable in user mode. + } + + public static void MsrI(CodeGenContext context, uint imm, uint mask, bool r) + { + if (r) + { + // Writes SPSR, unpredictable in user mode. + } + else + { + Operand ctx = Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempRegister2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + + if ((mask & 2) != 0) + { + // Endian flag. + + context.Arm64Assembler.Mov(tempRegister2.Operand, (imm >> 9) & 1); + context.Arm64Assembler.Bfi(tempRegister.Operand, tempRegister2.Operand, 9, 1); + } + + if ((mask & 4) != 0) + { + // GE flags. + + context.Arm64Assembler.Mov(tempRegister2.Operand, (imm >> 16) & 0xf); + context.Arm64Assembler.Bfi(tempRegister.Operand, tempRegister2.Operand, 16, 4); + } + + if ((mask & 8) != 0) + { + // NZCVQ flags. + + context.Arm64Assembler.Mov(tempRegister2.Operand, (imm >> 27) & 0x1f); + context.Arm64Assembler.Bfi(tempRegister.Operand, tempRegister2.Operand, 27, 5); + context.Arm64Assembler.Mov(tempRegister2.Operand, (imm >> 28) & 0xf); + InstEmitCommon.RestoreNzcvFlags(context, tempRegister2.Operand); + context.SetNzcvModified(); + } + } + } + + public static void MsrR(CodeGenContext context, uint rn, uint mask, bool r) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + if (r) + { + // Writes SPSR, unpredictable in user mode. + } + else + { + Operand ctx = Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister tempRegister2 = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + + if ((mask & 2) != 0) + { + // Endian flag. + + context.Arm64Assembler.Lsr(tempRegister2.Operand, rnOperand, InstEmitCommon.Const(9)); + context.Arm64Assembler.Bfi(tempRegister.Operand, tempRegister2.Operand, 9, 1); + } + + if ((mask & 4) != 0) + { + // GE flags. + + context.Arm64Assembler.Lsr(tempRegister2.Operand, rnOperand, InstEmitCommon.Const(16)); + context.Arm64Assembler.Bfi(tempRegister.Operand, tempRegister2.Operand, 16, 4); + } + + if ((mask & 8) != 0) + { + // NZCVQ flags. + + context.Arm64Assembler.Lsr(tempRegister2.Operand, rnOperand, InstEmitCommon.Const(27)); + context.Arm64Assembler.Bfi(tempRegister.Operand, tempRegister2.Operand, 27, 5); + context.Arm64Assembler.Lsr(tempRegister2.Operand, rnOperand, InstEmitCommon.Const(28)); + InstEmitCommon.RestoreNzcvFlags(context, tempRegister2.Operand); + context.SetNzcvModified(); + } + } + } + + public static void Setend(CodeGenContext context, bool e) + { + Operand ctx = Register(context.RegisterAllocator.FixedContextRegister); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.LdrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + + if (e) + { + context.Arm64Assembler.Orr(tempRegister.Operand, tempRegister.Operand, InstEmitCommon.Const(1 << 9)); + } + else + { + context.Arm64Assembler.Bfc(tempRegister.Operand, 9, 1); + } + + context.Arm64Assembler.StrRiUn(tempRegister.Operand, ctx, NativeContextOffsets.FlagsBaseOffset); + } + + public static void Svc(CodeGenContext context, uint imm) + { + context.AddPendingSvc(imm); + context.Arm64Assembler.B(0); + } + + public static void Udf(CodeGenContext context, uint encoding, uint imm) + { + context.AddPendingUdf(encoding); + context.Arm64Assembler.B(0); + } + + public static void PrivilegedInstruction(CodeGenContext context, uint encoding) + { + Udf(context, encoding, 0); + } + + private static IntPtr GetBkptHandlerPtr() + { + return Marshal.GetFunctionPointerForDelegate<SoftwareInterruptHandler>(NativeInterface.Break); + } + + private static IntPtr GetSvcHandlerPtr() + { + return Marshal.GetFunctionPointerForDelegate<SoftwareInterruptHandler>(NativeInterface.SupervisorCall); + } + + private static IntPtr GetUdfHandlerPtr() + { + return Marshal.GetFunctionPointerForDelegate<SoftwareInterruptHandler>(NativeInterface.Undefined); + } + + private static IntPtr GetCntpctEl0Ptr() + { + return Marshal.GetFunctionPointerForDelegate<Get64>(NativeInterface.GetCntpctEl0); + } + + private static IntPtr CheckSynchronizationPtr() + { + return Marshal.GetFunctionPointerForDelegate<GetBool>(NativeInterface.CheckSynchronization); + } + + public static bool NeedsCall(InstName name) + { + // All instructions that might do a host call should be included here. + // That is required to reserve space on the stack for caller saved registers. + + switch (name) + { + case InstName.Mcr: + case InstName.Mrc: + case InstName.Mrrc: + case InstName.Svc: + case InstName.Udf: + return true; + } + + return false; + } + + public static void WriteBkpt(CodeWriter writer, RegisterAllocator regAlloc, TailMerger tailMerger, int spillBaseOffset, uint pc, uint imm) + { + Assembler asm = new(writer); + + WriteCall(ref asm, regAlloc, GetBkptHandlerPtr(), skipContext: true, spillBaseOffset, null, pc, imm); + WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, skipContext: true, spillBaseOffset); + } + + public static void WriteSvc(CodeWriter writer, RegisterAllocator regAlloc, TailMerger tailMerger, int spillBaseOffset, uint pc, uint svcId) + { + Assembler asm = new(writer); + + WriteCall(ref asm, regAlloc, GetSvcHandlerPtr(), skipContext: true, spillBaseOffset, null, pc, svcId); + WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, skipContext: true, spillBaseOffset); + } + + public static void WriteUdf(CodeWriter writer, RegisterAllocator regAlloc, TailMerger tailMerger, int spillBaseOffset, uint pc, uint imm) + { + Assembler asm = new(writer); + + WriteCall(ref asm, regAlloc, GetUdfHandlerPtr(), skipContext: true, spillBaseOffset, null, pc, imm); + WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, skipContext: true, spillBaseOffset); + } + + public static void WriteReadCntpct(CodeWriter writer, RegisterAllocator regAlloc, int spillBaseOffset, int rt, int rt2) + { + Assembler asm = new(writer); + + uint resultMask = (1u << rt) | (1u << rt2); + int tempRegister = 0; + + while ((resultMask & (1u << tempRegister)) != 0 && tempRegister < 32) + { + tempRegister++; + } + + Debug.Assert(tempRegister < 32); + + WriteSpill(ref asm, regAlloc, resultMask, skipContext: false, spillBaseOffset, tempRegister); + + Operand rn = Register(tempRegister); + + asm.Mov(rn, (ulong)GetCntpctEl0Ptr()); + asm.Blr(rn); + + if (rt != rt2) + { + asm.Lsr(Register(rt2), Register(0), InstEmitCommon.Const(32)); + } + + asm.Mov(Register(rt, OperandType.I32), Register(0, OperandType.I32)); // Zero-extend. + + WriteFill(ref asm, regAlloc, resultMask, skipContext: false, spillBaseOffset, tempRegister); + } + + public static void WriteSyncPoint(CodeWriter writer, RegisterAllocator regAlloc, TailMerger tailMerger, int spillBaseOffset) + { + Assembler asm = new(writer); + + WriteSyncPoint(writer, ref asm, regAlloc, tailMerger, skipContext: false, spillBaseOffset); + } + + private static void WriteSyncPoint(CodeWriter writer, ref Assembler asm, RegisterAllocator regAlloc, TailMerger tailMerger, bool skipContext, 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, skipContext, 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, skipContext, 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); + } + + private static void WriteCall( + ref Assembler asm, + RegisterAllocator regAlloc, + IntPtr funcPtr, + bool skipContext, + int spillBaseOffset, + int? resultRegister, + params 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, skipContext, 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, skipContext, spillBaseOffset, tempRegister); + } + + private static void WriteSpill(ref Assembler asm, RegisterAllocator regAlloc, uint exceptMask, bool skipContext, int spillOffset, int tempRegister) + { + WriteSpillOrFill(ref asm, regAlloc, skipContext, exceptMask, spillOffset, tempRegister, spill: true); + } + + private static void WriteFill(ref Assembler asm, RegisterAllocator regAlloc, uint exceptMask, bool skipContext, int spillOffset, int tempRegister) + { + WriteSpillOrFill(ref asm, regAlloc, skipContext, exceptMask, spillOffset, tempRegister, spill: false); + } + + private static void WriteSpillOrFill( + ref Assembler asm, + RegisterAllocator regAlloc, + bool skipContext, + uint exceptMask, + int spillOffset, + int tempRegister, + bool spill) + { + uint gprMask = regAlloc.UsedGprsMask & ~(AbiConstants.GprCalleeSavedRegsMask | exceptMask); + + if (skipContext) + { + gprMask &= ~Compiler.UsableGprsMask; + } + + if (!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(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(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; + } + } + + if (spill) + { + Operand rt = Register(tempRegister, OperandType.I32); + + asm.MrsNzcv(rt); + asm.StrRiUn(rt, Register(SpIndex), spillOffset); + } + + spillOffset += 8; + + if ((spillOffset & 8) != 0) + { + spillOffset += 8; + } + + uint fpSimdMask = regAlloc.UsedFpSimdMask; + + if (skipContext) + { + fpSimdMask &= ~Compiler.UsableFpSimdMask; + } + + 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(SpIndex), spillOffset); + } + else + { + asm.LdpRiUn(Register(reg, OperandType.V128), Register(reg + 1, OperandType.V128), Register(SpIndex), spillOffset); + } + + fpSimdMask &= ~(3u << reg); + spillOffset += 32; + } + else + { + if (spill) + { + asm.StrRiUn(Register(reg, OperandType.V128), Register(SpIndex), spillOffset); + } + else + { + asm.LdrRiUn(Register(reg, OperandType.V128), Register(SpIndex), spillOffset); + } + + fpSimdMask &= ~(1u << reg); + spillOffset += 16; + } + } + } + + public static Operand Register(int register, OperandType type = OperandType.I64) + { + return new Operand(register, RegisterType.Integer, type); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpArithmetic.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpArithmetic.cs new file mode 100644 index 00000000..efb2fc6b --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpArithmetic.cs @@ -0,0 +1,95 @@ +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitVfpArithmetic + { + public static void VabsF(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FabsFloat); + } + + public static void VaddF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FaddFloat); + } + + public static void VdivF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FdivFloat); + } + + public static void VfmaF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryRdF(context, rd, rn, rm, size, context.Arm64Assembler.FmaddFloat); + } + + public static void VfmsF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryRdF(context, rd, rn, rm, size, context.Arm64Assembler.FmsubFloat); + } + + public static void VfnmaF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryRdF(context, rd, rn, rm, size, context.Arm64Assembler.FnmaddFloat); + } + + public static void VfnmsF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryRdF(context, rd, rn, rm, size, context.Arm64Assembler.FnmsubFloat); + } + + public static void Vmaxnm(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FmaxnmFloat); + } + + public static void Vminnm(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FminnmFloat); + } + + public static void VmlaF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryMulNegRdF(context, rd, rn, rm, size, negD: false, negProduct: false); + } + + public static void VmlsF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryMulNegRdF(context, rd, rn, rm, size, negD: false, negProduct: true); + } + + public static void VmulF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FmulFloat); + } + + public static void VnegF(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FnegFloat); + } + + public static void VnmlaF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryMulNegRdF(context, rd, rn, rm, size, negD: true, negProduct: true); + } + + public static void VnmlsF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarTernaryMulNegRdF(context, rd, rn, rm, size, negD: true, negProduct: false); + } + + public static void VnmulF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FnmulFloat); + } + + public static void VsqrtF(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FsqrtFloat); + } + + public static void VsubF(CodeGenContext context, uint rd, uint rn, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarBinaryF(context, rd, rn, rm, size, context.Arm64Assembler.FsubFloat); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs new file mode 100644 index 00000000..f5f30609 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs @@ -0,0 +1,133 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using System; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitVfpCompare + { + public static void VcmpI(CodeGenContext context, uint cond, uint rd, uint size) + { + EmitVcmpVcmpe(context, cond, rd, 0, size, zero: true, e: false); + } + + public static void VcmpR(CodeGenContext context, uint cond, uint rd, uint rm, uint size) + { + EmitVcmpVcmpe(context, cond, rd, rm, size, zero: false, e: false); + } + + public static void VcmpeI(CodeGenContext context, uint cond, uint rd, uint size) + { + EmitVcmpVcmpe(context, cond, rd, 0, size, zero: true, e: true); + } + + public static void VcmpeR(CodeGenContext context, uint cond, uint rd, uint rm, uint size) + { + EmitVcmpVcmpe(context, cond, rd, rm, size, zero: false, e: true); + } + + private static void EmitVcmpVcmpe(CodeGenContext context, uint cond, uint rd, uint rm, uint size, bool zero, bool e) + { + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + uint ftype = size ^ 2u; + uint opc = zero ? 1u : 0u; + + using ScopedRegister rdReg = InstEmitNeonCommon.MoveScalarToSide(context, rd, singleRegs); + ScopedRegister rmReg; + Operand rmOrZero; + + if (zero) + { + rmReg = default; + rmOrZero = new Operand(0, RegisterType.Vector, OperandType.V128); + } + else + { + rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, singleRegs); + rmOrZero = rmReg.Operand; + } + + using ScopedRegister oldFlags = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + bool canPeepholeOptimize = CanFuseVcmpVmrs(context, cond); + if (!canPeepholeOptimize) + { + InstEmitCommon.GetCurrentFlags(context, oldFlags.Operand); + } + + if (e) + { + context.Arm64Assembler.FcmpeFloat(rdReg.Operand, rmOrZero, opc, ftype); + } + else + { + context.Arm64Assembler.FcmpFloat(rdReg.Operand, rmOrZero, opc, ftype); + } + + // Save result flags from the FCMP operation on FPSCR register, then restore the old flags if needed. + + WriteUpdateFpsrNzcv(context); + + if (!canPeepholeOptimize) + { + InstEmitCommon.RestoreNzcvFlags(context, oldFlags.Operand); + } + + if (!zero) + { + rmReg.Dispose(); + } + } + + private static void WriteUpdateFpsrNzcv(CodeGenContext context) + { + using ScopedRegister fpsrRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister); + + context.Arm64Assembler.LdrRiUn(fpsrRegister.Operand, ctx, NativeContextOffsets.FpFlagsBaseOffset); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + context.Arm64Assembler.Bfi(fpsrRegister.Operand, flagsRegister.Operand, 28, 4); + context.Arm64Assembler.StrRiUn(fpsrRegister.Operand, ctx, NativeContextOffsets.FpFlagsBaseOffset); + } + + private static bool CanFuseVcmpVmrs(CodeGenContext context, uint vcmpCond) + { + // Conditions might be different for the VCMP and VMRS instructions if they are inside a IT block, + // we don't bother to check right now, so just always skip if inside an IT block. + if (context.InITBlock) + { + return false; + } + + InstInfo nextInfo = context.PeekNextInstruction(); + + // We're looking for a VMRS instructions. + if (nextInfo.Name != InstName.Vmrs) + { + return false; + } + + // Conditions must match. + if (vcmpCond != (nextInfo.Encoding >> 28)) + { + return false; + } + + // Reg must be 1, Rt must be PC indicating VMRS to PSTATE.NZCV. + if (((nextInfo.Encoding >> 16) & 0xf) != 1 || ((nextInfo.Encoding >> 12) & 0xf) != RegisterUtils.PcRegister) + { + return false; + } + + context.SetSkipNextInstruction(); + + return true; + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpConvert.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpConvert.cs new file mode 100644 index 00000000..8e488695 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpConvert.cs @@ -0,0 +1,305 @@ +using System; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitVfpConvert + { + public static void Vcvta(CodeGenContext context, uint rd, uint rm, bool op, uint size) + { + if (size == 3) + { + // F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register. + + if (op) + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtasFloat); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtauFloat); + } + } + else if (op) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtasS, context.Arm64Assembler.FcvtasSH); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtauS, context.Arm64Assembler.FcvtauSH); + } + } + + public static void Vcvtb(CodeGenContext context, uint rd, uint rm, uint sz, uint op) + { + EmitVcvtbVcvtt(context, rd, rm, sz, op, top: false); + } + + public static void Vcvtm(CodeGenContext context, uint rd, uint rm, bool op, uint size) + { + if (size == 3) + { + // F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register. + + if (op) + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtmsFloat); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtmuFloat); + } + } + else if (op) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtmsS, context.Arm64Assembler.FcvtmsSH); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtmuS, context.Arm64Assembler.FcvtmuSH); + } + } + + public static void Vcvtn(CodeGenContext context, uint rd, uint rm, bool op, uint size) + { + if (size == 3) + { + // F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register. + + if (op) + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtnsFloat); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtnuFloat); + } + } + else if (op) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtnsS, context.Arm64Assembler.FcvtnsSH); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtnuS, context.Arm64Assembler.FcvtnuSH); + } + } + + public static void Vcvtp(CodeGenContext context, uint rd, uint rm, bool op, uint size) + { + if (size == 3) + { + // F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register. + + if (op) + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtpsFloat); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtpuFloat); + } + } + else if (op) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtpsS, context.Arm64Assembler.FcvtpsSH); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtpuS, context.Arm64Assembler.FcvtpuSH); + } + } + + public static void VcvtDs(CodeGenContext context, uint rd, uint rm, uint size) + { + bool doubleToSingle = size == 3; + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (doubleToSingle) + { + // Double to single. + + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, false); + + context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, 0, 1); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, true); + } + else + { + // Single to double. + + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, true); + + context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, 1, 0); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, false); + } + } + + public static void VcvtIv(CodeGenContext context, uint rd, uint rm, bool unsigned, uint size) + { + if (size == 3) + { + // F64 -> S32/U32 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register. + + if (unsigned) + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtzuFloatInt); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryToGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.FcvtzsFloatInt); + } + } + else + { + if (unsigned) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtzuIntS, context.Arm64Assembler.FcvtzuIntSH); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FcvtzsIntS, context.Arm64Assembler.FcvtzsIntSH); + } + } + } + + public static void VcvtVi(CodeGenContext context, uint rd, uint rm, bool unsigned, uint size) + { + if (size == 3) + { + // S32/U32 -> F64 conversion on SIMD is not supported, so we convert it to a GPR, then insert it back into the SIMD register. + + if (unsigned) + { + InstEmitNeonCommon.EmitScalarUnaryFromGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.UcvtfFloatInt); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryFromGprTempF(context, rd, rm, size, 0, context.Arm64Assembler.ScvtfFloatInt); + } + } + else + { + if (unsigned) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.UcvtfIntS, context.Arm64Assembler.UcvtfIntSH); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.ScvtfIntS, context.Arm64Assembler.ScvtfIntSH); + } + } + } + + public static void VcvtXv(CodeGenContext context, uint rd, uint imm5, bool sx, uint sf, uint op, bool u) + { + Debug.Assert(op >> 1 == 0); + + bool unsigned = u; + bool toFixed = op == 1; + uint size = sf; + uint fbits = Math.Clamp((sx ? 32u : 16u) - imm5, 1, 8u << (int)size); + + if (toFixed) + { + if (unsigned) + { + InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: false, context.Arm64Assembler.FcvtzuFixS); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: false, context.Arm64Assembler.FcvtzsFixS); + } + } + else + { + if (unsigned) + { + InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: !sx, context.Arm64Assembler.UcvtfFixS); + } + else + { + InstEmitNeonCommon.EmitScalarUnaryFixedF(context, rd, rd, fbits, size, is16Bit: !sx, context.Arm64Assembler.ScvtfFixS); + } + } + } + + public static void VcvtrIv(CodeGenContext context, uint rd, uint rm, uint op, uint size) + { + bool unsigned = (op & 1) == 0; + + Debug.Assert(size == 1 || size == 2 || size == 3); + + bool singleRegs = size != 3; + + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = InstEmitNeonCommon.PickSimdRegister(context.RegisterAllocator, rmReg); + + // Round using the FPCR rounding mode first, since the FCVTZ instructions will use the round to zero mode. + context.Arm64Assembler.FrintiFloat(tempRegister.Operand, rmReg.Operand, size ^ 2u); + + if (unsigned) + { + if (size == 1) + { + context.Arm64Assembler.FcvtzuIntSH(tempRegister.Operand, tempRegister.Operand); + } + else + { + context.Arm64Assembler.FcvtzuIntS(tempRegister.Operand, tempRegister.Operand, size & 1); + } + } + else + { + if (size == 1) + { + context.Arm64Assembler.FcvtzsIntSH(tempRegister.Operand, tempRegister.Operand); + } + else + { + context.Arm64Assembler.FcvtzsIntS(tempRegister.Operand, tempRegister.Operand, size & 1); + } + } + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + + public static void Vcvtt(CodeGenContext context, uint rd, uint rm, uint sz, uint op) + { + EmitVcvtbVcvtt(context, rd, rm, sz, op, top: true); + } + + public static void EmitVcvtbVcvtt(CodeGenContext context, uint rd, uint rm, uint sz, uint op, bool top) + { + bool usesDouble = sz == 1; + bool convertFromHalf = op == 0; + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped(); + + if (convertFromHalf) + { + // Half to single/double. + + using ScopedRegister rmReg = InstEmitNeonCommon.Move16BitScalarToSide(context, rm, top); + + context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, usesDouble ? 1u : 0u, 3u); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, !usesDouble); + } + else + { + // Single/double to half. + + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, !usesDouble); + + context.Arm64Assembler.FcvtFloat(tempRegister.Operand, rmReg.Operand, 3u, usesDouble ? 1u : 0u); + + InstEmitNeonCommon.Insert16BitResult(context, tempRegister.Operand, rd, top); + } + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpMove.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpMove.cs new file mode 100644 index 00000000..5c1eefac --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpMove.cs @@ -0,0 +1,22 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitVfpMove + { + public static void Vsel(CodeGenContext context, uint rd, uint rn, uint rm, uint cc, uint size) + { + bool singleRegs = size != 3; + uint cond = (cc << 2) | ((cc & 2) ^ ((cc << 1) & 2)); + + using ScopedRegister rnReg = InstEmitNeonCommon.MoveScalarToSide(context, rn, singleRegs); + using ScopedRegister rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, singleRegs); + + using ScopedRegister tempRegister = InstEmitNeonCommon.PickSimdRegister(context.RegisterAllocator, rnReg, rmReg); + + context.Arm64Assembler.FcselFloat(tempRegister.Operand, rnReg.Operand, cond, rmReg.Operand, size ^ 2u); + + InstEmitNeonCommon.InsertResult(context, tempRegister.Operand, rd, singleRegs); + } + } +} diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpRound.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpRound.cs new file mode 100644 index 00000000..2826fa64 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpRound.cs @@ -0,0 +1,40 @@ +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitVfpRound + { + public static void Vrinta(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintaFloat); + } + + public static void Vrintm(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintmFloat); + } + + public static void Vrintn(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintnFloat); + } + + public static void Vrintp(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintpFloat); + } + + public static void Vrintr(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintiFloat); + } + + public static void Vrintx(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintxFloat); + } + + public static void Vrintz(CodeGenContext context, uint rd, uint rm, uint size) + { + InstEmitNeonCommon.EmitScalarUnaryF(context, rd, rm, size, context.Arm64Assembler.FrintzFloat); + } + } +} |
