diff options
| author | FICTURE7 <FICTURE7@gmail.com> | 2021-09-14 03:23:37 +0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-14 01:23:37 +0200 |
| commit | a9343c9364246d3288b4e7f20919ca1ad2e1fd3e (patch) | |
| tree | ba8ada2c2f5d2b4b0c92fbc5258bc43ad4809913 /ARMeilleure/CodeGen | |
| parent | ac4ec1a0151fd958d7ec58146169763b446836fe (diff) | |
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.
Diffstat (limited to 'ARMeilleure/CodeGen')
| -rw-r--r-- | ARMeilleure/CodeGen/CompiledFunction.cs | 43 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/Linking/RelocEntry.cs | 38 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/Linking/RelocInfo.cs | 32 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/Linking/Symbol.cs | 100 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/Linking/SymbolType.cs | 28 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/X86/Assembler.cs | 33 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/X86/CodeGenContext.cs | 63 | ||||
| -rw-r--r-- | ARMeilleure/CodeGen/X86/CodeGenerator.cs | 66 |
8 files changed, 312 insertions, 91 deletions
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 + /// <summary> + /// Represents a compiled function. + /// </summary> + readonly struct CompiledFunction { + /// <summary> + /// Gets the machine code of the <see cref="CompiledFunction"/>. + /// </summary> public byte[] Code { get; } + /// <summary> + /// Gets the <see cref="Unwinding.UnwindInfo"/> of the <see cref="CompiledFunction"/>. + /// </summary> public UnwindInfo UnwindInfo { get; } - public CompiledFunction(byte[] code, UnwindInfo unwindInfo) + /// <summary> + /// Gets the <see cref="Linking.RelocInfo"/> of the <see cref="CompiledFunction"/>. + /// </summary> + public RelocInfo RelocInfo { get; } + + /// <summary> + /// Initializes a new instance of the <see cref="CompiledFunction"/> struct with the specified machine code, + /// unwind info and relocation info. + /// </summary> + /// <param name="code">Machine code</param> + /// <param name="unwindInfo">Unwind info</param> + /// <param name="relocInfo">Relocation info</param> + internal CompiledFunction(byte[] code, UnwindInfo unwindInfo, RelocInfo relocInfo) { Code = code; UnwindInfo = unwindInfo; + RelocInfo = relocInfo; + } + + /// <summary> + /// Maps the <see cref="CompiledFunction"/> onto the <see cref="JitCache"/> and returns a delegate of type + /// <typeparamref name="T"/> pointing to the mapped function. + /// </summary> + /// <typeparam name="T">Type of delegate</typeparam> + /// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns> + public T Map<T>() + { + IntPtr codePtr = JitCache.Map(this); + + return Marshal.GetDelegateForFunctionPointer<T>(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 +{ + /// <summary> + /// Represents a relocation. + /// </summary> + readonly struct RelocEntry + { + public const int Stride = 13; // Bytes. + + /// <summary> + /// Gets the position of the relocation. + /// </summary> + public int Position { get; } + + /// <summary> + /// Gets the <see cref="Symbol"/> of the relocation. + /// </summary> + public Symbol Symbol { get; } + + /// <summary> + /// Initializes a new instance of the <see cref="RelocEntry"/> struct with the specified position and + /// <see cref="Symbol"/>. + /// </summary> + /// <param name="position">Position of relocation</param> + /// <param name="symbol">Symbol of relocation</param> + public RelocEntry(int position, Symbol symbol) + { + Position = position; + Symbol = symbol; + } + + /// <inheritdoc/> + 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 +{ + /// <summary> + /// Represents relocation information about a <see cref="CompiledFunction"/>. + /// </summary> + readonly struct RelocInfo + { + /// <summary> + /// Gets an empty <see cref="RelocInfo"/>. + /// </summary> + public static RelocInfo Empty { get; } = new RelocInfo(null); + + private readonly RelocEntry[] _entries; + + /// <summary> + /// Gets the set of <see cref="RelocEntry"/>. + /// </summary> + public ReadOnlySpan<RelocEntry> Entries => _entries ?? Array.Empty<RelocEntry>(); + + /// <summary> + /// Initializes a new instance of the <see cref="RelocInfo"/> struct with the specified set of + /// <see cref="RelocEntry"/>. + /// </summary> + /// <param name="entries">Set of <see cref="RelocInfo"/> to use</param> + public RelocInfo(RelocEntry[] entries) + { + _entries = entries ?? Array.Empty<RelocEntry>(); + } + } +}
\ 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 +{ + /// <summary> + /// Represents a symbol. + /// </summary> + readonly struct Symbol + { + private readonly ulong _value; + + /// <summary> + /// Gets the <see cref="SymbolType"/> of the <see cref="Symbol"/>. + /// </summary> + public SymbolType Type { get; } + + /// <summary> + /// Gets the value of the <see cref="Symbol"/>. + /// </summary> + /// <exception cref="InvalidOperationException"><see cref="Type"/> is <see cref="SymbolType.None"/></exception> + public ulong Value + { + get + { + if (Type == SymbolType.None) + { + ThrowSymbolNone(); + } + + return _value; + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="Symbol"/> structure with the specified <see cref="SymbolType"/> and value. + /// </summary> + /// <param name="type">Type of symbol</param> + /// <param name="value">Value of symbol</param> + public Symbol(SymbolType type, ulong value) + { + (Type, _value) = (type, value); + } + + /// <summary> + /// Determines if the specified <see cref="Symbol"/> instances are equal. + /// </summary> + /// <param name="a">First instance</param> + /// <param name="b">Second instance</param> + /// <returns><see langword="true"/> if equal; otherwise <see langword="false"/></returns> + public static bool operator ==(Symbol a, Symbol b) + { + return a.Equals(b); + } + + /// <summary> + /// Determines if the specified <see cref="Symbol"/> instances are not equal. + /// </summary> + /// <param name="a">First instance</param> + /// <param name="b">Second instance</param> + /// <returns><see langword="true"/> if not equal; otherwise <see langword="false"/></returns> + /// <inheritdoc/> + public static bool operator !=(Symbol a, Symbol b) + { + return !(a == b); + } + + /// <summary> + /// Determines if the specified <see cref="Symbol"/> is equal to this <see cref="Symbol"/> instance. + /// </summary> + /// <param name="other">Other <see cref="Symbol"/> instance</param> + /// <returns><see langword="true"/> if equal; otherwise <see langword="false"/></returns> + public bool Equals(Symbol other) + { + return other.Type == Type && other._value == _value; + } + + /// <inheritdoc/> + public override bool Equals(object obj) + { + return obj is Symbol sym && Equals(sym); + } + + /// <inheritdoc/> + public override int GetHashCode() + { + return HashCode.Combine(Type, _value); + } + + /// <inheritdoc/> + 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 +{ + /// <summary> + /// Types of <see cref="Symbol"/>. + /// </summary> + enum SymbolType : byte + { + /// <summary> + /// Refers to nothing, i.e no symbol. + /// </summary> + None, + + /// <summary> + /// Refers to an entry in <see cref="Translation.Delegates"/>. + /// </summary> + DelegateTable, + + /// <summary> + /// Refers to an entry in <see cref="Translation.Translator.FunctionTable"/>. + /// </summary> + FunctionTable, + + /// <summary> + /// Refers to a special symbol which is handled by <see cref="Translation.PTC.Ptc.PatchCode"/>. + /// </summary> + 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<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; } 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<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; + _relocatable = relocatable; + _blockOffsets = new long[blocksCount]; + _jumps = new List<Jump>(); 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<Jump>(); - - _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<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); } } } 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) |
