diff --git a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs index ec115d996a..fe1a0b512f 100644 --- a/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs +++ b/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs @@ -7,7 +7,7 @@ namespace ARMeilleure.CodeGen.Optimizations { static class ConstantFolding { - public static void Fold(Operation operation) + public static void RunPass(Operation operation) { if (operation.Dest == null || operation.SourcesCount == 0) { @@ -184,7 +184,7 @@ namespace ARMeilleure.CodeGen.Optimizations } else if (type == OperandType.I64) { - EvaluateUnaryI64(operation, (x) => (long)x); + EvaluateUnaryI64(operation, (x) => (int)x); } break; diff --git a/ARMeilleure/CodeGen/Optimizations/Optimizer.cs b/ARMeilleure/CodeGen/Optimizations/Optimizer.cs index 14ef8e644f..6038cc40ae 100644 --- a/ARMeilleure/CodeGen/Optimizations/Optimizer.cs +++ b/ARMeilleure/CodeGen/Optimizations/Optimizer.cs @@ -7,7 +7,7 @@ namespace ARMeilleure.CodeGen.Optimizations { static class Optimizer { - public static void Optimize(ControlFlowGraph cfg) + public static void RunPass(ControlFlowGraph cfg) { bool modified; @@ -25,7 +25,7 @@ namespace ARMeilleure.CodeGen.Optimizations bool isUnused = IsUnused(node.Value); - if (!(node.Value is Operation operation) || (isUnused && !IsMemoryStore(operation.Inst))) + if (!(node.Value is Operation operation) || isUnused) { if (isUnused) { @@ -39,9 +39,9 @@ namespace ARMeilleure.CodeGen.Optimizations continue; } - ConstantFolding.Fold(operation); + ConstantFolding.RunPass(operation); - Simplification.Simplify(operation); + Simplification.RunPass(operation); if (DestIsLocalVar(operation) && IsPropagableCopy(operation)) { @@ -131,18 +131,5 @@ namespace ARMeilleure.CodeGen.Optimizations return operation.Dest.Type == operation.GetSource(0).Type; } - - private static bool IsMemoryStore(Instruction inst) - { - switch (inst) - { - case Instruction.Store: - case Instruction.Store16: - case Instruction.Store8: - return true; - } - - return false; - } } } \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Optimizations/Simplification.cs b/ARMeilleure/CodeGen/Optimizations/Simplification.cs index 4092625f50..b929fbdadb 100644 --- a/ARMeilleure/CodeGen/Optimizations/Simplification.cs +++ b/ARMeilleure/CodeGen/Optimizations/Simplification.cs @@ -7,7 +7,7 @@ namespace ARMeilleure.CodeGen.Optimizations { static class Simplification { - public static void Simplify(Operation operation) + public static void RunPass(Operation operation) { switch (operation.Inst) { diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs index bf4e2fc8f7..cb44c08deb 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs @@ -1,4 +1,3 @@ -using ARMeilleure.CodeGen.X86; using ARMeilleure.Common; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; @@ -188,13 +187,13 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { LiveInterval interval = _intervals[iIndex]; - if (interval.Register.Type == regType && interval.Overlaps(current)) + if (interval.Register.Type == regType) { - int nextOverlap = interval.NextOverlap(current); + int overlapPosition = interval.GetOverlapPosition(current); - if (freePositions[interval.Register.Index] > nextOverlap && nextOverlap != -1) + if (overlapPosition != LiveInterval.NotFound && freePositions[interval.Register.Index] > overlapPosition) { - freePositions[interval.Register.Index] = nextOverlap; + freePositions[interval.Register.Index] = overlapPosition; } } } @@ -319,9 +318,14 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { LiveInterval interval = _intervals[iIndex]; - if (interval.IsFixed && interval.Register.Type == regType && interval.Overlaps(current)) + if (interval.IsFixed && interval.Register.Type == regType) { - SetBlockedPosition(interval.Register.Index, interval.NextOverlap(current)); + int overlapPosition = interval.GetOverlapPosition(current); + + if (overlapPosition != LiveInterval.NotFound) + { + SetBlockedPosition(interval.Register.Index, overlapPosition); + } } } @@ -691,18 +695,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { operation.SetSource(srcIndex, register); } - else if (source is X86MemoryOperand memOp) - { - if (memOp.BaseAddress == current.Local) - { - memOp.BaseAddress = register; - } - - if (memOp.Index == current.Local) - { - memOp.Index = register; - } - } } if (operation.Dest == current.Local) @@ -1015,18 +1007,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { yield return source; } - else if (source is X86MemoryOperand memOp) - { - if (IsLocalOrRegister(memOp.BaseAddress.Kind)) - { - yield return memOp.BaseAddress; - } - - if (memOp.Index != null && IsLocalOrRegister(memOp.Index.Kind)) - { - yield return memOp.Index; - } - } } } diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs b/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs index d5809fb467..efc9f902e4 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { class LiveInterval : IComparable { - private const int NotFound = -1; + public const int NotFound = -1; private LiveInterval _parent; @@ -210,6 +210,31 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return false; } + public int GetOverlapPosition(LiveInterval other) + { + foreach (LiveRange range in other._ranges) + { + int overlapIndex = _ranges.BinarySearch(range); + + if (overlapIndex >= 0) + { + //It's possible that we have multiple overlaps within a single interval, + //in this case, we pick the one with the lowest start position, since + //we return the first overlap position. + while (overlapIndex > 0 && _ranges[overlapIndex - 1].End > range.Start) + { + overlapIndex--; + } + + LiveRange overlappingRange = _ranges[overlapIndex]; + + return overlappingRange.Start; + } + } + + return NotFound; + } + public IEnumerable SplitChilds() { return _childs.Values; @@ -243,30 +268,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators return NotFound; } - public int NextOverlap(LiveInterval other) - { - foreach (LiveRange range in other._ranges) - { - int overlapIndex = _ranges.BinarySearch(range); - - if (overlapIndex >= 0) - { - LiveRange overlappingRange = _ranges[overlapIndex]; - - if (range.Start > overlappingRange.Start) - { - return Math.Min(range.End, overlappingRange.End); - } - else - { - return overlappingRange.Start; - } - } - } - - return NotFound; - } - public LiveInterval Split(int position) { LiveInterval right = new LiveInterval(Local, _parent); diff --git a/ARMeilleure/CodeGen/X86/CallingConvention.cs b/ARMeilleure/CodeGen/X86/CallingConvention.cs index bd04af4a50..750f4dc561 100644 --- a/ARMeilleure/CodeGen/X86/CallingConvention.cs +++ b/ARMeilleure/CodeGen/X86/CallingConvention.cs @@ -44,15 +44,7 @@ namespace ARMeilleure.CodeGen.X86 public static int GetIntCalleeSavedRegisters() { - return (1 << (int)X86Register.Rbx) | - (1 << (int)X86Register.Rbp) | - (1 << (int)X86Register.Rdi) | - (1 << (int)X86Register.Rsi) | - (1 << (int)X86Register.Rsp) | - (1 << (int)X86Register.R12) | - (1 << (int)X86Register.R13) | - (1 << (int)X86Register.R14) | - (1 << (int)X86Register.R15); + return GetIntCallerSavedRegisters() ^ RegistersMask; } public static int GetVecCalleeSavedRegisters() diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index b626e7528c..61f3efee07 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -1,3 +1,4 @@ +using ARMeilleure.CodeGen.Optimizations; using ARMeilleure.CodeGen.RegisterAllocators; using ARMeilleure.Common; using ARMeilleure.Diagnostics; @@ -222,6 +223,12 @@ namespace ARMeilleure.CodeGen.X86 public static byte[] Generate(ControlFlowGraph cfg, MemoryManager memory) { + Logger.StartPass(PassName.Optimization); + + Optimizer.RunPass(cfg); + + Logger.EndPass(PassName.Optimization); + Logger.StartPass(PassName.PreAllocation); PreAllocator.RunPass(cfg, memory); @@ -598,7 +605,7 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateLoad(CodeGenContext context, Operation operation) { Operand value = operation.Dest; - Operand address = operation.GetSource(0); + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); if (value.GetRegister().Type == RegisterType.Integer) { @@ -636,27 +643,42 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateLoadSx16(CodeGenContext context, Operation operation) { - context.Assembler.Movsx16(operation.Dest, operation.GetSource(0)); + Operand value = operation.Dest; + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Movsx16(operation.Dest, address); } private static void GenerateLoadSx32(CodeGenContext context, Operation operation) { - context.Assembler.Movsx32(operation.Dest, operation.GetSource(0)); + Operand value = operation.Dest; + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Movsx32(operation.Dest, address); } private static void GenerateLoadSx8(CodeGenContext context, Operation operation) { - context.Assembler.Movsx8(operation.Dest, operation.GetSource(0)); + Operand value = operation.Dest; + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Movsx8(operation.Dest, address); } private static void GenerateLoadZx16(CodeGenContext context, Operation operation) { - context.Assembler.Movzx16(operation.Dest, operation.GetSource(0)); + Operand value = operation.Dest; + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Movzx16(operation.Dest, address); } private static void GenerateLoadZx8(CodeGenContext context, Operation operation) { - context.Assembler.Movzx8(operation.Dest, operation.GetSource(0)); + Operand value = operation.Dest; + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Movzx8(operation.Dest, address); } private static void GenerateMultiply(CodeGenContext context, Operation operation) @@ -781,8 +803,8 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateStore(CodeGenContext context, Operation operation) { - Operand address = operation.GetSource(0); Operand value = operation.GetSource(1); + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); if (value.GetRegister().Type == RegisterType.Integer) { @@ -796,12 +818,18 @@ namespace ARMeilleure.CodeGen.X86 private static void GenerateStore16(CodeGenContext context, Operation operation) { - context.Assembler.Mov16(operation.GetSource(0), operation.GetSource(1)); + Operand value = operation.GetSource(1); + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Mov16(address, value); } private static void GenerateStore8(CodeGenContext context, Operation operation) { - context.Assembler.Mov8(operation.GetSource(0), operation.GetSource(1)); + Operand value = operation.GetSource(1); + Operand address = GetMemoryOperand(operation.GetSource(0), value.Type); + + context.Assembler.Mov8(address, value); } private static void GenerateStoreToContext(CodeGenContext context, Operation operation) @@ -1807,6 +1835,11 @@ namespace ARMeilleure.CodeGen.X86 context.Assembler.Pshufd(dest, dest, 0xfc); } + private static X86MemoryOperand GetMemoryOperand(Operand operand, OperandType type) + { + return new X86MemoryOperand(type, operand, null, Scale.x1, 0); + } + private static Operand Get32BitsRegister(Register reg) { return new Operand(reg.Index, reg.Type, OperandType.I32); diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/ARMeilleure/CodeGen/X86/PreAllocator.cs index 576e36c1f3..852ea8ad5d 100644 --- a/ARMeilleure/CodeGen/X86/PreAllocator.cs +++ b/ARMeilleure/CodeGen/X86/PreAllocator.cs @@ -1,4 +1,3 @@ -using ARMeilleure.CodeGen.Optimizations; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Memory; using ARMeilleure.Translation; @@ -11,64 +10,8 @@ namespace ARMeilleure.CodeGen.X86 { static class PreAllocator { - private class IRModContext - { - private BasicBlock _block; - private LinkedListNode _node; - - public IRModContext(BasicBlock block, LinkedListNode node) - { - _block = block; - _node = node.Previous; - } - - public Operand Append(Instruction inst, Operand src1, Operand src2) - { - Operand destSrc = AppendCopy(src1); - - Operation operation = new Operation(inst, destSrc, destSrc, src2); - - if (src2 is X86MemoryOperand memOp) - { - AddMemoryOperandUse(memOp, operation); - } - - Append(operation); - - return destSrc; - } - - public Operand AppendCopy(Operand src) - { - Operation operation = new Operation(Instruction.Copy, Local(OperandType.I64), src); - - if (src is X86MemoryOperand memOp) - { - AddMemoryOperandUse(memOp, operation); - } - - Append(operation); - - return operation.Dest; - } - - private void Append(Operation operation) - { - if (_node != null) - { - _node = _block.Operations.AddAfter(_node, operation); - } - else - { - _node = _block.Operations.AddFirst(operation); - } - } - } - public static void RunPass(ControlFlowGraph cfg, MemoryManager memory) { - Optimizer.Optimize(cfg); - foreach (BasicBlock block in cfg.Blocks) { LinkedListNode nextNode; @@ -84,23 +27,6 @@ namespace ARMeilleure.CodeGen.X86 Instruction inst = operation.Inst; - if (inst.IsMemory()) - { - IRModContext context = new IRModContext(block, node); - - Operand va = operation.GetSource(0); - - OperandType valueType = inst == Instruction.Store || - inst == Instruction.Store16 || - inst == Instruction.Store8 ? operation.GetSource(1).Type : operation.Dest.Type; - - X86MemoryOperand hostAddr = GuestToHostAddress(context, memory, valueType, va); - - operation.SetSource(0, hostAddr); - - AddMemoryOperandUse(hostAddr, operation); - } - AddConstantCopy(node, operation); //Comparison instructions uses CMOVcc, which does not zero the @@ -233,7 +159,7 @@ namespace ARMeilleure.CodeGen.X86 if (source.Type == OperandType.I32) { - //For 32-bits integer, we can just zero-extend to 64-bits, + //For 32-bits integers, we can just zero-extend to 64-bits, //and then use the 64-bits signed conversion instructions. Operand zex = Local(OperandType.I64); @@ -243,7 +169,7 @@ namespace ARMeilleure.CodeGen.X86 } else /* if (source.Type == OperandType.I64) */ { - //For 64-bits integer, we need to do the following: + //For 64-bits integers, we need to do the following: //- Ensure that the integer has the most significant bit clear. //-- This can be done by shifting the value right by 1, that is, dividing by 2. //-- The least significant bit is lost in this case though. @@ -396,7 +322,7 @@ namespace ARMeilleure.CodeGen.X86 Operand retValueAddr = null; - if (dest.Type == OperandType.V128) + if (dest != null && dest.Type == OperandType.V128) { retValueAddr = Local(OperandType.I64); @@ -425,11 +351,7 @@ namespace ARMeilleure.CodeGen.X86 node.List.AddBefore(node, allocOp); - X86MemoryOperand memOp = new X86MemoryOperand(source.Type, stackAddr, null, Scale.x1, 0); - - Operation storeOp = new Operation(Instruction.Store, null, memOp, source); - - AddMemoryOperandUse(memOp, storeOp); + Operation storeOp = new Operation(Instruction.Store, null, stackAddr, source); node.List.AddBefore(node, storeOp); @@ -479,7 +401,7 @@ namespace ARMeilleure.CodeGen.X86 { Operand source = operation.GetSource(index + 1); - Operand offset = new Operand(index * 8); + Operand offset = new Operand((index + retArgs) * 8); Operation spillOp = new Operation(Instruction.SpillArg, null, offset, source); @@ -513,11 +435,7 @@ namespace ARMeilleure.CodeGen.X86 } else { - X86MemoryOperand memOp = new X86MemoryOperand(dest.Type, retValueAddr, null, Scale.x1, 0); - - Operation loadOp = new Operation(Instruction.Load, dest, memOp); - - AddMemoryOperandUse(memOp, loadOp); + Operation loadOp = new Operation(Instruction.Load, dest, retValueAddr); node.List.AddAfter(node, loadOp); @@ -611,50 +529,6 @@ namespace ARMeilleure.CodeGen.X86 return value == (int)value; } - private static X86MemoryOperand GuestToHostAddress( - IRModContext context, - MemoryManager memory, - OperandType valueType, - Operand va) - { - Operand vaPageOffs = context.Append(Instruction.BitwiseAnd, va, Const((ulong)MemoryManager.PageMask)); - - Operand ptBaseAddr = context.AppendCopy(Const(memory.PageTable.ToInt64())); - - int bit = MemoryManager.PageBits; - - do - { - va = context.Append(Instruction.ShiftRightUI, va, Const(bit)); - - Operand ptOffs = va; - - bit += memory.PtLevelBits; - - if (bit < memory.AddressSpaceBits) - { - ptOffs = context.Append(Instruction.BitwiseAnd, va, Const((ulong)memory.PtLevelMask)); - } - - X86MemoryOperand memOp = new X86MemoryOperand(OperandType.I64, ptBaseAddr, ptOffs, Scale.x8, 0); - - ptBaseAddr = context.AppendCopy(memOp); - } - while (bit < memory.AddressSpaceBits); - - return new X86MemoryOperand(valueType, ptBaseAddr, vaPageOffs, Scale.x1, 0); - } - - private static void AddMemoryOperandUse(X86MemoryOperand memOp, Operation operation) - { - memOp.BaseAddress.Uses.AddLast(operation); - - if (memOp.Index != null) - { - memOp.Index.Uses.AddLast(operation); - } - } - private static void Delete(LinkedListNode node, Operation operation) { operation.Dest = null; @@ -749,6 +623,7 @@ namespace ARMeilleure.CodeGen.X86 case Instruction.CompareLessUI: case Instruction.CompareNotEqual: case Instruction.Multiply: + case Instruction.RotateRight: case Instruction.ShiftLeft: case Instruction.ShiftRightSI: case Instruction.ShiftRightUI: diff --git a/ARMeilleure/Diagnostics/PassName.cs b/ARMeilleure/Diagnostics/PassName.cs index ac0f246e53..ca5e23cf9e 100644 --- a/ARMeilleure/Diagnostics/PassName.cs +++ b/ARMeilleure/Diagnostics/PassName.cs @@ -4,6 +4,7 @@ namespace ARMeilleure.Diagnostics { Translation, SsaConstruction, + Optimization, PreAllocation, RegisterAllocation } diff --git a/ARMeilleure/Instructions/InstEmitMemory.cs b/ARMeilleure/Instructions/InstEmitMemory.cs index b58d0748d2..7fc4f0ef90 100644 --- a/ARMeilleure/Instructions/InstEmitMemory.cs +++ b/ARMeilleure/Instructions/InstEmitMemory.cs @@ -35,15 +35,17 @@ namespace ARMeilleure.Instructions Operand address = GetAddress(context); - Operand value = GetT(context, op.Rt); - - if (signed) + if (signed && op.Extend64) { - value = EmitLoadSx(context, value, address, op.Size); + EmitLoadSx64(context, address, op.Rt, op.Size); + } + else if (signed) + { + EmitLoadSx32(context, address, op.Rt, op.Size); } else { - value = EmitLoadZx(context, value, address, op.Size); + EmitLoadZx(context, address, op.Rt, op.Size); } EmitWBackIfNeeded(context, address); @@ -58,15 +60,13 @@ namespace ARMeilleure.Instructions return; } - Operand value = GetT(context, op.Rt); - if (op.Signed) { - value = EmitLoadSx(context, value, Const(op.Immediate), op.Size); + EmitLoadSx64(context, Const(op.Immediate), op.Rt, op.Size); } else { - value = EmitLoadZx(context, value, Const(op.Immediate), op.Size); + EmitLoadZx(context, Const(op.Immediate), op.Rt, op.Size); } } @@ -76,15 +76,13 @@ namespace ARMeilleure.Instructions void EmitLoad(int rt, Operand ldAddr) { - Operand value = GetT(context, rt); - if (op.Extend64) { - value = EmitLoadSx(context, value, ldAddr, op.Size); + EmitLoadSx64(context, ldAddr, rt, op.Size); } else { - value = EmitLoadZx(context, value, ldAddr, op.Size); + EmitLoadZx(context, ldAddr, rt, op.Size); } } @@ -104,9 +102,7 @@ namespace ARMeilleure.Instructions Operand address = GetAddress(context); - Operand t = GetT(context, op.Rt); - - EmitStore(context, address, t, op.Size); + EmitStore(context, address, op.Rt, op.Size); EmitWBackIfNeeded(context, address); } @@ -119,11 +115,8 @@ namespace ARMeilleure.Instructions Operand address2 = context.Add(address, Const(1L << op.Size)); - Operand t = GetT(context, op.Rt); - Operand t2 = GetT(context, op.Rt2); - - EmitStore(context, address, t, op.Size); - EmitStore(context, address2, t2, op.Size); + EmitStore(context, address, op.Rt, op.Size); + EmitStore(context, address2, op.Rt2, op.Size); EmitWBackIfNeeded(context, address); } diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index ed2cb22464..bcb516e84d 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -1,8 +1,10 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Memory; using ARMeilleure.State; using ARMeilleure.Translation; using System; +using System.Reflection; using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -11,69 +13,423 @@ namespace ARMeilleure.Instructions { static class InstEmitMemoryHelper { - public static Operand EmitLoadZx( - EmitterContext context, - Operand value, - Operand address, - int size) + private static bool ForceFallback = false; + + private enum Extension { - //TODO: Support vector loads with size < 4. - //Also handle value.Kind == OperandKind.Constant. + Zx, + Sx32, + Sx64 + } - switch (size) + public static void EmitLoadZx(EmitterContext context, Operand address, int rt, int size) + { + EmitLoad(context, address, Extension.Zx, rt, size); + } + + public static void EmitLoadSx32(EmitterContext context, Operand address, int rt, int size) + { + EmitLoad(context, address, Extension.Sx32, rt, size); + } + + public static void EmitLoadSx64(EmitterContext context, Operand address, int rt, int size) + { + EmitLoad(context, address, Extension.Sx64, rt, size); + } + + private static void EmitLoad(EmitterContext context, Operand address, Extension ext, int rt, int size) + { + bool isSimd = IsSimd(context); + + if ((uint)size > (isSimd ? 4 : 3)) { - case 0: return context.LoadZx8 (value, address); - case 1: return context.LoadZx16(value, address); - case 2: return context.Load (value, address); - case 3: return context.Load (value, address); - case 4: return context.Load (value, address); + throw new ArgumentOutOfRangeException(nameof(size)); + } - default: throw new ArgumentOutOfRangeException(nameof(size)); + if (isSimd) + { + if (ForceFallback || !Optimizations.UseSse2 || size < 2) + { + EmitReadVectorFallback(context, address, rt, size); + } + else + { + EmitReadVector(context, address, rt, size); + } + } + else + { + if (ForceFallback) + { + EmitReadIntFallback(context, address, rt, size); + } + else + { + EmitReadInt(context, address, rt, size); + } + } + + if (!isSimd) + { + Operand value = GetT(context, rt); + + if (ext == Extension.Sx64) + { + value = context.Copy(Local(OperandType.I64), value); + } + + if (ext == Extension.Sx32 || ext == Extension.Sx64) + { + switch (size) + { + case 0: value = context.SignExtend8 (value); break; + case 1: value = context.SignExtend16(value); break; + case 2: value = context.SignExtend32(value); break; + } + } } } - public static Operand EmitLoadSx( - EmitterContext context, - Operand value, - Operand address, - int size) + public static void EmitStore(EmitterContext context, Operand address, int rt, int size) { - //TODO: Support vector loads with size < 4. - //Also handle value.Kind == OperandKind.Constant. + bool isSimd = IsSimd(context); - switch (size) + if ((uint)size > (isSimd ? 4 : 3)) { - case 0: return context.LoadSx8 (value, address); - case 1: return context.LoadSx16(value, address); - case 2: return context.LoadSx32(value, address); - case 3: return context.Load (value, address); - case 4: return context.Load (value, address); + throw new ArgumentOutOfRangeException(nameof(size)); + } - default: throw new ArgumentOutOfRangeException(nameof(size)); + if (isSimd) + { + if (ForceFallback || !Optimizations.UseSse2 || size < 2) + { + EmitWriteVectorFallback(context, address, rt, size); + } + else + { + EmitWriteVector(context, address, rt, size); + } + } + else + { + if (ForceFallback) + { + EmitWriteIntFallback(context, address, rt, size); + } + else + { + EmitWriteInt(context, address, rt, size); + } } } - public static void EmitStore( - EmitterContext context, - Operand address, - Operand value, - int size) + private static bool IsSimd(EmitterContext context) { - //TODO: Support vector stores with size < 4. + return context.CurrOp is IOpCodeSimd && + !(context.CurrOp is OpCodeSimdMemMs || + context.CurrOp is OpCodeSimdMemSs); + } + + private static void EmitReadInt(EmitterContext context, Operand address, int rt, int size) + { + Operand isUnalignedAddr = EmitAddressCheck(context, address, size); + + Operand lblFastPath = Label(); + Operand lblSlowPath = Label(); + Operand lblEnd = Label(); + + context.BranchIfFalse(lblFastPath, isUnalignedAddr); + + context.MarkLabel(lblSlowPath); + + EmitReadIntFallback(context, address, rt, size); + + context.Branch(lblEnd); + + context.MarkLabel(lblFastPath); + + Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath); + + Operand value = null; switch (size) { - case 0: context.Store8 (address, value); break; - case 1: context.Store16(address, value); break; - case 2: context.Store (address, value); break; - case 3: context.Store (address, value); break; - case 4: context.Store (address, value); break; - - default: throw new ArgumentOutOfRangeException(nameof(size)); + case 0: value = context.LoadZx8 (Local(OperandType.I32), physAddr); break; + case 1: value = context.LoadZx16(Local(OperandType.I32), physAddr); break; + case 2: value = context.Load (Local(OperandType.I32), physAddr); break; + case 3: value = context.Load (Local(OperandType.I64), physAddr); break; } + + context.Copy(GetT(context, rt), value); + + context.MarkLabel(lblEnd); } - public static Operand GetT(EmitterContext context, int rt) + private static void EmitReadVector(EmitterContext context, Operand address, int rt, int size) + { + Operand isUnalignedAddr = EmitAddressCheck(context, address, size); + + Operand lblFastPath = Label(); + Operand lblSlowPath = Label(); + Operand lblEnd = Label(); + + context.BranchIfFalse(lblFastPath, isUnalignedAddr); + + context.MarkLabel(lblSlowPath); + + EmitReadVectorFallback(context, address, rt, size); + + context.Branch(lblEnd); + + context.MarkLabel(lblFastPath); + + Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath); + + Operand value = null; + + /*switch (size) + { + case 2: context.EmitCall(typeof(Sse), nameof(Sse.LoadScalarVector128)); break; + + case 3: + { + Type[] types = new Type[] { typeof(double*) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.LoadScalarVector128), types)); + + break; + } + + case 4: context.EmitCall(typeof(Sse), nameof(Sse.LoadAlignedVector128)); break; + + throw new InvalidOperationException($"Invalid vector load size of {1 << size} bytes."); + }*/ + + context.Copy(GetVec(rt), value); + + context.MarkLabel(lblEnd); + } + + private static void EmitWriteInt(EmitterContext context, Operand address, int rt, int size) + { + Operand isUnalignedAddr = EmitAddressCheck(context, address, size); + + Operand lblFastPath = Label(); + Operand lblSlowPath = Label(); + Operand lblEnd = Label(); + + context.BranchIfFalse(lblFastPath, isUnalignedAddr); + + context.MarkLabel(lblSlowPath); + + EmitWriteIntFallback(context, address, rt, size); + + context.Branch(lblEnd); + + context.MarkLabel(lblFastPath); + + Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath); + + Operand value = GetT(context, rt); + + if (size < 3) + { + value = context.Copy(Local(OperandType.I32), value); + } + + switch (size) + { + case 0: context.Store8 (physAddr, value); break; + case 1: context.Store16(physAddr, value); break; + case 2: context.Store (physAddr, value); break; + case 3: context.Store (physAddr, value); break; + } + + context.MarkLabel(lblEnd); + } + + private static void EmitWriteVector(EmitterContext context, Operand address, int rt, int size) + { + Operand isUnalignedAddr = EmitAddressCheck(context, address, size); + + Operand lblFastPath = Label(); + Operand lblSlowPath = Label(); + Operand lblEnd = Label(); + + context.BranchIfFalse(lblFastPath, isUnalignedAddr); + + context.MarkLabel(lblSlowPath); + + EmitWriteVectorFallback(context, address, rt, size); + + context.Branch(lblEnd); + + context.MarkLabel(lblFastPath); + + Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath); + + Operand value = GetVec(rt); + + /*switch (size) + { + case 2: context.EmitCall(typeof(Sse), nameof(Sse.StoreScalar)); break; + case 3: context.EmitCall(typeof(Sse2), nameof(Sse2.StoreScalar)); break; + case 4: context.EmitCall(typeof(Sse), nameof(Sse.StoreAligned)); break; + + default: throw new InvalidOperationException($"Invalid vector store size of {1 << size} bytes."); + }*/ + + context.MarkLabel(lblEnd); + } + + private static Operand EmitAddressCheck(EmitterContext context, Operand address, int size) + { + long addressCheckMask = ~(context.Memory.AddressSpaceSize - 1); + + addressCheckMask |= (1u << size) - 1; + + return context.BitwiseAnd(address, Const(addressCheckMask)); + } + + private static Operand EmitPtPointerLoad(EmitterContext context, Operand address, Operand lblFallbackPath) + { + Operand pte = Const(context.Memory.PageTable.ToInt64()); + + int bit = MemoryManager.PageBits; + + do + { + Operand addrPart = context.ShiftRightUI(address, Const(bit)); + + bit += context.Memory.PtLevelBits; + + if (bit < context.Memory.AddressSpaceBits) + { + addrPart = context.BitwiseAnd(addrPart, Const((long)context.Memory.PtLevelMask)); + } + + Operand pteOffset = context.ShiftLeft(addrPart, Const(3)); + + Operand pteAddress = context.Add(pte, pteOffset); + + pte = context.Load(Local(OperandType.I64), pteAddress); + } + while (bit < context.Memory.AddressSpaceBits); + + if (!context.Memory.HasWriteWatchSupport) + { + Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask)); + + context.BranchIfTrue(lblFallbackPath, hasFlagSet); + } + + Operand pageOffset = context.BitwiseAnd(address, Const((long)MemoryManager.PageMask)); + + Operand physAddr = context.Add(pte, pageOffset); + + return physAddr; + } + + private static void EmitReadIntFallback(EmitterContext context, Operand address, int rt, int size) + { + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(NativeInterface.ReadByte); break; + case 1: fallbackMethodName = nameof(NativeInterface.ReadUInt16); break; + case 2: fallbackMethodName = nameof(NativeInterface.ReadUInt32); break; + case 3: fallbackMethodName = nameof(NativeInterface.ReadUInt64); break; + } + + MethodInfo info = typeof(NativeInterface).GetMethod(fallbackMethodName); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + address = context.Copy(Local(OperandType.I64), address); + } + + context.Copy(GetT(context, rt), context.Call(info, address)); + } + + private static void EmitReadVectorFallback(EmitterContext context, Operand address, int rt, int size) + { + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(NativeInterface.ReadVector8); break; + case 1: fallbackMethodName = nameof(NativeInterface.ReadVector16); break; + case 2: fallbackMethodName = nameof(NativeInterface.ReadVector32); break; + case 3: fallbackMethodName = nameof(NativeInterface.ReadVector64); break; + case 4: fallbackMethodName = nameof(NativeInterface.ReadVector128); break; + } + + MethodInfo info = typeof(NativeInterface).GetMethod(fallbackMethodName); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + address = context.Copy(Local(OperandType.I64), address); + } + + context.Copy(GetVec(rt), context.Call(info, address)); + } + + private static void EmitWriteIntFallback(EmitterContext context, Operand address, int rt, int size) + { + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(NativeInterface.WriteByte); break; + case 1: fallbackMethodName = nameof(NativeInterface.WriteUInt16); break; + case 2: fallbackMethodName = nameof(NativeInterface.WriteUInt32); break; + case 3: fallbackMethodName = nameof(NativeInterface.WriteUInt64); break; + } + + MethodInfo info = typeof(NativeInterface).GetMethod(fallbackMethodName); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + address = context.Copy(Local(OperandType.I64), address); + } + + Operand value = GetT(context, rt); + + if (size < 3) + { + value = context.Copy(Local(OperandType.I32), value); + } + + context.Call(info, address, value); + } + + private static void EmitWriteVectorFallback(EmitterContext context, Operand address, int rt, int size) + { + string fallbackMethodName = null; + + switch (size) + { + case 0: fallbackMethodName = nameof(NativeInterface.WriteVector8); break; + case 1: fallbackMethodName = nameof(NativeInterface.WriteVector16); break; + case 2: fallbackMethodName = nameof(NativeInterface.WriteVector32); break; + case 3: fallbackMethodName = nameof(NativeInterface.WriteVector64); break; + case 4: fallbackMethodName = nameof(NativeInterface.WriteVector128); break; + } + + MethodInfo info = typeof(NativeInterface).GetMethod(fallbackMethodName); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + address = context.Copy(Local(OperandType.I64), address); + } + + Operand value = GetVec(rt); + + context.Call(info, address, value); + } + + private static Operand GetT(EmitterContext context, int rt) { OpCode op = context.CurrOp; diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs index f8fe826c40..db83696835 100644 --- a/ARMeilleure/Instructions/NativeInterface.cs +++ b/ARMeilleure/Instructions/NativeInterface.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Memory; using ARMeilleure.State; using System.Collections.Concurrent; @@ -7,16 +8,28 @@ namespace ARMeilleure.Instructions { static class NativeInterface { - private static ConcurrentDictionary _contexts; + private struct ThreadContext + { + public ExecutionContext Context { get; } + public MemoryManager Memory { get; } + + public ThreadContext(ExecutionContext context, MemoryManager memory) + { + Context = context; + Memory = memory; + } + } + + private static ConcurrentDictionary _contexts; static NativeInterface() { - _contexts = new ConcurrentDictionary(); + _contexts = new ConcurrentDictionary(); } - public static void RegisterThread(ExecutionContext context) + public static void RegisterThread(ExecutionContext context, MemoryManager memory) { - _contexts.TryAdd(Thread.CurrentThread, context); + _contexts.TryAdd(Thread.CurrentThread, new ThreadContext(context, memory)); } public static void UnregisterThread() @@ -39,9 +52,104 @@ namespace ARMeilleure.Instructions GetContext().OnUndefined(address, opCode); } + public static byte ReadByte(ulong address) + { + return GetMemoryManager().ReadByte((long)address); + } + + public static ushort ReadUInt16(ulong address) + { + return GetMemoryManager().ReadUInt16((long)address); + } + + public static uint ReadUInt32(ulong address) + { + return GetMemoryManager().ReadUInt32((long)address); + } + + public static ulong ReadUInt64(ulong address) + { + return GetMemoryManager().ReadUInt64((long)address); + } + + public static V128 ReadVector8(ulong address) + { + return new V128(0); //TODO + } + + public static V128 ReadVector16(ulong address) + { + return new V128(0); //TODO + } + + public static V128 ReadVector32(ulong address) + { + return new V128(0); //TODO + } + + public static V128 ReadVector64(ulong address) + { + return new V128(0); //TODO + } + + public static V128 ReadVector128(ulong address) + { + return new V128(0); //TODO + } + + public static void WriteByte(ulong address, byte value) + { + GetMemoryManager().WriteByte((long)address, value); + } + + public static void WriteUInt16(ulong address, ushort value) + { + GetMemoryManager().WriteUInt16((long)address, value); + } + + public static void WriteUInt32(ulong address, uint value) + { + GetMemoryManager().WriteUInt32((long)address, value); + } + + public static void WriteUInt64(ulong address, ulong value) + { + GetMemoryManager().WriteUInt64((long)address, value); + } + + public static void WriteVector8(ulong address, V128 value) + { + //TODO + } + + public static void WriteVector16(ulong address, V128 value) + { + //TODO + } + + public static void WriteVector32(ulong address, V128 value) + { + //TODO + } + + public static void WriteVector64(ulong address, V128 value) + { + //TODO + } + + public static void WriteVector128(ulong address, V128 value) + { + //TODO + } + public static ExecutionContext GetContext() { - return _contexts[Thread.CurrentThread]; + return _contexts[Thread.CurrentThread].Context; + } + + public static MemoryManager GetMemoryManager() + { + return _contexts[Thread.CurrentThread].Memory; } } } \ No newline at end of file diff --git a/ARMeilleure/IntermediateRepresentation/Instruction.cs b/ARMeilleure/IntermediateRepresentation/Instruction.cs index c707f5c55f..0f6a004af5 100644 --- a/ARMeilleure/IntermediateRepresentation/Instruction.cs +++ b/ARMeilleure/IntermediateRepresentation/Instruction.cs @@ -228,25 +228,6 @@ namespace ARMeilleure.IntermediateRepresentation return false; } - public static bool IsMemory(this Instruction inst) - { - switch (inst) - { - case Instruction.Load: - case Instruction.LoadSx16: - case Instruction.LoadSx32: - case Instruction.LoadSx8: - case Instruction.LoadZx16: - case Instruction.LoadZx8: - case Instruction.Store: - case Instruction.Store16: - case Instruction.Store8: - return true; - } - - return false; - } - public static bool IsShift(this Instruction inst) { switch (inst) diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs index 57e7cae956..b32d5f9f46 100644 --- a/ARMeilleure/Translation/EmitterContext.cs +++ b/ARMeilleure/Translation/EmitterContext.cs @@ -1,5 +1,6 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Memory; using ARMeilleure.State; using System; using System.Collections.Generic; @@ -16,6 +17,8 @@ namespace ARMeilleure.Translation public Aarch32Mode Mode { get; } + public MemoryManager Memory { get; set; } + private Dictionary _labels; private Dictionary _irLabels; diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 8e1343e29a..2d1a8766ca 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -26,7 +26,7 @@ namespace ARMeilleure.Translation public void Execute(ExecutionContext context, ulong address) { - NativeInterface.RegisterThread(context); + NativeInterface.RegisterThread(context, _memory); do { @@ -43,6 +43,8 @@ namespace ARMeilleure.Translation { EmitterContext context = new EmitterContext(); + context.Memory = _memory; + Block[] blocks = Decoder.DecodeFunction(_memory, address, ExecutionMode.Aarch64); ControlFlowGraph cfg = EmitAndGetCFG(context, blocks);