Refactor PtcInfo
(#2625)
* Refactor `PtcInfo` This change reduces the coupling of `PtcInfo` by moving relocation tracking to the backend. `RelocEntry`s remains as `RelocEntry`s through out the pipeline until it actually needs to be written to the PTC streams. Keeping this representation makes inspecting and manipulating relocations after compilations less painful. This is something I needed to do to patch relocations to 0 to diff dumps. Contributes to #1125. * Turn `Symbol` & `RelocInfo` into readonly structs * Add documentation to `CompiledFunction` * Remove `Compiler.Compile<T>` Remove `Compiler.Compile<T>` and replace it by `Map<T>` of the `CompiledFunction` returned.
This commit is contained in:
parent
ac4ec1a015
commit
a9343c9364
19 changed files with 256 additions and 247 deletions
|
@ -1,6 +1,7 @@
|
|||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
|
@ -61,12 +62,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
private static InstructionInfo[] _instTable;
|
||||
private readonly static InstructionInfo[] _instTable;
|
||||
|
||||
private Stream _stream;
|
||||
private readonly Stream _stream;
|
||||
|
||||
private PtcInfo _ptcInfo;
|
||||
private bool _ptcDisabled;
|
||||
public List<RelocEntry> Relocs { get; }
|
||||
public bool HasRelocs => Relocs != null;
|
||||
|
||||
static Assembler()
|
||||
{
|
||||
|
@ -294,12 +295,10 @@ namespace ARMeilleure.CodeGen.X86
|
|||
_instTable[(int)inst] = info;
|
||||
}
|
||||
|
||||
public Assembler(Stream stream, PtcInfo ptcInfo = null)
|
||||
public Assembler(Stream stream, bool relocatable)
|
||||
{
|
||||
_stream = stream;
|
||||
|
||||
_ptcInfo = ptcInfo;
|
||||
_ptcDisabled = ptcInfo == null;
|
||||
Relocs = relocatable ? new List<RelocEntry>() : null;
|
||||
}
|
||||
|
||||
public void Add(Operand dest, Operand source, OperandType type)
|
||||
|
@ -498,7 +497,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
public void Jcc(X86Condition condition, long offset)
|
||||
{
|
||||
if (_ptcDisabled && ConstFitsOnS8(offset))
|
||||
if (!HasRelocs && ConstFitsOnS8(offset))
|
||||
{
|
||||
WriteByte((byte)(0x70 | (int)condition));
|
||||
|
||||
|
@ -519,7 +518,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
public void Jmp(long offset)
|
||||
{
|
||||
if (_ptcDisabled && ConstFitsOnS8(offset))
|
||||
if (!HasRelocs && ConstFitsOnS8(offset))
|
||||
{
|
||||
WriteByte(0xeb);
|
||||
|
||||
|
@ -980,9 +979,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
WriteByte((byte)(info.OpRImm64 + (dest.GetRegister().Index & 0b111)));
|
||||
|
||||
if (_ptcInfo != default && source.Relocatable)
|
||||
if (HasRelocs && source.Relocatable)
|
||||
{
|
||||
_ptcInfo.WriteRelocEntry(new RelocEntry((int)_stream.Position, source.Symbol));
|
||||
Relocs.Add(new RelocEntry((int)_stream.Position, source.Symbol));
|
||||
}
|
||||
|
||||
WriteUInt64(imm);
|
||||
|
@ -1396,9 +1395,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||
return ConstFitsOnS32(value);
|
||||
}
|
||||
|
||||
public static int GetJccLength(long offset, bool ptcDisabled = true)
|
||||
public static int GetJccLength(long offset, bool relocatable = false)
|
||||
{
|
||||
if (ptcDisabled && ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
|
||||
if (!relocatable && ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
@ -1412,9 +1411,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
public static int GetJmpLength(long offset, bool ptcDisabled = true)
|
||||
public static int GetJmpLength(long offset, bool relocatable = false)
|
||||
{
|
||||
if (ptcDisabled && ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
|
||||
if (!relocatable && ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
@ -13,10 +13,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
private const int ReservedBytesForJump = 1;
|
||||
|
||||
private Stream _stream;
|
||||
|
||||
private PtcInfo _ptcInfo;
|
||||
private bool _ptcDisabled;
|
||||
private readonly Stream _stream;
|
||||
private readonly bool _relocatable;
|
||||
|
||||
public int StreamOffset => (int)_stream.Length;
|
||||
|
||||
|
@ -27,22 +25,17 @@ namespace ARMeilleure.CodeGen.X86
|
|||
public BasicBlock CurrBlock { get; private set; }
|
||||
|
||||
public int CallArgsRegionSize { get; }
|
||||
public int XmmSaveRegionSize { get; }
|
||||
public int XmmSaveRegionSize { get; }
|
||||
|
||||
private long[] _blockOffsets;
|
||||
private readonly long[] _blockOffsets;
|
||||
|
||||
private struct Jump
|
||||
{
|
||||
public bool IsConditional { get; }
|
||||
|
||||
public X86Condition Condition { get; }
|
||||
|
||||
public BasicBlock Target { get; }
|
||||
|
||||
public long JumpPosition { get; }
|
||||
|
||||
public long RelativeOffset { get; set; }
|
||||
|
||||
public int InstSize { get; set; }
|
||||
|
||||
public Jump(BasicBlock target, long jumpPosition, int instSize = 0)
|
||||
|
@ -70,33 +63,26 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
private List<Jump> _jumps;
|
||||
private readonly List<Jump> _jumps;
|
||||
|
||||
private X86Condition _jNearCondition;
|
||||
|
||||
private long _jNearPosition;
|
||||
private int _jNearLength;
|
||||
private int _jNearLength;
|
||||
|
||||
public CodeGenContext(Stream stream, AllocationResult allocResult, int maxCallArgs, int blocksCount, PtcInfo ptcInfo = null)
|
||||
public CodeGenContext(Stream stream, AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||
{
|
||||
_stream = stream;
|
||||
|
||||
AllocResult = allocResult;
|
||||
|
||||
Assembler = new Assembler(stream, ptcInfo);
|
||||
|
||||
CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
|
||||
XmmSaveRegionSize = xmmSaveRegionSize;
|
||||
|
||||
_relocatable = relocatable;
|
||||
_blockOffsets = new long[blocksCount];
|
||||
|
||||
_jumps = new List<Jump>();
|
||||
|
||||
_ptcInfo = ptcInfo;
|
||||
_ptcDisabled = ptcInfo == null;
|
||||
AllocResult = allocResult;
|
||||
Assembler = new Assembler(stream, relocatable);
|
||||
CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
|
||||
XmmSaveRegionSize = xmmSaveRegionSize;
|
||||
}
|
||||
|
||||
private int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int 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).
|
||||
|
@ -144,7 +130,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
public void JumpTo(BasicBlock target)
|
||||
{
|
||||
if (_ptcDisabled)
|
||||
if (!_relocatable)
|
||||
{
|
||||
_jumps.Add(new Jump(target, _stream.Position));
|
||||
|
||||
|
@ -160,7 +146,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
public void JumpTo(X86Condition condition, BasicBlock target)
|
||||
{
|
||||
if (_ptcDisabled)
|
||||
if (!_relocatable)
|
||||
{
|
||||
_jumps.Add(new Jump(condition, target, _stream.Position));
|
||||
|
||||
|
@ -178,7 +164,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
_jNearCondition = condition;
|
||||
_jNearPosition = _stream.Position;
|
||||
_jNearLength = Assembler.GetJccLength(0, _ptcDisabled);
|
||||
_jNearLength = Assembler.GetJccLength(0, _relocatable);
|
||||
|
||||
_stream.Seek(_jNearLength, SeekOrigin.Current);
|
||||
}
|
||||
|
@ -191,7 +177,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
long offset = currentPosition - (_jNearPosition + _jNearLength);
|
||||
|
||||
Debug.Assert(_jNearLength == Assembler.GetJccLength(offset, _ptcDisabled), "Relative offset doesn't fit on near jump.");
|
||||
Debug.Assert(_jNearLength == Assembler.GetJccLength(offset, _relocatable), "Relative offset doesn't fit on near jump.");
|
||||
|
||||
Assembler.Jcc(_jNearCondition, offset);
|
||||
|
||||
|
@ -206,7 +192,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
public byte[] GetCode()
|
||||
public (byte[], RelocInfo) GetCode()
|
||||
{
|
||||
// Write jump relative offsets.
|
||||
bool modified;
|
||||
|
@ -223,7 +209,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
long offset = jumpTarget - jump.JumpPosition;
|
||||
|
||||
if (_ptcDisabled)
|
||||
if (!_relocatable)
|
||||
{
|
||||
if (offset < 0)
|
||||
{
|
||||
|
@ -300,7 +286,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
using (MemoryStream codeStream = new MemoryStream())
|
||||
{
|
||||
Assembler assembler = new Assembler(codeStream, _ptcInfo);
|
||||
Assembler assembler = new Assembler(codeStream, _relocatable);
|
||||
|
||||
for (int index = 0; index < _jumps.Count; index++)
|
||||
{
|
||||
|
@ -309,7 +295,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||
|
||||
_stream.Read(buffer);
|
||||
_stream.Seek(_ptcDisabled ? ReservedBytesForJump : jump.InstSize, SeekOrigin.Current);
|
||||
_stream.Seek(!_relocatable ? ReservedBytesForJump : jump.InstSize, SeekOrigin.Current);
|
||||
|
||||
codeStream.Write(buffer);
|
||||
|
||||
|
@ -325,7 +311,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
_stream.CopyTo(codeStream);
|
||||
|
||||
return codeStream.ToArray();
|
||||
var code = codeStream.ToArray();
|
||||
var relocInfo = Assembler.HasRelocs
|
||||
? new RelocInfo(Assembler.Relocs.ToArray())
|
||||
: RelocInfo.Empty;
|
||||
|
||||
return (code, relocInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.CodeGen.Optimizations;
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
|
@ -5,7 +6,6 @@ using ARMeilleure.Common;
|
|||
using ARMeilleure.Diagnostics;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
@ -91,7 +91,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
_instTable[(int)inst] = func;
|
||||
}
|
||||
|
||||
public static CompiledFunction Generate(CompilerContext cctx, PtcInfo ptcInfo = null)
|
||||
public static CompiledFunction Generate(CompilerContext cctx)
|
||||
{
|
||||
ControlFlowGraph cfg = cctx.Cfg;
|
||||
|
||||
|
@ -149,53 +149,47 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
Logger.StartPass(PassName.CodeGeneration);
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
bool relocatable = (cctx.Options & CompilerOptions.Relocatable) != 0;
|
||||
|
||||
using MemoryStream stream = new();
|
||||
|
||||
CodeGenContext context = new(stream, allocResult, maxCallArgs, cfg.Blocks.Count, relocatable);
|
||||
|
||||
UnwindInfo unwindInfo = WritePrologue(context);
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
CodeGenContext context = new CodeGenContext(stream, allocResult, maxCallArgs, cfg.Blocks.Count, ptcInfo);
|
||||
context.EnterBlock(block);
|
||||
|
||||
UnwindInfo unwindInfo = WritePrologue(context);
|
||||
|
||||
ptcInfo?.WriteUnwindInfo(unwindInfo);
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
|
||||
{
|
||||
context.EnterBlock(block);
|
||||
|
||||
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
|
||||
{
|
||||
GenerateOperation(context, node);
|
||||
}
|
||||
|
||||
if (block.SuccessorsCount == 0)
|
||||
{
|
||||
// The only blocks which can have 0 successors are exit blocks.
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
||||
last.Instruction == Instruction.Return);
|
||||
}
|
||||
else
|
||||
{
|
||||
BasicBlock succ = block.GetSuccessor(0);
|
||||
|
||||
if (succ != block.ListNext)
|
||||
{
|
||||
context.JumpTo(succ);
|
||||
}
|
||||
}
|
||||
GenerateOperation(context, node);
|
||||
}
|
||||
|
||||
byte[] code = context.GetCode();
|
||||
|
||||
if (ptcInfo != null)
|
||||
if (block.SuccessorsCount == 0)
|
||||
{
|
||||
ptcInfo.Code = code;
|
||||
// The only blocks which can have 0 successors are exit blocks.
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
||||
last.Instruction == Instruction.Return);
|
||||
}
|
||||
else
|
||||
{
|
||||
BasicBlock succ = block.GetSuccessor(0);
|
||||
|
||||
Logger.EndPass(PassName.CodeGeneration);
|
||||
|
||||
return new CompiledFunction(code, unwindInfo);
|
||||
if (succ != block.ListNext)
|
||||
{
|
||||
context.JumpTo(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(byte[] code, RelocInfo relocInfo) = context.GetCode();
|
||||
|
||||
Logger.EndPass(PassName.CodeGeneration);
|
||||
|
||||
return new CompiledFunction(code, unwindInfo, relocInfo);
|
||||
}
|
||||
|
||||
private static void GenerateOperation(CodeGenContext context, Operation operation)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue