Skip to content

Commit

Permalink
Feature: support toggling the rendering of pseudoinstructions
Browse files Browse the repository at this point in the history
Pseudo instructions like `addi x4,zero,2` are rendered as the more human-
friendly `li x4,2`. However, the user can force the instructions to be
rendered in their canonical format.
  • Loading branch information
uxmal committed Feb 9, 2024
1 parent d91b332 commit b4213db
Show file tree
Hide file tree
Showing 23 changed files with 871 additions and 681 deletions.
45 changes: 42 additions & 3 deletions src/Arch/RiscV/RiscVAssemblyRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,33 @@ public string FormatValue(Constant c, bool forceSignForSignedIntegers = false)

public void Render(RiscVInstruction instr, MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
{
if (!options.Flags.HasFlag(MachineInstructionRendererFlags.RenderInstructionsCanonically))
{
switch (instr.Mnemonic)
{
case Mnemonic.addi:
if (((RegisterStorage) instr.Operands[1]).Number == 0)
{
renderer.WriteMnemonic("li");
renderer.Tab();
RenderOperands(instr, options, renderer, instr.Operands[0], instr.Operands[2]);
return;
}
if (((ImmediateOperand) instr.Operands[2]).Value.IsZero)
{
renderer.WriteMnemonic("mv");
renderer.Tab();
RenderOperands(instr, options, renderer, instr.Operands[0], instr.Operands[1]);
return;
}
break;
}
}
DoRender(instr, renderer, options);
}

private void DoRender(RiscVInstruction instr, MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
{
RenderMnemonic(instr, renderer);

if (instr.Operands.Length > 0)
Expand Down Expand Up @@ -287,15 +314,27 @@ protected virtual void RenderMnemonic(RiscVInstruction instr, MachineInstruction
renderer.WriteMnemonic(sb.ToString());
}

protected virtual void RenderOperands(RiscVInstruction instr, MachineInstructionRendererOptions options, MachineInstructionRenderer renderer)
protected virtual void RenderOperands(
RiscVInstruction instr,
MachineInstructionRendererOptions options,
MachineInstructionRenderer renderer)
{
RenderOperands(instr, options, renderer, instr.Operands);
}

protected virtual void RenderOperands(
RiscVInstruction instr,
MachineInstructionRendererOptions options,
MachineInstructionRenderer renderer,
params MachineOperand[] operands)
{
var operandSeparator = "";
for (int i = 0; i < instr.Operands.Length; ++i)
for (int i = 0; i < operands.Length; ++i)
{
renderer.WriteString(operandSeparator);
operandSeparator = options.OperandSeparator ?? ",";

var operand = instr.Operands[i];
var operand = operands[i];
RenderOperand(instr, operand, renderer, options);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Core/Machine/MachineInstructionRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public enum MachineInstructionRendererFlags
None = 0,
ExplicitOperandSize = 1,
ResolvePcRelativeAddress = 2,
RenderInstructionsCanonically = 4, // Render pseudoinstruction as their 'base' instructions.
}


Expand Down
6 changes: 5 additions & 1 deletion src/Core/Output/Dumper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public Dumper(Program program)
this.program = program;
}

public bool RenderInstructionsCanonically { get; set; }
public bool ShowAddresses { get; set; }
public bool ShowCodeBytes { get; set; }

Expand Down Expand Up @@ -205,9 +206,12 @@ public void DumpAssembler(
var dasm = arch.CreateDisassembler(arch.CreateImageReader(mem, addrStart));
try
{
var flags = MachineInstructionRendererFlags.ResolvePcRelativeAddress;
if (this.RenderInstructionsCanonically)
flags |= MachineInstructionRendererFlags.RenderInstructionsCanonically;
var writer = new FormatterInstructionWriter(formatter, program.Procedures, true);
var options = new MachineInstructionRendererOptions(
flags: MachineInstructionRendererFlags.ResolvePcRelativeAddress,
flags: flags,
syntax: "");
foreach (var instr in dasm)
{
Expand Down
1 change: 1 addition & 0 deletions src/Core/Serialization/ProjectLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ public void LoadUserData(UserData_v4 sUser, Program program, UserData user, Imag
}
program.User.ShowAddressesInDisassembly = sUser.ShowAddressesInDisassembly;
program.User.ShowBytesInDisassembly = sUser.ShowBytesInDisassembly;
program.User.RenderInstructionsCanonically = sUser.RenderInstructionsCanonically;
program.User.ExtractResources = sUser.ExtractResources;
// Backwards compatibility: older versions used single file policy.
program.User.OutputFilePolicy = sUser.OutputFilePolicy ?? Program.SingleFilePolicy;
Expand Down
1 change: 1 addition & 0 deletions src/Core/Serialization/ProjectSaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public DecompilerInput_v5 VisitProgram(ImageLocation projectLocation, Program pr
Annotations = program.User.Annotations.Select(SerializeAnnotation).ToList(),
TextEncoding = program.User.TextEncoding != Encoding.ASCII ? program.User.TextEncoding?.WebName : null,
RegisterValues = SerializeRegisterValues(program.User.RegisterValues),
RenderInstructionsCanonically = program.User.RenderInstructionsCanonically,
ShowAddressesInDisassembly = program.User.ShowAddressesInDisassembly,
ShowBytesInDisassembly = program.User.ShowBytesInDisassembly,
Segments = program.User.Segments.Select(SerializeSegment).ToList(),
Expand Down
4 changes: 4 additions & 0 deletions src/Core/Serialization/Project_v4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ public UserData_v4()
[XmlArrayItem("assume")]
public RegisterValue_v2[]? RegisterValues;

[XmlElement("renderBaseInstrs")]
[DefaultValue(false)]
public bool RenderInstructionsCanonically;

[XmlElement("segment")]
public List<Segment_v4> Segments;

Expand Down
6 changes: 6 additions & 0 deletions src/Core/UserData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ public UserData()
/// </summary>
public SortedList<Address, List<UserRegisterValue>> RegisterValues { get; set; }

/// <summary>
/// If true, render instructions in their base format (no translation
/// to pseudo-mnemonics).
/// </summary>
public bool RenderInstructionsCanonically { get; set; }

/// <summary>
/// If set, display addresses in the written disassembly file.
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion src/Decompiler/Decompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ public void DumpAssembler(
Dumper dump = new Dumper(program)
{
ShowAddresses = program.User.ShowAddressesInDisassembly,
ShowCodeBytes = program.User.ShowBytesInDisassembly
ShowCodeBytes = program.User.ShowBytesInDisassembly,
RenderInstructionsCanonically = program.User.RenderInstructionsCanonically,
};
dump.Dump(segmentItems, wr);
}
Expand Down
9 changes: 9 additions & 0 deletions src/Drivers/CmdLine/CmdLineDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ private void Decompile(Dictionary<string, object> pArgs)
{
decompiler.Project.Programs[0].User.ShowAddressesInDisassembly = true;
}
if (pArgs.ContainsKey("dasm-base-instrs"))
{
decompiler.Project.Programs[0].User.RenderCanonicalInstructions = true;

Check failure on line 210 in src/Drivers/CmdLine/CmdLineDriver.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'UserData' does not contain a definition for 'RenderCanonicalInstructions' and no accessible extension method 'RenderCanonicalInstructions' accepting a first argument of type 'UserData' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 210 in src/Drivers/CmdLine/CmdLineDriver.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'UserData' does not contain a definition for 'RenderCanonicalInstructions' and no accessible extension method 'RenderCanonicalInstructions' accepting a first argument of type 'UserData' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 210 in src/Drivers/CmdLine/CmdLineDriver.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'UserData' does not contain a definition for 'RenderCanonicalInstructions' and no accessible extension method 'RenderCanonicalInstructions' accepting a first argument of type 'UserData' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 210 in src/Drivers/CmdLine/CmdLineDriver.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'UserData' does not contain a definition for 'RenderCanonicalInstructions' and no accessible extension method 'RenderCanonicalInstructions' accepting a first argument of type 'UserData' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 210 in src/Drivers/CmdLine/CmdLineDriver.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'UserData' does not contain a definition for 'RenderCanonicalInstructions' and no accessible extension method 'RenderCanonicalInstructions' accepting a first argument of type 'UserData' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 210 in src/Drivers/CmdLine/CmdLineDriver.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'UserData' does not contain a definition for 'RenderCanonicalInstructions' and no accessible extension method 'RenderCanonicalInstructions' accepting a first argument of type 'UserData' could be found (are you missing a using directive or an assembly reference?)
}
if (pArgs.ContainsKey("dasm-bytes"))
{
decompiler.Project.Programs[0].User.ShowBytesInDisassembly = true;
Expand Down Expand Up @@ -581,6 +585,10 @@ private Dictionary<string,object> ProcessArguments(TextWriter w, string[] args)
{
parsedArgs["dasm-bytes"] = true;
}
else if (args[i] == "--dasm-base-instrs")
{
parsedArgs["dasm-base-instrs"] = true;
}
else if (args[i] == "--scan-only")
{
//$TODO: deprecate this command
Expand Down Expand Up @@ -714,6 +722,7 @@ private void Usage(TextWriter w)
DumpEnvironments(config, w, " {0,-25} {1}");
w.WriteLine(" --base <address> Use <address> as the base address of the program.");
w.WriteLine(" --dasm-address Display addresses in disassembled machine code.");
w.WriteLine(" --dasm-base-instrs Don't display pseudo- or aliased instructions.");
w.WriteLine(" --dasm-bytes Display individual bytes in disassembled machine code.");
w.WriteLine(" --data <hex-bytes> Supply machine code as hex bytes");
w.WriteLine(" --default-to <format> If no executable format can be recognized, default");
Expand Down
3 changes: 2 additions & 1 deletion src/Gui/CmdIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ public enum CmdIds
ViewCfgCode,
ViewCfgGraph,
ViewPcRelative,
OpenInNewTab,
ViewInstructionsCanonically,
OpenInNewTab,
EditDeclaration,
EditComment,
EditLabel,
Expand Down
2 changes: 2 additions & 0 deletions src/Gui/CommandDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ public class CommandDefinitions
new CmdDefinition { id = CmdIds.ViewCfgCode, cmdSet = Reko, container = GroupIds.GrpCodeViewMode, priority = 1, text = "View _code" },
new CmdDefinition { id = CmdIds.ViewCfgGraph, cmdSet = Reko, container = GroupIds.GrpCodeViewMode, priority = 2, text = "View control _graph" },
new CmdDefinition { id = CmdIds.ViewPcRelative, cmdSet = Reko, container = GroupIds.GrpLowLevel, text = "Show _PC relative addresses" },
new CmdDefinition { id = CmdIds.ViewInstructionsCanonically, cmdSet = Reko, container = GroupIds.GrpLowLevel, text = "Display instructions _canonically" },
new CmdDefinition { id = CmdIds.OpenInNewTab, cmdSet = Reko, container = GroupIds.GrpCodeView, priority = 0, text = "Open in new tab" },
new CmdDefinition { id = CmdIds.EditDeclaration, cmdSet = Reko, container = GroupIds.GrpCodeView, priority = 1, text = "Edit declaration" },
new CmdDefinition { id = CmdIds.EditComment, cmdSet = Reko, container = GroupIds.GrpCodeView, priority = 2, text = "Edit comment" },
Expand Down Expand Up @@ -238,6 +239,7 @@ public class CommandDefinitions
new Placement { item=(int)CmdIds.OpenLink, container=GroupIds.GrpDisassemblerNav },
new Placement { item=(int)CmdIds.OpenLinkInNewWindow, container=GroupIds.GrpDisassemblerNav },
new Placement { item=(int)CmdIds.ViewPcRelative, container=GroupIds.GrpDisassemblerShow },
new Placement { item=(int)CmdIds.ViewInstructionsCanonically, container=GroupIds.GrpDisassemblerShow },
new Placement { item=(int)CmdIds.EditCopy, container=GroupIds.GrpDisassemblerEdit },
new Placement { item=(int)CmdIds.EditRename, container=GroupIds.GrpDisassemblerEdit },
new Placement { item=(int)CmdIds.EditRegisterValues, container=GroupIds.GrpDisassemblerEdit },
Expand Down
12 changes: 8 additions & 4 deletions src/Gui/TextViewing/AbstractDisassemblyTextModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public AbstractDisassemblyTextModel(Program program, IProcessorArchitecture arch
public object CurrentPosition => offset;
public object EndPosition => offsetEnd;
public int LineCount => GetPositionEstimate(offsetEnd - offsetStart);
public bool RenderInstructionsCanonically { get; set; }
public bool ShowPcRelative { get; set; }

public int ComparePositions(object a, object b)
Expand All @@ -94,10 +95,13 @@ public LineSpan[] GetLineSpans(int count)
seg.MemoryArea != null &&
seg.MemoryArea.IsValidAddress(addr))
{
var options = new MachineInstructionRendererOptions(
flags: ShowPcRelative
? MachineInstructionRendererFlags.None
: MachineInstructionRendererFlags.ResolvePcRelativeAddress);
var flags = MachineInstructionRendererFlags.None;
if (ShowPcRelative)
flags |= MachineInstructionRendererFlags.ResolvePcRelativeAddress;
if (RenderInstructionsCanonically)
flags |= MachineInstructionRendererFlags.RenderInstructionsCanonically;

var options = new MachineInstructionRendererOptions(flags: flags);
arch = GetArchitectureForAddress(addr);
var cellBitSize = seg.MemoryArea.CellBitSize;
var rdr = arch.CreateImageReader(seg.MemoryArea, addr);
Expand Down
1 change: 1 addition & 0 deletions src/Gui/decompiler-menus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
<cmd id="ViewCfgCode" cmdSet="Reko" container="GrpCodeViewMode" priority="1">View _code</cmd>
<cmd id="ViewCfgGraph" cmdSet="Reko" container="GrpCodeViewMode" priority="2">View control _graph</cmd>
<cmd id="ViewPcRelative" cmdSet="Reko" container="GrpLowLevel">Show _PC relative addresses</cmd>
<cmd id="ViewBaseInstructions" cmdSet="Reko" container="GrpLowLevel">Show _base instructions</cmd>
<cmd id="OpenInNewTab" cmdSet="Reko" container="GrpCodeView" priority="0">Open in new tab</cmd>
<cmd id="EditDeclaration" cmdSet="Reko" container="GrpCodeView" priority="1">Edit declaration</cmd>
<cmd id="EditComment" cmdSet="Reko" container="GrpCodeView" priority="2">Edit comment</cmd>
Expand Down
76 changes: 76 additions & 0 deletions src/UnitTests/Arch/RiscV/RiscVAssemblyRendererTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#region License
/*
* Copyright (C) 1999-2024 John Källén.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#endregion

using NUnit.Framework;
using Reko.Arch.RiscV;
using Reko.Core;
using Reko.Core.Machine;


namespace Reko.UnitTests.Arch.RiscV
{
[TestFixture]
public class RiscVAssemblyRendererTests
{
private readonly RegisterStorage zero = RegisterStorage.Reg32("zero", 0);
private readonly RegisterStorage x1 = RegisterStorage.Reg32("x1", 1);
private MachineInstructionRendererFlags flags;

[SetUp]
public void Setup()
{
flags = MachineInstructionRendererFlags.None;
}

private void AssertCode(string sExpected, RiscVInstruction instr)
{
var sr = new StringRenderer();
var options = new MachineInstructionRendererOptions(
flags: flags
);
instr.Render(sr, options);
var sActual = sr.ToString();
Assert.AreEqual(sExpected, sActual);
}

[Test]
public void RiscVAr_addi_aliased_to_li()
{
var instr = new RiscVInstruction
{
Mnemonic = Mnemonic.addi,
Operands = new MachineOperand[] { x1, zero, ImmediateOperand.Int32(-1)},
};
AssertCode("li\tx1,-0x1", instr);
}

[Test]
public void RiscVAr_addi_not_aliased_to_li()
{
var instr = new RiscVInstruction
{
Mnemonic = Mnemonic.addi,
Operands = new MachineOperand[] { x1, zero, ImmediateOperand.Int32(-1)},
};
flags |= MachineInstructionRendererFlags.RenderInstructionsCanonically;
AssertCode("addi\tx1,zero,-0x1", instr);
}
}
}
13 changes: 12 additions & 1 deletion src/UnitTests/Arch/RiscV/RiscVDisassemblerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#region License
/*
* Copyright (C) 1999-2024 John Källén.
Expand Down Expand Up @@ -140,6 +139,18 @@ public void RiscV_dasm_addi()
AssertCode("addi\tsp,sp,-0x1C0", 0b1110010000000001000000010_00100_11);
}

[Test]
public void RiscV_dasm_addi_li()
{
AssertCode("li\ta0,-0x1C0", 0b111001000000_00000_000_01010_00100_11);
}

[Test]
public void RiscV_dasm_addi_mv()
{
AssertCode("mv\ta0,s8", 0b000000000000_11000_000_01010_00100_11);
}

[Test]
public void RiscV_dasm_auipc()
{
Expand Down
4 changes: 0 additions & 4 deletions src/UnitTests/Core/Serialization/ProjectPersisterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@
using Reko.Core;
using Reko.Core.Serialization;
using Reko.Core.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;

namespace Reko.UnitTests.Core.Serialization
{
Expand Down
6 changes: 4 additions & 2 deletions src/UnitTests/Core/Serialization/ProjectSerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,9 @@ public void Ps_Load_v5()
Heuristics =
{
new Heuristic_v3 { Name = "shingle" }
}
}
},
RenderInstructionsCanonically = true,
},
},
new DecompilerInput_v5
{
Expand Down Expand Up @@ -344,6 +345,7 @@ public void Ps_Load_v5()

Assert.IsTrue(inputFile0.User.Heuristics.Contains("aggressive-branch-removal"));
Assert.IsTrue(inputFile0.User.Heuristics.Contains("shingle"));
Assert.IsTrue(inputFile0.User.RenderInstructionsCanonically);

Assert.AreEqual(OsPath.Absolute("tmp","foo","i am positive+.exe"), inputFile1.Location.FilesystemPath);
}
Expand Down
1 change: 1 addition & 0 deletions src/UnitTests/Core/Serialization/SerializedProjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ public void SudSaveProject()
{ jumpTable.Address, jumpTable }
},
OutputFilePolicy = Program.SegmentFilePolicy,
RenderInstructionsCanonically = true,
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/UserInterfaces/WindowsForms/Controls/DisassemblyControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ public IProcessorArchitecture Architecture
}
private IProcessorArchitecture arch;

public bool RenderInstructionsCanonically
{
get { return dasmModel != null && dasmModel.RenderInstructionsCanonically; }
set
{
if (dasmModel is null)
return;
dasmModel.RenderInstructionsCanonically = value;
Invalidate();
}
}

public bool ShowPcRelative
{
get { return dasmModel != null ? dasmModel.ShowPcRelative : false; }
Expand Down
Loading

0 comments on commit b4213db

Please sign in to comment.