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,3 +1,4 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.Diagnostics;

View file

@ -2,35 +2,16 @@ using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.X86;
using ARMeilleure.Diagnostics;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
static class Compiler
{
public static T Compile<T>(
ControlFlowGraph cfg,
OperandType[] argTypes,
OperandType retType,
CompilerOptions options,
PtcInfo ptcInfo = null)
{
CompiledFunction func = Compile(cfg, argTypes, retType, options, ptcInfo);
IntPtr codePtr = JitCache.Map(func);
return Marshal.GetDelegateForFunctionPointer<T>(codePtr);
}
public static CompiledFunction Compile(
ControlFlowGraph cfg,
OperandType[] argTypes,
OperandType retType,
CompilerOptions options,
PtcInfo ptcInfo = null)
CompilerOptions options)
{
Logger.StartPass(PassName.Dominance);
@ -57,7 +38,7 @@ namespace ARMeilleure.Translation
CompilerContext cctx = new(cfg, argTypes, retType, options);
return CodeGenerator.Generate(cctx, ptcInfo);
return CodeGenerator.Generate(cctx);
}
}
}

View file

@ -5,10 +5,11 @@ namespace ARMeilleure.Translation
[Flags]
enum CompilerOptions
{
None = 0,
SsaForm = 1 << 0,
Optimize = 1 << 1,
Lsra = 1 << 2,
None = 0,
SsaForm = 1 << 0,
Optimize = 1 << 1,
Lsra = 1 << 2,
Relocatable = 1 << 3,
MediumCq = SsaForm | Optimize,
HighCq = SsaForm | Optimize | Lsra

View file

@ -1,9 +1,9 @@
using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.CodeGen.X86;
using ARMeilleure.Common;
using ARMeilleure.Memory;
using ARMeilleure.Translation.Cache;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
@ -727,15 +727,10 @@ namespace ARMeilleure.Translation.PTC
UnwindInfo unwindInfo,
bool highCq)
{
CompiledFunction cFunc = new CompiledFunction(code, unwindInfo);
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
var gFunc = cFunc.Map<GuestFunction>();
IntPtr codePtr = JitCache.Map(cFunc);
GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer<GuestFunction>(codePtr);
TranslatedFunction tFunc = new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
return tFunc;
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
}
private static void UpdateInfo(InfoEntry infoEntry)
@ -889,10 +884,14 @@ namespace ARMeilleure.Translation.PTC
return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
}
internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, Hash128 hash, bool highCq, PtcInfo ptcInfo)
internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
{
lock (_lock)
{
byte[] code = compiledFunc.Code;
RelocInfo relocInfo = compiledFunc.RelocInfo;
UnwindInfo unwindInfo = compiledFunc.UnwindInfo;
InfoEntry infoEntry = new InfoEntry();
infoEntry.Address = address;
@ -900,18 +899,37 @@ namespace ARMeilleure.Translation.PTC
infoEntry.Hash = hash;
infoEntry.HighCq = highCq;
infoEntry.Stubbed = false;
infoEntry.CodeLength = ptcInfo.Code.Length;
infoEntry.RelocEntriesCount = ptcInfo.RelocEntriesCount;
infoEntry.CodeLength = code.Length;
infoEntry.RelocEntriesCount = relocInfo.Entries.Length;
SerializeStructure(_infosStream, infoEntry);
WriteCode(ptcInfo.Code.AsSpan());
WriteCode(code.AsSpan());
// WriteReloc.
ptcInfo.RelocStream.WriteTo(_relocsStream);
using var relocInfoWriter = new BinaryWriter(_relocsStream, EncodingCache.UTF8NoBOM, true);
foreach (RelocEntry entry in relocInfo.Entries)
{
relocInfoWriter.Write(entry.Position);
relocInfoWriter.Write((byte)entry.Symbol.Type);
relocInfoWriter.Write(entry.Symbol.Value);
}
// WriteUnwindInfo.
ptcInfo.UnwindInfoStream.WriteTo(_unwindInfosStream);
using var unwindInfoWriter = new BinaryWriter(_unwindInfosStream, EncodingCache.UTF8NoBOM, true);
unwindInfoWriter.Write(unwindInfo.PushEntries.Length);
foreach (UnwindPushEntry unwindPushEntry in unwindInfo.PushEntries)
{
unwindInfoWriter.Write((int)unwindPushEntry.PseudoOp);
unwindInfoWriter.Write(unwindPushEntry.PrologOffset);
unwindInfoWriter.Write(unwindPushEntry.RegIndex);
unwindInfoWriter.Write(unwindPushEntry.StackOffsetOrAllocSize);
}
unwindInfoWriter.Write(unwindInfo.PrologSize);
}
}

View file

@ -1,63 +0,0 @@
using ARMeilleure.CodeGen.Unwinding;
using System;
using System.IO;
namespace ARMeilleure.Translation.PTC
{
class PtcInfo : IDisposable
{
private readonly BinaryWriter _relocWriter;
private readonly BinaryWriter _unwindInfoWriter;
public byte[] Code { get; set; }
public MemoryStream RelocStream { get; }
public MemoryStream UnwindInfoStream { get; }
public int RelocEntriesCount { get; private set; }
public PtcInfo()
{
RelocStream = new MemoryStream();
UnwindInfoStream = new MemoryStream();
_relocWriter = new BinaryWriter(RelocStream, EncodingCache.UTF8NoBOM, true);
_unwindInfoWriter = new BinaryWriter(UnwindInfoStream, EncodingCache.UTF8NoBOM, true);
RelocEntriesCount = 0;
}
public void WriteRelocEntry(RelocEntry relocEntry)
{
_relocWriter.Write((int)relocEntry.Position);
_relocWriter.Write((byte)relocEntry.Symbol.Type);
_relocWriter.Write((ulong)relocEntry.Symbol.Value);
RelocEntriesCount++;
}
public void WriteUnwindInfo(UnwindInfo unwindInfo)
{
_unwindInfoWriter.Write((int)unwindInfo.PushEntries.Length);
foreach (UnwindPushEntry unwindPushEntry in unwindInfo.PushEntries)
{
_unwindInfoWriter.Write((int)unwindPushEntry.PseudoOp);
_unwindInfoWriter.Write((int)unwindPushEntry.PrologOffset);
_unwindInfoWriter.Write((int)unwindPushEntry.RegIndex);
_unwindInfoWriter.Write((int)unwindPushEntry.StackOffsetOrAllocSize);
}
_unwindInfoWriter.Write((int)unwindInfo.PrologSize);
}
public void Dispose()
{
_relocWriter.Dispose();
_unwindInfoWriter.Dispose();
RelocStream.Dispose();
UnwindInfoStream.Dispose();
}
}
}

View file

@ -1,21 +0,0 @@
namespace ARMeilleure.Translation.PTC
{
struct RelocEntry
{
public const int Stride = 13; // Bytes.
public int Position;
public Symbol Symbol;
public RelocEntry(int position, Symbol symbol)
{
Position = position;
Symbol = symbol;
}
public override string ToString()
{
return $"({nameof(Position)} = {Position}, {nameof(Symbol)} = {Symbol})";
}
}
}

View file

@ -1,100 +0,0 @@
using System;
namespace ARMeilleure.Translation.PTC
{
/// <summary>
/// Represents a symbol.
/// </summary>
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.");
}
}
}

View file

@ -1,28 +0,0 @@
namespace ARMeilleure.Translation.PTC
{
/// <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="Delegates"/>.
/// </summary>
DelegateTable,
/// <summary>
/// Refers to an entry in <see cref="Translator.FunctionTable"/>.
/// </summary>
FunctionTable,
/// <summary>
/// Refers to a special symbol which is handled by <see cref="Ptc.PatchCode"/>.
/// </summary>
Special
}
}

View file

@ -1,3 +1,4 @@
using ARMeilleure.CodeGen;
using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.Diagnostics;
@ -279,32 +280,30 @@ namespace ARMeilleure.Translation
Logger.EndPass(PassName.RegisterUsage);
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
var retType = OperandType.I64;
var argTypes = new OperandType[] { OperandType.I64 };
CompilerOptions options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
GuestFunction func;
if (!context.HasPtc)
if (context.HasPtc)
{
func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options);
options |= CompilerOptions.Relocatable;
}
else
CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options);
if (context.HasPtc)
{
using PtcInfo ptcInfo = new PtcInfo();
func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options, ptcInfo);
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, hash, highCq, ptcInfo);
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
}
var result = new TranslatedFunction(func, counter, funcSize, highCq);
GuestFunction func = compiledFunc.Map<GuestFunction>();
Allocators.ResetAll();
return result;
return new TranslatedFunction(func, counter, funcSize, highCq);
}
private struct Range

View file

@ -178,7 +178,7 @@ namespace ARMeilleure.Translation
var retType = OperandType.I64;
var argTypes = new[] { OperandType.I64 };
var func = Compiler.Compile<GuestFunction>(cfg, argTypes, retType, CompilerOptions.HighCq);
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
return Marshal.GetFunctionPointerForDelegate(func);
}
@ -204,7 +204,7 @@ namespace ARMeilleure.Translation
var retType = OperandType.I64;
var argTypes = new[] { OperandType.I64 };
var func = Compiler.Compile<GuestFunction>(cfg, argTypes, retType, CompilerOptions.HighCq);
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
return Marshal.GetFunctionPointerForDelegate(func);
}
@ -242,7 +242,7 @@ namespace ARMeilleure.Translation
var retType = OperandType.None;
var argTypes = new[] { OperandType.I64, OperandType.I64 };
return Compiler.Compile<DispatcherFunction>(cfg, argTypes, retType, CompilerOptions.HighCq);
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<DispatcherFunction>();
}
}
}