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:
FICTURE7 2021-09-14 03:23:37 +04:00 committed by GitHub
parent ac4ec1a015
commit a9343c9364
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 256 additions and 247 deletions

View file

@ -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;
}

View file

@ -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);
}
}
}

View file

@ -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)