Add support for calls, and some instructions that depends on them

This commit is contained in:
gdkchan 2019-06-03 00:16:56 -03:00
parent 24038139bf
commit 1d3e1d929e
25 changed files with 747 additions and 50 deletions

View file

@ -0,0 +1,16 @@
namespace ARMeilleure.CodeGen.RegisterAllocators
{
struct AllocationResult
{
public int UsedRegisters { get; }
public int SpillRegionSize { get; }
public int MaxCallArgs { get; }
public AllocationResult(int usedRegisters, int spillRegionSize, int maxCallArgs)
{
UsedRegisters = usedRegisters;
SpillRegionSize = spillRegionSize;
MaxCallArgs = maxCallArgs;
}
}
}

View file

@ -70,13 +70,13 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
}
}
public RAReport RunPass(ControlFlowGraph cfg, RegisterMasks regMasks)
public AllocationResult RunPass(ControlFlowGraph cfg, RegisterMasks regMasks)
{
PhiFunctions.Remove(cfg);
NumberLocals(cfg);
BuildIntervals(cfg);
BuildIntervals(cfg, regMasks, out int maxCallArgs);
//CoalesceCopies(cfg.Blocks);
@ -113,7 +113,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
InsertSplitCopies();
InsertSplitCopiesAtEdges(cfg);
return new RAReport(context.UsedRegisters);
return new AllocationResult(context.UsedRegisters, context.StackAlloc.TotalSize, maxCallArgs);
}
private void AllocateInterval(AllocationContext context, LiveInterval current, int cIndex)
@ -806,8 +806,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
_parentIntervals = _intervals.ToArray();
}
private void BuildIntervals(ControlFlowGraph cfg)
private void BuildIntervals(ControlFlowGraph cfg, RegisterMasks regMasks, out int maxCallArgs)
{
maxCallArgs = 0;
_blockRanges = new LiveRange[cfg.Blocks.Count];
int mapSize = _intervals.Count + RegistersCount;
@ -935,6 +937,27 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
interval.SetStart(operationPos);
interval.AddUsePosition(operationPos);
}
if (node is Operation operation && operation.Inst == Instruction.Call)
{
int callerSavedRegs = regMasks.IntCallerSavedRegisters;
while (callerSavedRegs != 0)
{
int callerSavedReg = BitUtils.LowestBitSet(callerSavedRegs);
LiveInterval interval = _intervals[callerSavedReg];
interval.AddRange(operationPos, operationPos + 1);
callerSavedRegs &= ~(1 << callerSavedReg);
}
if (maxCallArgs < operation.SourcesCount - 1)
{
maxCallArgs = operation.SourcesCount - 1;
}
}
}
}
}

View file

@ -1,12 +0,0 @@
namespace ARMeilleure.CodeGen.RegisterAllocators
{
struct RAReport
{
public int UsedRegisters;
public RAReport(int usedRegisters)
{
UsedRegisters = usedRegisters;
}
}
}

View file

@ -3,11 +3,16 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
struct RegisterMasks
{
public int IntAvailableRegisters { get; }
public int IntCallerSavedRegisters { get; }
public int IntCalleeSavedRegisters { get; }
public RegisterMasks(int intAvailableRegisters, int intCalleeSavedRegisters)
public RegisterMasks(
int intAvailableRegisters,
int intCallerSavedRegisters,
int intCalleeSavedRegisters)
{
IntAvailableRegisters = intAvailableRegisters;
IntCallerSavedRegisters = intCallerSavedRegisters;
IntCalleeSavedRegisters = intCalleeSavedRegisters;
}
}

View file

@ -7,6 +7,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
class StackAllocator
{
public int TotalSize { get; private set; }
private List<ulong> _masks;
public StackAllocator()
@ -44,7 +46,16 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
SetFreeMask(index, free);
return -((index * 32 + freeBit) * 4 + sizeInWords * 4);
int offset = (index * 32 + freeBit) * 4;
int size = offset + sizeInWords * 4;
if (TotalSize < size)
{
TotalSize = size;
}
return offset;
}
free &= ~useMask;

View file

@ -61,6 +61,7 @@ namespace ARMeilleure.CodeGen.X86
Add(X86Instruction.And, new InstInfo(0x000021, 0x040083, 0x040081, BadOp, 0x000023, InstFlags.None));
Add(X86Instruction.Bsr, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000fbd, InstFlags.None));
Add(X86Instruction.Bswap, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000fc8, InstFlags.RegOnly));
Add(X86Instruction.Call, new InstInfo(0x0200ff, BadOp, BadOp, BadOp, BadOp, InstFlags.None));
Add(X86Instruction.Cmovcc, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000f40, InstFlags.None));
Add(X86Instruction.Cmp, new InstInfo(0x000039, 0x070083, 0x070081, BadOp, 0x00003b, InstFlags.None));
Add(X86Instruction.Div, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x0600f7, InstFlags.None));
@ -121,6 +122,11 @@ namespace ARMeilleure.CodeGen.X86
WriteInstruction(dest, null, X86Instruction.Bswap);
}
public void Call(Operand dest)
{
WriteInstruction(dest, null, X86Instruction.Call);
}
public void Cdq()
{
WriteByte(0x99);

View file

@ -1,3 +1,5 @@
using System;
namespace ARMeilleure.CodeGen.X86
{
static class CallingConvention
@ -12,6 +14,17 @@ namespace ARMeilleure.CodeGen.X86
return mask;
}
public static int GetIntCallerSavedRegisters()
{
return (1 << (int)X86Register.Rax) |
(1 << (int)X86Register.Rdx) |
(1 << (int)X86Register.Rcx) |
(1 << (int)X86Register.R8) |
(1 << (int)X86Register.R9) |
(1 << (int)X86Register.R10) |
(1 << (int)X86Register.R11);
}
public static int GetIntCalleeSavedRegisters()
{
return (1 << (int)X86Register.Rbx) |
@ -25,6 +38,24 @@ namespace ARMeilleure.CodeGen.X86
(1 << (int)X86Register.R15);
}
public static int GetIntArgumentsOnRegsCount()
{
return 4;
}
public static X86Register GetIntArgumentRegister(int index)
{
switch (index)
{
case 0: return X86Register.Rcx;
case 1: return X86Register.Rdx;
case 2: return X86Register.R8;
case 3: return X86Register.R9;
}
throw new ArgumentOutOfRangeException(nameof(index));
}
public static X86Register GetIntReturnRegister()
{
return X86Register.Rax;

View file

@ -1,4 +1,5 @@
using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.Common;
using ARMeilleure.IntermediateRepresentation;
using System.Collections.Generic;
using System.Diagnostics;
@ -12,12 +13,14 @@ namespace ARMeilleure.CodeGen.X86
private Stream _stream;
public RAReport RAReport { get; }
public AllocationResult AllocResult { get; }
public Assembler Assembler { get; }
public BasicBlock CurrBlock { get; private set; }
public int CallArgsRegionSize { get; }
private struct Jump
{
public bool IsConditional { get; }
@ -65,19 +68,64 @@ namespace ARMeilleure.CodeGen.X86
private long _jNearPosition;
private int _jNearLength;
public CodeGenContext(Stream stream, RAReport raReport, int blocksCount)
public CodeGenContext(Stream stream, AllocationResult allocResult, int blocksCount)
{
_stream = stream;
RAReport = raReport;
AllocResult = allocResult;
Assembler = new Assembler(stream);
CallArgsRegionSize = GetCallArgsRegionSize(allocResult);
_blockOffsets = new long[blocksCount];
_jumps = new List<Jump>();
}
private int GetCallArgsRegionSize(AllocationResult allocResult)
{
//We need to add 8 bytes to the total size, as the call to this
//function already pushed 8 bytes (the return address).
int mask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.UsedRegisters;
mask |= 1 << (int)X86Register.Rbp;
int calleeSaveRegionSize = CountBits(mask) * 8 + 8;
int argsCount = allocResult.MaxCallArgs;
//The ABI mandates that the space for at least 4 arguments
//is reserved on the stack (this is called shadow space).
if (argsCount < 4)
{
argsCount = 4;
}
int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;
int callArgsAndFrameSize = frameSize + argsCount * 8;
//Ensure that the Stack Pointer will be aligned to 16 bytes.
callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;
return callArgsAndFrameSize - frameSize;
}
private static int CountBits(int mask)
{
int count = 0;
while (mask != 0)
{
mask &= ~(1 << BitUtils.LowestBitSet(mask));
count++;
}
return count;
}
public void EnterBlock(BasicBlock block)
{
_blockOffsets[block.Index] = _stream.Position;

View file

@ -26,6 +26,7 @@ namespace ARMeilleure.CodeGen.X86
Add(Instruction.BranchIfFalse, GenerateBranchIfFalse);
Add(Instruction.BranchIfTrue, GenerateBranchIfTrue);
Add(Instruction.ByteSwap, GenerateByteSwap);
Add(Instruction.Call, GenerateCall);
Add(Instruction.CompareEqual, GenerateCompareEqual);
Add(Instruction.CompareGreater, GenerateCompareGreater);
Add(Instruction.CompareGreaterOrEqual, GenerateCompareGreaterOrEqual);
@ -62,6 +63,7 @@ namespace ARMeilleure.CodeGen.X86
Add(Instruction.SignExtend32, GenerateSignExtend32);
Add(Instruction.SignExtend8, GenerateSignExtend8);
Add(Instruction.Spill, GenerateSpill);
Add(Instruction.SpillArg, GenerateSpillArg);
Add(Instruction.Store, GenerateStore);
Add(Instruction.Store16, GenerateStore16);
Add(Instruction.Store8, GenerateStore8);
@ -82,13 +84,14 @@ namespace ARMeilleure.CodeGen.X86
RegisterMasks regMasks = new RegisterMasks(
CallingConvention.GetIntAvailableRegisters(),
CallingConvention.GetIntCallerSavedRegisters(),
CallingConvention.GetIntCalleeSavedRegisters());
RAReport raReport = regAlloc.RunPass(cfg, regMasks);
AllocationResult allocResult = regAlloc.RunPass(cfg, regMasks);
using (MemoryStream stream = new MemoryStream())
{
CodeGenContext context = new CodeGenContext(stream, raReport, cfg.Blocks.Count);
CodeGenContext context = new CodeGenContext(stream, allocResult, cfg.Blocks.Count);
WritePrologue(context);
@ -121,7 +124,7 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
throw new ArgumentException($"Invalid operation instruction \"{operation.Inst}\".");
throw new ArgumentException($"Invalid instruction \"{operation.Inst}\".");
}
}
@ -182,6 +185,11 @@ namespace ARMeilleure.CodeGen.X86
context.Assembler.Bswap(operation.Dest);
}
private static void GenerateCall(CodeGenContext context, Operation operation)
{
context.Assembler.Call(operation.GetSource(0));
}
private static void GenerateCompareEqual(CodeGenContext context, Operation operation)
{
GenerateCompare(context, operation, X86Condition.Equal);
@ -344,7 +352,9 @@ namespace ARMeilleure.CodeGen.X86
throw new InvalidOperationException("Fill has non-constant stack offset.");
}
X86MemoryOperand memOp = new X86MemoryOperand(dest.Type, Register(X86Register.Rsp), null, Scale.x1, offset.AsInt32());
int offs = offset.AsInt32() + context.CallArgsRegionSize;
X86MemoryOperand memOp = new X86MemoryOperand(dest.Type, Register(X86Register.Rsp), null, Scale.x1, offs);
context.Assembler.Mov(dest, memOp);
}
@ -491,7 +501,26 @@ namespace ARMeilleure.CodeGen.X86
throw new InvalidOperationException("Spill has non-constant stack offset.");
}
X86MemoryOperand memOp = new X86MemoryOperand(source.Type, Register(X86Register.Rsp), null, Scale.x1, offset.AsInt32());
int offs = offset.AsInt32() + context.CallArgsRegionSize;
X86MemoryOperand memOp = new X86MemoryOperand(source.Type, Register(X86Register.Rsp), null, Scale.x1, offs);
context.Assembler.Mov(memOp, source);
}
private static void GenerateSpillArg(CodeGenContext context, Operation operation)
{
Operand offset = operation.GetSource(0);
Operand source = operation.GetSource(1);
if (offset.Kind != OperandKind.Constant)
{
throw new InvalidOperationException("Spill has non-constant stack offset.");
}
int offs = offset.AsInt32();
X86MemoryOperand memOp = new X86MemoryOperand(source.Type, Register(X86Register.Rsp), null, Scale.x1, offs);
context.Assembler.Mov(memOp, source);
}
@ -575,7 +604,7 @@ namespace ARMeilleure.CodeGen.X86
private static void WritePrologue(CodeGenContext context)
{
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.RAReport.UsedRegisters;
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.UsedRegisters;
mask |= 1 << (int)X86Register.Rbp;
@ -587,14 +616,28 @@ namespace ARMeilleure.CodeGen.X86
mask &= ~(1 << bit);
}
int reservedStackSize = context.CallArgsRegionSize + context.AllocResult.SpillRegionSize;
if (reservedStackSize != 0)
{
context.Assembler.Sub(Register(X86Register.Rsp), new Operand(reservedStackSize));
}
}
private static void WriteEpilogue(CodeGenContext context)
{
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.RAReport.UsedRegisters;
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.UsedRegisters;
mask |= 1 << (int)X86Register.Rbp;
int reservedStackSize = context.CallArgsRegionSize + context.AllocResult.SpillRegionSize;
if (reservedStackSize != 0)
{
context.Assembler.Add(Register(X86Register.Rsp), new Operand(reservedStackSize));
}
while (mask != 0)
{
int bit = BitUtils.HighestBitSet(mask);

View file

@ -168,7 +168,7 @@ namespace ARMeilleure.CodeGen.X86
private static void AddFixedRegisterCopy(LinkedListNode<Node> node, Operation operation)
{
if (operation.Dest == null || operation.SourcesCount == 0)
if (operation.SourcesCount == 0)
{
return;
}
@ -241,6 +241,59 @@ namespace ARMeilleure.CodeGen.X86
operation.SetSource(1, rcx);
}
//Copy values to registers expected by the function being called,
//as mandated by the ABI.
if (inst == Instruction.Call)
{
int argsCount = operation.SourcesCount;
int maxArgs = CallingConvention.GetIntArgumentsOnRegsCount();
if (argsCount > maxArgs + 1)
{
argsCount = maxArgs + 1;
}
for (int index = 1; index < argsCount; index++)
{
Operand source = operation.GetSource(index);
Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(index - 1), source.Type);
Operation srcCopyOp = new Operation(Instruction.Copy, argReg, source);
node.List.AddBefore(node, srcCopyOp);
operation.SetSource(index, argReg);
}
//The remaining arguments (those that are not passed on registers)
//should be passed on the stack, we write them to the stack with "SpillArg".
for (int index = argsCount; index < operation.SourcesCount; index++)
{
Operand source = operation.GetSource(index);
Operand offset = new Operand((index - 1) * 8);
Operation srcSpillOp = new Operation(Instruction.SpillArg, null, offset, source);
node.List.AddBefore(node, srcSpillOp);
operation.SetSource(index, new Operand(OperandKind.Undefined));
}
if (dest != null)
{
Operand retReg = Gpr(CallingConvention.GetIntReturnRegister(), dest.Type);
Operation destCopyOp = new Operation(Instruction.Copy, dest, retReg);
node.List.AddAfter(node, destCopyOp);
operation.Dest = retReg;
}
}
}
private static void AddSameDestSrc1Copy(LinkedListNode<Node> node, Operation operation)

View file

@ -6,6 +6,7 @@ namespace ARMeilleure.CodeGen.X86
And,
Bsr,
Bswap,
Call,
Cmovcc,
Cmp,
Div,

View file

@ -1,10 +1,10 @@
namespace ARMeilleure.Decoders
{
class OpCodeDiv : OpCodeAlu
class OpCodeAluBinary : OpCodeAlu
{
public int Rm { get; private set; }
public OpCodeDiv(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
public OpCodeAluBinary(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rm = (opCode >> 16) & 0x1f;
}

View file

@ -73,7 +73,7 @@ namespace ARMeilleure.Decoders
SetA64("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Bl, InstEmit.Bl, typeof(OpCodeBImmAl));
SetA64("1101011000111111000000xxxxx00000", InstName.Blr, InstEmit.Blr, typeof(OpCodeBReg));
SetA64("1101011000011111000000xxxxx00000", InstName.Br, InstEmit.Br, typeof(OpCodeBReg));
SetA64("11010100001xxxxxxxxxxxxxxxx00000", InstName.Brk, null, typeof(OpCodeException));
SetA64("11010100001xxxxxxxxxxxxxxxx00000", InstName.Brk, InstEmit.Brk, typeof(OpCodeException));
SetA64("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Cbnz, InstEmit.Cbnz, typeof(OpCodeBImmCmp));
SetA64("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Cbz, InstEmit.Cbz, typeof(OpCodeBImmCmp));
SetA64("x0111010010xxxxxxxxx10xxxxx0xxxx", InstName.Ccmn, InstEmit.Ccmn, typeof(OpCodeCcmpImm));
@ -83,14 +83,14 @@ namespace ARMeilleure.Decoders
SetA64("11010101000000110011xxxx01011111", InstName.Clrex, null, typeof(OpCodeSystem));
SetA64("x101101011000000000101xxxxxxxxxx", InstName.Cls, InstEmit.Cls, typeof(OpCodeAlu));
SetA64("x101101011000000000100xxxxxxxxxx", InstName.Clz, InstEmit.Clz, typeof(OpCodeAlu));
SetA64("00011010110xxxxx010000xxxxxxxxxx", InstName.Crc32b, null, typeof(OpCodeAluRs));
SetA64("00011010110xxxxx010001xxxxxxxxxx", InstName.Crc32h, null, typeof(OpCodeAluRs));
SetA64("00011010110xxxxx010010xxxxxxxxxx", InstName.Crc32w, null, typeof(OpCodeAluRs));
SetA64("10011010110xxxxx010011xxxxxxxxxx", InstName.Crc32x, null, typeof(OpCodeAluRs));
SetA64("00011010110xxxxx010100xxxxxxxxxx", InstName.Crc32cb, null, typeof(OpCodeAluRs));
SetA64("00011010110xxxxx010101xxxxxxxxxx", InstName.Crc32ch, null, typeof(OpCodeAluRs));
SetA64("00011010110xxxxx010110xxxxxxxxxx", InstName.Crc32cw, null, typeof(OpCodeAluRs));
SetA64("10011010110xxxxx010111xxxxxxxxxx", InstName.Crc32cx, null, typeof(OpCodeAluRs));
SetA64("00011010110xxxxx010000xxxxxxxxxx", InstName.Crc32b, InstEmit.Crc32b, typeof(OpCodeAluBinary));
SetA64("00011010110xxxxx010001xxxxxxxxxx", InstName.Crc32h, InstEmit.Crc32h, typeof(OpCodeAluBinary));
SetA64("00011010110xxxxx010010xxxxxxxxxx", InstName.Crc32w, InstEmit.Crc32w, typeof(OpCodeAluBinary));
SetA64("10011010110xxxxx010011xxxxxxxxxx", InstName.Crc32x, InstEmit.Crc32x, typeof(OpCodeAluBinary));
SetA64("00011010110xxxxx010100xxxxxxxxxx", InstName.Crc32cb, InstEmit.Crc32cb, typeof(OpCodeAluBinary));
SetA64("00011010110xxxxx010101xxxxxxxxxx", InstName.Crc32ch, InstEmit.Crc32ch, typeof(OpCodeAluBinary));
SetA64("00011010110xxxxx010110xxxxxxxxxx", InstName.Crc32cw, InstEmit.Crc32cw, typeof(OpCodeAluBinary));
SetA64("10011010110xxxxx010111xxxxxxxxxx", InstName.Crc32cx, InstEmit.Crc32cx, typeof(OpCodeAluBinary));
SetA64("x0011010100xxxxxxxxx00xxxxxxxxxx", InstName.Csel, InstEmit.Csel, typeof(OpCodeCsel));
SetA64("x0011010100xxxxxxxxx01xxxxxxxxxx", InstName.Csinc, InstEmit.Csinc, typeof(OpCodeCsel));
SetA64("x1011010100xxxxxxxxx00xxxxxxxxxx", InstName.Csinv, InstEmit.Csinv, typeof(OpCodeCsel));
@ -145,9 +145,9 @@ namespace ARMeilleure.Decoders
SetA64("1111100110xxxxxxxxxxxxxxxxxxxxxx", InstName.Pfrm, null, typeof(OpCodeMemImm));
SetA64("11111000100xxxxxxxxx00xxxxxxxxxx", InstName.Pfrm, null, typeof(OpCodeMemImm));
SetA64("11011000xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Pfrm, null, typeof(OpCodeMemLit));
SetA64("x101101011000000000000xxxxxxxxxx", InstName.Rbit, null, typeof(OpCodeAlu));
SetA64("x101101011000000000000xxxxxxxxxx", InstName.Rbit, InstEmit.Rbit, typeof(OpCodeAlu));
SetA64("1101011001011111000000xxxxx00000", InstName.Ret, InstEmit.Ret, typeof(OpCodeBReg));
SetA64("x101101011000000000001xxxxxxxxxx", InstName.Rev16, null, typeof(OpCodeAlu));
SetA64("x101101011000000000001xxxxxxxxxx", InstName.Rev16, InstEmit.Rev16, typeof(OpCodeAlu));
SetA64("x101101011000000000010xxxxxxxxxx", InstName.Rev32, InstEmit.Rev32, typeof(OpCodeAlu));
SetA64("1101101011000000000011xxxxxxxxxx", InstName.Rev64, InstEmit.Rev64, typeof(OpCodeAlu));
SetA64("x0011010110xxxxx001011xxxxxxxxxx", InstName.Rorv, InstEmit.Rorv, typeof(OpCodeAluRs));
@ -155,7 +155,7 @@ namespace ARMeilleure.Decoders
SetA64("x1111010000xxxxx000000xxxxxxxxxx", InstName.Sbcs, InstEmit.Sbcs, typeof(OpCodeAluRs));
SetA64("00010011000xxxxx0xxxxxxxxxxxxxxx", InstName.Sbfm, InstEmit.Sbfm, typeof(OpCodeBfm));
SetA64("1001001101xxxxxxxxxxxxxxxxxxxxxx", InstName.Sbfm, InstEmit.Sbfm, typeof(OpCodeBfm));
SetA64("x0011010110xxxxx000011xxxxxxxxxx", InstName.Sdiv, InstEmit.Sdiv, typeof(OpCodeDiv));
SetA64("x0011010110xxxxx000011xxxxxxxxxx", InstName.Sdiv, InstEmit.Sdiv, typeof(OpCodeAluBinary));
SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", InstName.Smaddl, InstEmit.Smaddl, typeof(OpCodeMul));
SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", InstName.Smsubl, InstEmit.Smsubl, typeof(OpCodeMul));
SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", InstName.Smulh, InstEmit.Smulh, typeof(OpCodeMul));
@ -178,13 +178,13 @@ namespace ARMeilleure.Decoders
SetA64("11101011<<0xxxxxxxxxxxxxxxxxxxxx", InstName.Subs, InstEmit.Subs, typeof(OpCodeAluRs));
SetA64("x1101011001xxxxxxxx0xxxxxxxxxxxx", InstName.Subs, InstEmit.Subs, typeof(OpCodeAluRx));
SetA64("x1101011001xxxxxxxx100xxxxxxxxxx", InstName.Subs, InstEmit.Subs, typeof(OpCodeAluRx));
SetA64("11010100000xxxxxxxxxxxxxxxx00001", InstName.Svc, null, typeof(OpCodeException));
SetA64("11010100000xxxxxxxxxxxxxxxx00001", InstName.Svc, InstEmit.Svc, typeof(OpCodeException));
SetA64("1101010100001xxxxxxxxxxxxxxxxxxx", InstName.Sys, null, typeof(OpCodeSystem));
SetA64("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Tbnz, InstEmit.Tbnz, typeof(OpCodeBImmTest));
SetA64("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Tbz, InstEmit.Tbz, typeof(OpCodeBImmTest));
SetA64("01010011000xxxxx0xxxxxxxxxxxxxxx", InstName.Ubfm, InstEmit.Ubfm, typeof(OpCodeBfm));
SetA64("1101001101xxxxxxxxxxxxxxxxxxxxxx", InstName.Ubfm, InstEmit.Ubfm, typeof(OpCodeBfm));
SetA64("x0011010110xxxxx000010xxxxxxxxxx", InstName.Udiv, InstEmit.Udiv, typeof(OpCodeDiv));
SetA64("x0011010110xxxxx000010xxxxxxxxxx", InstName.Udiv, InstEmit.Udiv, typeof(OpCodeAluBinary));
SetA64("10011011101xxxxx0xxxxxxxxxxxxxxx", InstName.Umaddl, InstEmit.Umaddl, typeof(OpCodeMul));
SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", InstName.Umsubl, InstEmit.Umsubl, typeof(OpCodeMul));
SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", InstName.Umulh, InstEmit.Umulh, typeof(OpCodeMul));

View file

@ -2,7 +2,6 @@ using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
@ -253,20 +252,49 @@ namespace ARMeilleure.Instructions
SetAluD(context, context.BitwiseOr(GetAluN(context), GetAluM(context)));
}
public static void Rbit(EmitterContext context) => EmitCall32_64(context,
nameof(SoftFallback.ReverseBits32),
nameof(SoftFallback.ReverseBits64));
public static void Rev16(EmitterContext context) => EmitCall32_64(context,
nameof(SoftFallback.ReverseBytes16_32),
nameof(SoftFallback.ReverseBytes16_64));
public static void Rev32(EmitterContext context)
{
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
Operand n = GetIntOrZR(op, op.Rn);
if (op.RegisterSize == RegisterSize.Int32)
{
SetAluDOrZR(context, context.ByteSwap(GetIntOrZR(op, op.Rn)));
SetAluDOrZR(context, context.ByteSwap(n));
}
else
{
throw new NotImplementedException();
EmitCall32_64(context, null, nameof(SoftFallback.ReverseBytes32_64));
}
}
private static void EmitCall32_64(EmitterContext context, string name32, string name64)
{
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
Operand n = GetIntOrZR(op, op.Rn);
Operand d;
if (op.RegisterSize == RegisterSize.Int32)
{
d = context.Call(typeof(SoftFallback).GetMethod(name32), n);
}
else
{
d = context.Call(typeof(SoftFallback).GetMethod(name64), n);
}
SetAluDOrZR(context, d);
}
public static void Rev64(EmitterContext context)
{
OpCodeAlu op = (OpCodeAlu)context.CurrOp;

View file

@ -14,7 +14,7 @@ namespace ARMeilleure.Instructions
private static void EmitDiv(EmitterContext context, bool unsigned)
{
OpCodeDiv op = (OpCodeDiv)context.CurrOp;
OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp;
//If Rm == 0, Rd = 0 (division by zero).
Operand n = GetIntOrZR(op, op.Rn);

View file

@ -0,0 +1,49 @@
using ARMeilleure.Decoders;
using ARMeilleure.Translation;
using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit
{
public static void Brk(EmitterContext context)
{
EmitExceptionCall(context, nameof(NativeInterface.Break));
}
public static void Svc(EmitterContext context)
{
EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall));
}
private static void EmitExceptionCall(EmitterContext context, string mthdName)
{
OpCodeException op = (OpCodeException)context.CurrOp;
MethodInfo info = typeof(NativeInterface).GetMethod(mthdName);
context.Call(info, Const(op.Address), Const(op.Id));
if (context.CurrBlock.Next == null)
{
context.Return(Const(op.Address + 4));
}
}
public static void Und(EmitterContext context)
{
OpCode op = context.CurrOp;
MethodInfo info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined));
context.Call(info, Const(op.Address), Const(op.RawOpCode));
if (context.CurrBlock.Next == null)
{
context.Return(Const(op.Address + 4));
}
}
}
}

View file

@ -0,0 +1,66 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit
{
public static void Crc32b(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32b));
}
public static void Crc32h(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32h));
}
public static void Crc32w(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32w));
}
public static void Crc32x(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32x));
}
public static void Crc32cb(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32cb));
}
public static void Crc32ch(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32ch));
}
public static void Crc32cw(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32cw));
}
public static void Crc32cx(EmitterContext context)
{
EmitCrc32Call(context, nameof(SoftFallback.Crc32cx));
}
private static void EmitCrc32Call(EmitterContext context, string name)
{
OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp;
MethodInfo info = typeof(SoftFallback).GetMethod(name);
Operand n = GetIntOrZR(op, op.Rn);
Operand m = GetIntOrZR(op, op.Rm);
Operand d = context.Call(info, n, m);
SetIntOrZR(context, op.Rd, d);
}
}
}

View file

@ -0,0 +1,47 @@
using ARMeilleure.State;
using System.Collections.Concurrent;
using Thread = System.Threading.Thread;
namespace ARMeilleure.Instructions
{
static class NativeInterface
{
private static ConcurrentDictionary<Thread, ExecutionContext> _contexts;
static NativeInterface()
{
_contexts = new ConcurrentDictionary<Thread, ExecutionContext>();
}
public static void RegisterThread(ExecutionContext context)
{
_contexts.TryAdd(Thread.CurrentThread, context);
}
public static void UnregisterThread()
{
_contexts.TryRemove(Thread.CurrentThread, out _);
}
public static void Break(ulong address, int imm)
{
GetContext().OnBreak(address, imm);
}
public static void SupervisorCall(ulong address, int imm)
{
GetContext().OnSupervisorCall(address, imm);
}
public static void Undefined(ulong address, int opCode)
{
GetContext().OnUndefined(address, opCode);
}
private static ExecutionContext GetContext()
{
return _contexts[Thread.CurrentThread];
}
}
}

View file

@ -0,0 +1,167 @@
using System;
namespace ARMeilleure.Instructions
{
static class SoftFallback
{
#region "ShlReg"
#endregion
#region "ShrImm64"
#endregion
#region "Count"
public static ulong CountSetBits8(ulong value) // "size" is 8 (SIMD&FP Inst.).
{
value = ((value >> 1) & 0x55ul) + (value & 0x55ul);
value = ((value >> 2) & 0x33ul) + (value & 0x33ul);
return (value >> 4) + (value & 0x0ful);
}
#endregion
#region "Crc32"
private const uint Crc32RevPoly = 0xedb88320;
private const uint Crc32cRevPoly = 0x82f63b78;
public static uint Crc32b(uint crc, byte val) => Crc32 (crc, Crc32RevPoly, val);
public static uint Crc32h(uint crc, ushort val) => Crc32h(crc, Crc32RevPoly, val);
public static uint Crc32w(uint crc, uint val) => Crc32w(crc, Crc32RevPoly, val);
public static uint Crc32x(uint crc, ulong val) => Crc32x(crc, Crc32RevPoly, val);
public static uint Crc32cb(uint crc, byte val) => Crc32 (crc, Crc32cRevPoly, val);
public static uint Crc32ch(uint crc, ushort val) => Crc32h(crc, Crc32cRevPoly, val);
public static uint Crc32cw(uint crc, uint val) => Crc32w(crc, Crc32cRevPoly, val);
public static uint Crc32cx(uint crc, ulong val) => Crc32x(crc, Crc32cRevPoly, val);
private static uint Crc32h(uint crc, uint poly, ushort val)
{
crc = Crc32(crc, poly, (byte)(val >> 0));
crc = Crc32(crc, poly, (byte)(val >> 8));
return crc;
}
private static uint Crc32w(uint crc, uint poly, uint val)
{
crc = Crc32(crc, poly, (byte)(val >> 0 ));
crc = Crc32(crc, poly, (byte)(val >> 8 ));
crc = Crc32(crc, poly, (byte)(val >> 16));
crc = Crc32(crc, poly, (byte)(val >> 24));
return crc;
}
private static uint Crc32x(uint crc, uint poly, ulong val)
{
crc = Crc32(crc, poly, (byte)(val >> 0 ));
crc = Crc32(crc, poly, (byte)(val >> 8 ));
crc = Crc32(crc, poly, (byte)(val >> 16));
crc = Crc32(crc, poly, (byte)(val >> 24));
crc = Crc32(crc, poly, (byte)(val >> 32));
crc = Crc32(crc, poly, (byte)(val >> 40));
crc = Crc32(crc, poly, (byte)(val >> 48));
crc = Crc32(crc, poly, (byte)(val >> 56));
return crc;
}
private static uint Crc32(uint crc, uint poly, byte val)
{
crc ^= val;
for (int bit = 7; bit >= 0; bit--)
{
uint mask = (uint)(-(int)(crc & 1));
crc = (crc >> 1) ^ (poly & mask);
}
return crc;
}
#endregion
#region "Aes"
#endregion
#region "Sha1"
#endregion
#region "Sha256"
#endregion
#region "Reverse"
public static uint ReverseBits8(uint value)
{
value = ((value & 0xaa) >> 1) | ((value & 0x55) << 1);
value = ((value & 0xcc) >> 2) | ((value & 0x33) << 2);
return (value >> 4) | ((value & 0x0f) << 4);
}
public static uint ReverseBits32(uint value)
{
value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1);
value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2);
value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4);
value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8);
return (value >> 16) | (value << 16);
}
public static ulong ReverseBits64(ulong value)
{
value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((value & 0x5555555555555555) << 1 );
value = ((value & 0xcccccccccccccccc) >> 2 ) | ((value & 0x3333333333333333) << 2 );
value = ((value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((value & 0x0f0f0f0f0f0f0f0f) << 4 );
value = ((value & 0xff00ff00ff00ff00) >> 8 ) | ((value & 0x00ff00ff00ff00ff) << 8 );
value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16);
return (value >> 32) | (value << 32);
}
public static uint ReverseBytes16_32(uint value) => (uint)ReverseBytes16_64(value);
public static ulong ReverseBytes16_64(ulong value) => ReverseBytes(value, RevSize.Rev16);
public static ulong ReverseBytes32_64(ulong value) => ReverseBytes(value, RevSize.Rev32);
private enum RevSize
{
Rev16,
Rev32,
Rev64
}
private static ulong ReverseBytes(ulong value, RevSize size)
{
value = ((value & 0xff00ff00ff00ff00) >> 8) | ((value & 0x00ff00ff00ff00ff) << 8);
if (size == RevSize.Rev16)
{
return value;
}
value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16);
if (size == RevSize.Rev32)
{
return value;
}
value = ((value & 0xffffffff00000000) >> 32) | ((value & 0x00000000ffffffff) << 32);
if (size == RevSize.Rev64)
{
return value;
}
throw new ArgumentException(nameof(size));
}
#endregion
}
}

View file

@ -11,6 +11,7 @@ namespace ARMeilleure.IntermediateRepresentation
BranchIfFalse,
BranchIfTrue,
ByteSwap,
Call,
CompareEqual,
CompareGreater,
CompareGreaterOrEqual,
@ -47,6 +48,7 @@ namespace ARMeilleure.IntermediateRepresentation
SignExtend16,
SignExtend32,
Spill,
SpillArg,
Store,
Store16,
Store8,

View file

@ -8,6 +8,10 @@ namespace ARMeilleure.State
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
public event EventHandler<InstExceptionEventArgs> Break;
public event EventHandler<InstExceptionEventArgs> SupervisorCall;
public event EventHandler<InstUndefinedEventArgs> Undefined;
public ExecutionContext()
{
_nativeContext = new NativeContext();
@ -22,6 +26,21 @@ namespace ARMeilleure.State
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
internal void OnBreak(ulong address, int imm)
{
Break?.Invoke(this, new InstExceptionEventArgs(address, imm));
}
internal void OnSupervisorCall(ulong address, int imm)
{
SupervisorCall?.Invoke(this, new InstExceptionEventArgs(address, imm));
}
internal void OnUndefined(ulong address, int opCode)
{
Undefined?.Invoke(this, new InstUndefinedEventArgs(address, opCode));
}
public void Dispose()
{
_nativeContext.Dispose();

View file

@ -0,0 +1,16 @@
using System;
namespace ARMeilleure.State
{
public class InstExceptionEventArgs : EventArgs
{
public ulong Address { get; }
public int Id { get; }
public InstExceptionEventArgs(ulong address, int id)
{
Address = address;
Id = id;
}
}
}

View file

@ -0,0 +1,16 @@
using System;
namespace ARMeilleure.State
{
public class InstUndefinedEventArgs : EventArgs
{
public ulong Address { get; }
public int OpCode { get; }
public InstUndefinedEventArgs(ulong address, int opCode)
{
Address = address;
OpCode = opCode;
}
}
}

View file

@ -1,7 +1,9 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -81,6 +83,61 @@ namespace ARMeilleure.Translation
return Add(Instruction.ByteSwap, Local(a.Type), a);
}
public Operand Call(MethodInfo info, params Operand[] callArgs)
{
long methodPtr = info.MethodHandle.GetFunctionPointer().ToInt64();
Operand[] args = new Operand[callArgs.Length + 1];
args[0] = Const(methodPtr);
Array.Copy(callArgs, 0, args, 1, callArgs.Length);
if (info.ReturnType != typeof(void))
{
OperandType returnType = GetOperandType(info.ReturnType);
return Add(Instruction.Call, Local(returnType), args);
}
else
{
return Add(Instruction.Call, null, args);
}
}
private static OperandType GetOperandType(Type type)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.UInt16:
case TypeCode.Int16:
case TypeCode.UInt32:
case TypeCode.Int32:
return OperandType.I32;
case TypeCode.UInt64:
case TypeCode.Int64:
return OperandType.I64;
case TypeCode.Single:
return OperandType.FP32;
case TypeCode.Double:
return OperandType.FP64;
}
throw new ArgumentException($"Invalid type \"{type.Name}\".");
}
public Operand Call(OperandType returnType, params Operand[] callArgs)
{
return Add(Instruction.Call, Local(returnType), callArgs);
}
public Operand ConditionalSelect(Operand a, Operand b, Operand c)
{
return Add(Instruction.ConditionalSelect, Local(b.Type), a, b, c);

View file

@ -5,6 +5,7 @@ using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using ARMeilleure.State;
using System;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -25,6 +26,8 @@ namespace ARMeilleure.Translation
public void Execute(ExecutionContext context, ulong address)
{
NativeInterface.RegisterThread(context);
do
{
TranslatedFunction func = Translate(address, ExecutionMode.Aarch64);
@ -32,6 +35,8 @@ namespace ARMeilleure.Translation
address = func.Execute(context);
}
while (address != 0);
NativeInterface.UnregisterThread();
}
private TranslatedFunction Translate(ulong address, ExecutionMode mode)
@ -99,7 +104,7 @@ namespace ARMeilleure.Translation
}
else
{
System.Console.WriteLine("unimpl " + opCode.Instruction.Name + " at 0x" + opCode.Address.ToString("X16"));
throw new InvalidOperationException($"Invalid instruction \"{opCode.Instruction.Name}\".");
}
if (lblPredicateSkip != null)