From a9343c9364246d3288b4e7f20919ca1ad2e1fd3e Mon Sep 17 00:00:00 2001 From: FICTURE7 Date: Tue, 14 Sep 2021 03:23:37 +0400 Subject: 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` Remove `Compiler.Compile` and replace it by `Map` of the `CompiledFunction` returned. --- ARMeilleure/CodeGen/CompiledFunction.cs | 43 ++++++++++++- ARMeilleure/CodeGen/Linking/RelocEntry.cs | 38 ++++++++++++ ARMeilleure/CodeGen/Linking/RelocInfo.cs | 32 ++++++++++ ARMeilleure/CodeGen/Linking/Symbol.cs | 100 ++++++++++++++++++++++++++++++ ARMeilleure/CodeGen/Linking/SymbolType.cs | 28 +++++++++ ARMeilleure/CodeGen/X86/Assembler.cs | 33 +++++----- ARMeilleure/CodeGen/X86/CodeGenContext.cs | 63 ++++++++----------- ARMeilleure/CodeGen/X86/CodeGenerator.cs | 66 +++++++++----------- 8 files changed, 312 insertions(+), 91 deletions(-) create mode 100644 ARMeilleure/CodeGen/Linking/RelocEntry.cs create mode 100644 ARMeilleure/CodeGen/Linking/RelocInfo.cs create mode 100644 ARMeilleure/CodeGen/Linking/Symbol.cs create mode 100644 ARMeilleure/CodeGen/Linking/SymbolType.cs (limited to 'ARMeilleure/CodeGen') diff --git a/ARMeilleure/CodeGen/CompiledFunction.cs b/ARMeilleure/CodeGen/CompiledFunction.cs index 61e89c24..ab5e88eb 100644 --- a/ARMeilleure/CodeGen/CompiledFunction.cs +++ b/ARMeilleure/CodeGen/CompiledFunction.cs @@ -1,17 +1,56 @@ +using ARMeilleure.CodeGen.Linking; using ARMeilleure.CodeGen.Unwinding; +using ARMeilleure.Translation.Cache; +using System; +using System.Runtime.InteropServices; namespace ARMeilleure.CodeGen { - struct CompiledFunction + /// + /// Represents a compiled function. + /// + readonly struct CompiledFunction { + /// + /// Gets the machine code of the . + /// public byte[] Code { get; } + /// + /// Gets the of the . + /// public UnwindInfo UnwindInfo { get; } - public CompiledFunction(byte[] code, UnwindInfo unwindInfo) + /// + /// Gets the of the . + /// + public RelocInfo RelocInfo { get; } + + /// + /// Initializes a new instance of the struct with the specified machine code, + /// unwind info and relocation info. + /// + /// Machine code + /// Unwind info + /// Relocation info + internal CompiledFunction(byte[] code, UnwindInfo unwindInfo, RelocInfo relocInfo) { Code = code; UnwindInfo = unwindInfo; + RelocInfo = relocInfo; + } + + /// + /// Maps the onto the and returns a delegate of type + /// pointing to the mapped function. + /// + /// Type of delegate + /// A delegate of type pointing to the mapped function + public T Map() + { + IntPtr codePtr = JitCache.Map(this); + + return Marshal.GetDelegateForFunctionPointer(codePtr); } } } \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Linking/RelocEntry.cs b/ARMeilleure/CodeGen/Linking/RelocEntry.cs new file mode 100644 index 00000000..a27bfded --- /dev/null +++ b/ARMeilleure/CodeGen/Linking/RelocEntry.cs @@ -0,0 +1,38 @@ +namespace ARMeilleure.CodeGen.Linking +{ + /// + /// Represents a relocation. + /// + readonly struct RelocEntry + { + public const int Stride = 13; // Bytes. + + /// + /// Gets the position of the relocation. + /// + public int Position { get; } + + /// + /// Gets the of the relocation. + /// + public Symbol Symbol { get; } + + /// + /// Initializes a new instance of the struct with the specified position and + /// . + /// + /// Position of relocation + /// Symbol of relocation + public RelocEntry(int position, Symbol symbol) + { + Position = position; + Symbol = symbol; + } + + /// + public override string ToString() + { + return $"({nameof(Position)} = {Position}, {nameof(Symbol)} = {Symbol})"; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Linking/RelocInfo.cs b/ARMeilleure/CodeGen/Linking/RelocInfo.cs new file mode 100644 index 00000000..922b8bfe --- /dev/null +++ b/ARMeilleure/CodeGen/Linking/RelocInfo.cs @@ -0,0 +1,32 @@ +using System; + +namespace ARMeilleure.CodeGen.Linking +{ + /// + /// Represents relocation information about a . + /// + readonly struct RelocInfo + { + /// + /// Gets an empty . + /// + public static RelocInfo Empty { get; } = new RelocInfo(null); + + private readonly RelocEntry[] _entries; + + /// + /// Gets the set of . + /// + public ReadOnlySpan Entries => _entries ?? Array.Empty(); + + /// + /// Initializes a new instance of the struct with the specified set of + /// . + /// + /// Set of to use + public RelocInfo(RelocEntry[] entries) + { + _entries = entries ?? Array.Empty(); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Linking/Symbol.cs b/ARMeilleure/CodeGen/Linking/Symbol.cs new file mode 100644 index 00000000..fa47ee23 --- /dev/null +++ b/ARMeilleure/CodeGen/Linking/Symbol.cs @@ -0,0 +1,100 @@ +using System; + +namespace ARMeilleure.CodeGen.Linking +{ + /// + /// Represents a symbol. + /// + readonly struct Symbol + { + private readonly ulong _value; + + /// + /// Gets the of the . + /// + public SymbolType Type { get; } + + /// + /// Gets the value of the . + /// + /// is + public ulong Value + { + get + { + if (Type == SymbolType.None) + { + ThrowSymbolNone(); + } + + return _value; + } + } + + /// + /// Initializes a new instance of the structure with the specified and value. + /// + /// Type of symbol + /// Value of symbol + public Symbol(SymbolType type, ulong value) + { + (Type, _value) = (type, value); + } + + /// + /// Determines if the specified instances are equal. + /// + /// First instance + /// Second instance + /// if equal; otherwise + public static bool operator ==(Symbol a, Symbol b) + { + return a.Equals(b); + } + + /// + /// Determines if the specified instances are not equal. + /// + /// First instance + /// Second instance + /// if not equal; otherwise + /// + public static bool operator !=(Symbol a, Symbol b) + { + return !(a == b); + } + + /// + /// Determines if the specified is equal to this instance. + /// + /// Other instance + /// if equal; otherwise + public bool Equals(Symbol other) + { + return other.Type == Type && other._value == _value; + } + + /// + public override bool Equals(object obj) + { + return obj is Symbol sym && Equals(sym); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Type, _value); + } + + /// + public override string ToString() + { + return $"{Type}:{_value}"; + } + + private static void ThrowSymbolNone() + { + throw new InvalidOperationException("Symbol refers to nothing."); + } + } +} diff --git a/ARMeilleure/CodeGen/Linking/SymbolType.cs b/ARMeilleure/CodeGen/Linking/SymbolType.cs new file mode 100644 index 00000000..b05b6969 --- /dev/null +++ b/ARMeilleure/CodeGen/Linking/SymbolType.cs @@ -0,0 +1,28 @@ +namespace ARMeilleure.CodeGen.Linking +{ + /// + /// Types of . + /// + enum SymbolType : byte + { + /// + /// Refers to nothing, i.e no symbol. + /// + None, + + /// + /// Refers to an entry in . + /// + DelegateTable, + + /// + /// Refers to an entry in . + /// + FunctionTable, + + /// + /// Refers to a special symbol which is handled by . + /// + Special + } +} diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs index 044f6047..95608756 100644 --- a/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/ARMeilleure/CodeGen/X86/Assembler.cs @@ -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 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() : 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; } diff --git a/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/ARMeilleure/CodeGen/X86/CodeGenContext.cs index fa726f2f..7e96dd85 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenContext.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenContext.cs @@ -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 _jumps; + private readonly List _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; + _relocatable = relocatable; + _blockOffsets = new long[blocksCount]; + _jumps = new List(); AllocResult = allocResult; - - Assembler = new Assembler(stream, ptcInfo); - + Assembler = new Assembler(stream, relocatable); CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize); XmmSaveRegionSize = xmmSaveRegionSize; - - _blockOffsets = new long[blocksCount]; - - _jumps = new List(); - - _ptcInfo = ptcInfo; - _ptcDisabled = ptcInfo == null; } - 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 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); } } } diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index 5818eb2e..924c113c 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -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()) - { - CodeGenContext context = new CodeGenContext(stream, allocResult, maxCallArgs, cfg.Blocks.Count, ptcInfo); + 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); + UnwindInfo unwindInfo = WritePrologue(context); - ptcInfo?.WriteUnwindInfo(unwindInfo); + for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) + { + context.EnterBlock(block); - 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); + GenerateOperation(context, node); + } - 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; - 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); - Debug.Assert(last.Instruction == Instruction.Tailcall || - last.Instruction == Instruction.Return); - } - else + if (succ != block.ListNext) { - BasicBlock succ = block.GetSuccessor(0); - - if (succ != block.ListNext) - { - context.JumpTo(succ); - } + context.JumpTo(succ); } } + } - byte[] code = context.GetCode(); + (byte[] code, RelocInfo relocInfo) = context.GetCode(); - if (ptcInfo != null) - { - ptcInfo.Code = code; - } + Logger.EndPass(PassName.CodeGeneration); - Logger.EndPass(PassName.CodeGeneration); - - return new CompiledFunction(code, unwindInfo); - } + return new CompiledFunction(code, unwindInfo, relocInfo); } private static void GenerateOperation(CodeGenContext context, Operation operation) -- cgit v1.2.3