Skip to content

Commit

Permalink
Feature: Platform.PreservedRegisters
Browse files Browse the repository at this point in the history
Added support for explictly stating which registers are _not_ modified after a procedure call.

Added support for extended C keywords for MacOS (may need refinement to match the actual keywords used in MPW C)
  • Loading branch information
uxmal committed Apr 24, 2023
1 parent 3c67e6c commit d838eba
Show file tree
Hide file tree
Showing 15 changed files with 92 additions and 14 deletions.
2 changes: 2 additions & 0 deletions src/Core/Configuration/PlatformArchitectureDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ public class PlatformArchitectureDefinition
public PlatformArchitectureDefinition()
{
this.TrashedRegisters = new List<string>();
this.PreservedRegisters = new List<string>();
this.TypeLibraries = new List<TypeLibraryDefinition>();
this.ProcedurePrologs = new List<MaskedPattern>();
}

public string? Name { get; set; }
public List<string> TrashedRegisters { get; set; }
public List<string> PreservedRegisters { get; set; }
public List<TypeLibraryDefinition> TypeLibraries { get; set; }
public List<MaskedPattern> ProcedurePrologs { get; set; }
}
Expand Down
5 changes: 5 additions & 0 deletions src/Core/Configuration/RekoConfigurationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,18 @@ private TypeLibraryDefinition LoadTypeLibraryReference(TypeLibraryReference_v1 t
private PlatformArchitectureDefinition LoadPlatformArchitecture(PlatformArchitecture_v1 spa)
{
var sTrashedRegs = spa.TrashedRegisters ?? "";
var sPreservedRegs = spa.PreservedRegisters ?? "";
return new PlatformArchitectureDefinition
{
Name = spa.Name,
TrashedRegisters = sTrashedRegs
.Split(',')
.Select(s => s.Trim())
.ToList(),
PreservedRegisters = sPreservedRegs
.Split(',')
.Select(s => s.Trim())
.ToList(),
TypeLibraries = LoadCollection(spa.TypeLibraries, LoadTypeLibraryReference),
ProcedurePrologs = LoadMaskedPatterns(spa.ProcedurePrologs)
};
Expand Down
3 changes: 3 additions & 0 deletions src/Core/Configuration/RekoConfiguration_v1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ public class PlatformArchitecture_v1
[XmlElement("TrashedRegisters")]
public string? TrashedRegisters;

[XmlElement("PreservedRegisters")]
public string? PreservedRegisters;

[XmlArray("TypeLibraries")]
[XmlArrayItem("TypeLibrary")]
public TypeLibraryReference_v1[]? TypeLibraries;
Expand Down
16 changes: 16 additions & 0 deletions src/Core/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public interface IPlatform
/// <returns>A set of registers</returns>
IReadOnlySet<RegisterStorage> TrashedRegisters { get; }

IReadOnlySet<RegisterStorage> PreservedRegisters { get; }

/// <summary>
/// The structure member alignment for this platform, measured in
/// storage units (e.g. bytes). Objects larger than or equal to this
Expand Down Expand Up @@ -356,6 +358,7 @@ protected Platform(IServiceProvider services, IProcessorArchitecture arch, strin
this.PlatformProcedures = new Dictionary<Address, ExternalProcedure>();
this.ProcedurePrologs = LoadProcedurePrologs();
this.TrashedRegisters = new HashSet<RegisterStorage>(LoadTrashedRegisters());
this.PreservedRegisters = new HashSet<RegisterStorage>(LoadPreservedRegisters());
}

public IProcessorArchitecture Architecture { get; }
Expand Down Expand Up @@ -390,6 +393,7 @@ protected Platform(IServiceProvider services, IProcessorArchitecture arch, strin
public abstract string DefaultCallingConvention { get; }

public virtual IReadOnlySet<RegisterStorage> TrashedRegisters { get; protected set; }
public virtual IReadOnlySet<RegisterStorage> PreservedRegisters { get; protected set; }


/// <summary>
Expand Down Expand Up @@ -619,6 +623,18 @@ protected RegisterStorage[] LoadTrashedRegisters()
.ToArray()!;
}

protected RegisterStorage[] LoadPreservedRegisters()
{
var cfgSvc = Services?.GetService<IConfigurationService>();
var pa = cfgSvc?.GetEnvironment(this.PlatformIdentifier)?.Architectures?.SingleOrDefault(a => a.Name == Architecture.Name);
if (pa is null)
return Array.Empty<RegisterStorage>();
return pa.PreservedRegisters
.Select(r => Architecture.GetRegister(r))
.Where(r => r is { })
.ToArray()!;
}

public virtual Address? MakeAddressFromConstant(Constant c, bool codeAlign)
{
return Architecture.MakeAddressFromConstant(c, codeAlign);
Expand Down
13 changes: 10 additions & 3 deletions src/Decompiler/Analysis/SccWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ public SsaTransform ConvertToSsa(Procedure proc)
private static bool IsStackStorageOfPreservedRegister(
SsaState ssa,
IReadOnlySet<RegisterStorage> trashedRegisters,
IReadOnlySet<RegisterStorage> preservedRegisters,
CallBinding use)
{
if (use.Storage is not StackStorage)
Expand All @@ -264,7 +265,7 @@ private static bool IsStackStorageOfPreservedRegister(
return false;
if (!(ssa.Identifiers[id].IsOriginal))
return false;
return IsPreservedRegister(trashedRegisters, id.Storage);
return IsPreservedRegister(trashedRegisters, preservedRegisters, id.Storage);
}

/// <summary>
Expand All @@ -274,14 +275,18 @@ private static bool IsStackStorageOfPreservedRegister(
/// to be modified by the ABI of the processor or platform.
/// </summary>
/// <param name="trashedRegisters"></param>
/// <param name="preservedRegisters"></param>
/// <param name="stg"></param>
/// <returns></returns>
private static bool IsPreservedRegister(
IReadOnlySet<RegisterStorage> trashedRegisters,
IReadOnlySet<RegisterStorage> preservedRegisters,
Storage stg)
{
if (stg is not RegisterStorage)
if (stg is not RegisterStorage reg)
return false;
if (preservedRegisters.Any(r => r.Covers(reg)))
return true;
if (trashedRegisters.Count == 0)
return false;
return !trashedRegisters.Where(r => r.OverlapsWith(stg)).Any();
Expand All @@ -307,13 +312,15 @@ private void RemoveImplicitRegistersFromHellNode(
return;
}
var trashedRegisters = program.Platform.TrashedRegisters;
var preservedRegisters = program.Platform.PreservedRegisters;
var platform = program.Platform;
foreach (var use in ci.Uses.ToList())
{
if (IsPreservedRegister(trashedRegisters, use.Storage) ||
if (IsPreservedRegister(trashedRegisters, preservedRegisters, use.Storage) ||
IsStackStorageOfPreservedRegister(
ssa,
trashedRegisters,
preservedRegisters,
use) ||
(use.Storage is RegisterStorage reg &&
platform.IsImplicitArgumentRegister(reg)))
Expand Down
11 changes: 11 additions & 0 deletions src/Decompiler/Analysis/SsaTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ public void AddUsesToExitBlock()
sid.Identifier.Storage is not MemoryStorage &&
sid.Identifier.Storage is not StackStorage &&
sid.Identifier.Storage is not TemporaryStorage &&
!IsPreservedByPlatform(sid.Identifier.Storage) &&
!existing.Contains(sid.Identifier))
.Select(sid => sid.OriginalIdentifier);
reachingIds = SeparateSequences(reachingIds);
Expand All @@ -295,6 +296,16 @@ sid.Identifier.Storage is not TemporaryStorage &&
});
}

//$TODO: make this a method of platform, as some
// platforms require register aliasing (EAX, AX, AL etc)
// and others don't.
private bool IsPreservedByPlatform(Storage storage)
{
return storage is RegisterStorage reg &&
program.Platform.PreservedRegisters.
Any(r => r.Covers(reg));
}

/// <summary>
/// Remove SCC's of phi assignments whose arguments are other variables in the SCC
/// or a single external value.
Expand Down
11 changes: 11 additions & 0 deletions src/Decompiler/Scanning/RtlProcedure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

using Reko.Core;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Reko.Scanning
{
Expand Down Expand Up @@ -66,5 +68,14 @@ public override string ToString()
{
return $"RtlProcedure(entry: {Address}, blocks: {Blocks.Count})";
}

[Conditional("DEBUG")]
public void Dump()
{
Debug.Print(" {0}",
string.Join(",\r\n ", this.Blocks
.OrderBy(b => b.Address.ToLinear())
.Select(b => b.Address)));
}
}
}
5 changes: 1 addition & 4 deletions src/Decompiler/Scanning/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1141,10 +1141,7 @@ public void ShingleScanProcedures()
private void Dump(RtlProcedure rtlProc)
{
Debug.Print("== {0} ===============", rtlProc.Address);
Debug.Print(" {0}",
string.Join(",\r\n ", rtlProc.Blocks
.OrderBy(b => b.Address.ToLinear())
.Select(b => b.Address)));
rtlProc.Dump();
}


Expand Down
5 changes: 5 additions & 0 deletions src/Drivers/reko.config
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@
<!--<TypeLibrary Name="macos_classic.xml" />-->
<TypeLibrary Loader="MpwPascal" Name="macos/MacOSClassicInterfaces.p" />
</TypeLibraries>
<Architectures>
<Architecture name="m68k">
<PreservedRegisters>a5</PreservedRegisters>
</Architecture>
</Architectures>
</Environment>

<Environment Name="macOsPpc"
Expand Down
17 changes: 15 additions & 2 deletions src/Environments/MacOS/Classic/MacOSClassic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Reko.Environments.MacOS.Classic
{
Expand All @@ -52,6 +53,16 @@ public override bool IsImplicitArgumentRegister(RegisterStorage reg)
return reg.Number == Registers.a7.Number || reg.Number == Registers.a5.Number;
}

public override CParser CreateCParser(TextReader rdr, ParserState? state)
{
//$TODO: consider making MacOSKeywords for MacOS-specific
// extensions to the C.
state ??= new ParserState();
var lexer = new CLexer(rdr, CLexer.MsvcKeywords);
var parser = new CParser(state, lexer);
return parser;
}

private HashSet<RegisterStorage> CreateTrashedRegisters()
{
return new HashSet<RegisterStorage>
Expand All @@ -66,7 +77,7 @@ public override CallingConvention GetCallingConvention(string? ccName)
switch (ccName)
{
case "pascal":
case "__pascal":
case "__pascal":
return new PascalCallingConvention((M68kArchitecture) this.Architecture);
default:
return new M68kCallingConvention((M68kArchitecture) this.Architecture);
Expand Down Expand Up @@ -123,7 +134,9 @@ public override void InjectProcedureEntryStatements(Procedure proc, Address addr
m.MStore(proc.Frame.FramePointer, proc.Frame.Continuation);
var ptrA5World = EnsureA5Pointer();
var a5 = proc.Frame.EnsureRegister(Registers.a5);
m.Assign(a5, ptrA5World);
//m.Assign(a5, this.A5World.Address);
m.Assign(a5, m.Word32((uint)(this.A5World.Address.Offset + this.A5Offset)));
//m.Assign(a5, ptrA5World);
}

private Identifier EnsureA5Pointer()
Expand Down
2 changes: 2 additions & 0 deletions src/ImageLoaders/Llvm/LLVMPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public LLVMPlatform(IServiceProvider services)
this.Name = "LLVM";
this.StructureMemberAlignment = 4; //$BUG: is arch-specific.
this.TrashedRegisters = new HashSet<RegisterStorage>();
this.PreservedRegisters = new HashSet<RegisterStorage>();
}
#nullable enable

Expand Down Expand Up @@ -72,6 +73,7 @@ public LLVMPlatform(IServiceProvider services)
public int StructureMemberAlignment { get; set; }

public IReadOnlySet<RegisterStorage> TrashedRegisters { get; }
public IReadOnlySet<RegisterStorage> PreservedRegisters { get; }


public Address AdjustProcedureAddress(Address addrCode)
Expand Down
2 changes: 2 additions & 0 deletions src/UnitTests/Decompiler/Analysis/AnalysisTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ protected void Given_FakeWin32Platform()
.Returns((ulong ul, bool b) => Address.Ptr32((uint) ul));
platformMock.Setup(p => p.TrashedRegisters)
.Returns(new HashSet<RegisterStorage>());
platformMock.Setup(p => p.PreservedRegisters)
.Returns(new HashSet<RegisterStorage>());
Given_Platform(platformMock.Object);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ public void Dfa2_UserDefinedStackArgs()
var lex = new CLexer(r, CLexer.MsvcKeywords);
return new CParser(s ?? new ParserState(), lex);
}));
platform.Setup(p => p.PreservedRegisters).Returns(new HashSet<RegisterStorage>());

var dynamicLinker = new Mock<IDynamicLinker>().Object;
program.Platform = platform.Object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ private Procedure RunTest(string sExp, IProcessorArchitecture arch, Func<Procedu
var dynamicLinker = new Mock<IDynamicLinker>();
var platform = new Mock<IPlatform>();
platform.Setup(p => p.TrashedRegisters).Returns(new HashSet<RegisterStorage>());
platform.Setup(p => p.PreservedRegisters).Returns(new HashSet<RegisterStorage>());
platform.Setup(p => p.PointerType).Returns(arch.PointerType);
progBuilder.Program.Platform = platform.Object;
progBuilder.Program.SegmentMap = segmentMap;
Expand Down
12 changes: 7 additions & 5 deletions subjects/regression.log

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d838eba

Please sign in to comment.