ecc64c934d
* Add `Operand.Label` support to `Assembler` This adds label support to `Assembler` and enables branch tightening when compiling with relocatables. Jump management and patching has been moved to the `Assembler`. * Move instruction table to `Assembler.Table` * Set PTC internal version * Rename `Assembler.Table` to `AssemblerTable`
104 lines
No EOL
3.6 KiB
C#
104 lines
No EOL
3.6 KiB
C#
using ARMeilleure.CodeGen.RegisterAllocators;
|
|
using ARMeilleure.IntermediateRepresentation;
|
|
using System.IO;
|
|
using System.Numerics;
|
|
|
|
namespace ARMeilleure.CodeGen.X86
|
|
{
|
|
class CodeGenContext
|
|
{
|
|
private readonly Stream _stream;
|
|
private readonly Operand[] _blockLabels;
|
|
|
|
public int StreamOffset => (int)_stream.Length;
|
|
|
|
public AllocationResult AllocResult { get; }
|
|
|
|
public Assembler Assembler { get; }
|
|
public BasicBlock CurrBlock { get; private set; }
|
|
|
|
public int CallArgsRegionSize { get; }
|
|
public int XmmSaveRegionSize { get; }
|
|
|
|
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
|
{
|
|
_stream = new MemoryStream();
|
|
_blockLabels = new Operand[blocksCount];
|
|
|
|
AllocResult = allocResult;
|
|
Assembler = new Assembler(_stream, relocatable);
|
|
|
|
CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
|
|
XmmSaveRegionSize = xmmSaveRegionSize;
|
|
}
|
|
|
|
private static int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
|
|
{
|
|
// We need to add 8 bytes to the total size, as the call to this function already pushed 8 bytes (the
|
|
// return address).
|
|
int intMask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.IntUsedRegisters;
|
|
int vecMask = CallingConvention.GetVecCalleeSavedRegisters() & allocResult.VecUsedRegisters;
|
|
|
|
xmmSaveRegionSize = BitOperations.PopCount((uint)vecMask) * 16;
|
|
|
|
int calleeSaveRegionSize = BitOperations.PopCount((uint)intMask) * 8 + xmmSaveRegionSize + 8;
|
|
|
|
int argsCount = maxCallArgs;
|
|
|
|
if (argsCount < 0)
|
|
{
|
|
// When the function has no calls, argsCount is -1. In this case, we don't need to allocate the shadow
|
|
// space.
|
|
argsCount = 0;
|
|
}
|
|
else if (argsCount < 4)
|
|
{
|
|
// The ABI mandates that the space for at least 4 arguments is reserved on the stack (this is called
|
|
// shadow space).
|
|
argsCount = 4;
|
|
}
|
|
|
|
// TODO: Align XMM save region to 16 bytes because unwinding on Windows requires it.
|
|
int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;
|
|
|
|
// TODO: Instead of always multiplying by 16 (the largest possible size of a variable, since a V128 has 16
|
|
// bytes), we should calculate the exact size consumed by the arguments passed to the called functions on
|
|
// the stack.
|
|
int callArgsAndFrameSize = frameSize + argsCount * 16;
|
|
|
|
// Ensure that the Stack Pointer will be aligned to 16 bytes.
|
|
callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;
|
|
|
|
return callArgsAndFrameSize - frameSize;
|
|
}
|
|
|
|
public void EnterBlock(BasicBlock block)
|
|
{
|
|
Assembler.MarkLabel(GetLabel(block));
|
|
|
|
CurrBlock = block;
|
|
}
|
|
|
|
public void JumpTo(BasicBlock target)
|
|
{
|
|
Assembler.Jmp(GetLabel(target));
|
|
}
|
|
|
|
public void JumpTo(X86Condition condition, BasicBlock target)
|
|
{
|
|
Assembler.Jcc(condition, GetLabel(target));
|
|
}
|
|
|
|
private Operand GetLabel(BasicBlock block)
|
|
{
|
|
ref Operand label = ref _blockLabels[block.Index];
|
|
|
|
if (label == default)
|
|
{
|
|
label = Operand.Factory.Label();
|
|
}
|
|
|
|
return label;
|
|
}
|
|
}
|
|
} |