Skip to content

Commit

Permalink
Feature: long constant fusion for Sparc disassembly.
Browse files Browse the repository at this point in the history
  • Loading branch information
uxmal committed Oct 15, 2024
1 parent d2755b9 commit cffdf0d
Show file tree
Hide file tree
Showing 9 changed files with 522 additions and 271 deletions.
157 changes: 157 additions & 0 deletions src/Arch/Sparc/LongConstantFuser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#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 or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful or
* 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 or write to
* the Free Software Foundation or 675 Mass Ave or Cambridge or MA 02139 or USA.
*/
#endregion

using Reko.Core;
using Reko.Core.Collections;
using Reko.Core.Expressions;
using Reko.Core.Machine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace Reko.Arch.Sparc
{
public class LongConstantFuser : IEnumerable<MachineInstruction>
{
private readonly IEnumerable<SparcInstruction> dasm;

public LongConstantFuser(IEnumerable<SparcInstruction> dasm)
{
this.dasm = dasm;
}

public IEnumerator<MachineInstruction> GetEnumerator()
{
ImmediateOperand immLo;
var e = new LookaheadEnumerator<SparcInstruction>(dasm);
while (e.MoveNext())
{
var instrHi = e.Current;
switch (instrHi.Mnemonic)
{
case Mnemonic.sethi:
if (((RegisterStorage) instrHi.Operands[1]).Number == 0 ||
!e.TryPeek(1, out var instrLo))
{
break;
}
if (instrLo.Mnemonic == Mnemonic.add)
{
if (instrHi.Operands[1] == instrLo.Operands[0])
{
// Mutate the sethi and add
var immHi = (ImmediateOperand) instrHi.Operands[0];
immLo = (ImmediateOperand) instrLo.Operands[1];
var longConst = new ImmediateOperand(
Constant.Create(
instrHi.Operands[0].Width,
(uint) ((immHi.Value.ToInt32() << 16) |
immLo.Value.ToInt32())));
var hiOp = new SliceOperand(SliceType.Hi, immHi, longConst);
var loOp = new SliceOperand(SliceType.Lo, immLo, longConst);
instrHi.Operands[0] = hiOp;
instrLo.Operands[1] = loOp;
}
}
else if (IsMemoryLoadInstruction(instrLo.Mnemonic))
{
if (instrLo.Operands[0] is MemoryOperand memOp &&
instrHi.Operands[1] == memOp.Base &&
memOp.Offset is ImmediateOperand imm)
{
var immHi = (ImmediateOperand) instrHi.Operands[1];
immLo = imm;
// Mutate the addis/oris and the memory operand
var longConst = new ImmediateOperand(
Constant.Create(
instrHi.Operands[0].Width,
(immHi.Value.ToInt32() << 16) +
immLo.Value.ToInt32()));
var hiOp = new SliceOperand(SliceType.Hi, immHi, longConst);
var loOp = new SliceOperand(SliceType.Lo, immLo, longConst);
instrHi.Operands[0] = hiOp;
memOp.Offset = loOp;
}
}
else if (IsMemoryStoreInstruction(instrLo.Mnemonic) &&
instrLo.Operands[1] is MemoryOperand memOp)
{
if (instrHi.Operands[1] == memOp.Base &&
memOp.Offset is ImmediateOperand imm)
{
var immHi = (ImmediateOperand) instrHi.Operands[1];
immLo = imm;
// Mutate the addis/oris and the memory operand
var longConst = new ImmediateOperand(
Constant.Create(
instrHi.Operands[0].Width,
(immHi.Value.ToInt32() << 16) +
immLo.Value.ToInt32()));
var hiOp = new SliceOperand(SliceType.Hi, immHi, longConst);
var loOp = new SliceOperand(SliceType.Lo, immLo, longConst);
instrHi.Operands[1] = hiOp;
memOp.Offset = loOp;
}
}
break;
default:
break;
}
yield return e.Current;
}
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

private static bool IsMemoryLoadInstruction(Mnemonic mnemonic)
{
return mnemonic switch
{
Mnemonic.ld or
Mnemonic.ldd or
Mnemonic.ldf or
Mnemonic.lddf or
Mnemonic.ldsb or
Mnemonic.ldub or
Mnemonic.ldsh or
Mnemonic.lduh or
Mnemonic.ldsw or
Mnemonic.lduw or
_ => false
};
}

private static bool IsMemoryStoreInstruction(Mnemonic mnemonic)
{
return mnemonic switch
{
Mnemonic.st or
Mnemonic.std or
Mnemonic.stb or
Mnemonic.sth or
Mnemonic.stf or
Mnemonic.stdf or
_ => false
};
}

}
}
31 changes: 22 additions & 9 deletions src/Arch/Sparc/MemoryOperand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,13 @@
using Reko.Core.Expressions;
using Reko.Core.Machine;
using Reko.Core.Types;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Reko.Arch.Sparc
{
public class MemoryOperand : AbstractMachineOperand
{
private MemoryOperand(RegisterStorage b, Constant? offset, RegisterStorage? index, PrimitiveType width) : base(width)
private MemoryOperand(RegisterStorage b, MachineOperand? offset, RegisterStorage? index, PrimitiveType width) : base(width)
{
this.Base = b;
this.Offset = offset;
Expand All @@ -41,7 +37,7 @@ private MemoryOperand(RegisterStorage b, Constant? offset, RegisterStorage? inde

public static MemoryOperand Indirect(RegisterStorage baseRg, Constant offset, PrimitiveType dt)
{
return new MemoryOperand(baseRg, offset, null, dt);
return new MemoryOperand(baseRg, new ImmediateOperand(offset), null, dt);
}

public static MemoryOperand Indexed(RegisterStorage baseReg, RegisterStorage index, PrimitiveType dt)
Expand All @@ -50,7 +46,7 @@ public static MemoryOperand Indexed(RegisterStorage baseReg, RegisterStorage ind
}

public RegisterStorage Base { get; }
public Constant? Offset { get; }
public MachineOperand? Offset { get; set; }
public RegisterStorage? Index { get; }


Expand All @@ -59,11 +55,12 @@ protected override void DoRender(MachineInstructionRenderer renderer, MachineIns
renderer.WriteFormat("[%{0}", Base.Name);
if (Offset is not null)
{
if (!Offset.IsNegative)
int offset = IntOffset();
if (offset >= 0)
{
renderer.WriteString("+");
}
renderer.WriteString(Offset.ToInt16().ToString());
renderer.WriteString(offset.ToString());
}
else
{
Expand All @@ -72,5 +69,21 @@ protected override void DoRender(MachineInstructionRenderer renderer, MachineIns
}
renderer.WriteString("]");
}

public int IntOffset()
{
if (Offset is null)
return 0;
ImmediateOperand offset;
if (Offset is SliceOperand slice)
{
offset = slice.Value;
}
else
{
offset = (ImmediateOperand) Offset;
}
return offset.Value.ToInt32();
}
}
}
76 changes: 76 additions & 0 deletions src/Arch/Sparc/SliceOperand.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 Reko.Core.Machine;
using System.Collections.Generic;

namespace Reko.Arch.Sparc;

/// <summary>
/// Used to represent sliced immediate values in
/// sethi instructions.
/// </summary>
public class SliceOperand : AbstractMachineOperand
{
public SliceOperand(
SliceType slice,
ImmediateOperand value,
MachineOperand inferredValue)
: base(value.Width)
{
this.Slice = slice;
this.Value = value;
this.InferredValue = inferredValue;
}

public SliceType Slice { get; }
public ImmediateOperand Value { get; }
public MachineOperand InferredValue { get; }

protected override void DoRender(MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
{
renderer.WriteString(Slice.Format());
renderer.WriteChar('(');
InferredValue.Render(renderer, options);
renderer.WriteChar(')');
}
}

public enum SliceType
{
None,

Hi,
Lo,
}

public static class SliceTypeExtensions
{
private static readonly Dictionary<SliceType, string> strFormats = new()
{
{ SliceType.Hi, "%hi" },
{ SliceType.Lo, "%lo" },
};

public static string Format(this SliceType type)
{
return strFormats[type];
}
}
4 changes: 3 additions & 1 deletion src/Arch/Sparc/SparcArchitecture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ public SparcArchitecture(IServiceProvider services, string archId, Registers reg

public override IEnumerable<MachineInstruction> CreateDisassembler(EndianImageReader imageReader)
{
return new SparcDisassembler(this, Decoder, imageReader);
var dasm = new SparcDisassembler(this, Decoder, imageReader);
return new LongConstantFuser(dasm);
}

public override IEqualityComparer<MachineInstruction> CreateInstructionComparer(Normalize norm)
{
return new SparcInstructionComparer(norm);
Expand Down
4 changes: 2 additions & 2 deletions src/Arch/Sparc/SparcInstructionComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private bool CompareOperands(MachineOperand a, MachineOperand b)
return false;
if (mA.Offset is not null)
{
return CompareValues(mA.Offset, mB.Offset);
return mA.IntOffset() == mB.IntOffset();
}
else
{
Expand Down Expand Up @@ -96,7 +96,7 @@ private int GetOperandHash(MachineOperand op)
return a.GetHashCode();
case MemoryOperand m:
var h = GetRegisterHash(m.Base);
h = h ^ 29 * GetConstantHash(m.Offset);
h = h ^ 29 * m.IntOffset();
h = h ^ 59 * GetRegisterHash(m.Index);
return h;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Arch/Sparc/SparcRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,9 @@ private Expression RewriteMemOp(MachineOperand op, DataType size)
var mem = (MemoryOperand) op;
if (mem.Offset is not null)
{
int nOffset = mem.IntOffset();
baseReg = mem.Base == arch.Registers.g0 ? null : binder.EnsureRegister(mem.Base);
offset = mem.Offset.IsIntegerZero ? null : mem.Offset;
offset = nOffset == 0 ? null : Constant.Int32(nOffset);
}
else
{
Expand Down
6 changes: 3 additions & 3 deletions src/UnitTests/Arch/Mips/MipsRewriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,11 @@ public void MipsRw_swl_swr()
{
AssertCode(0xABA8002B, // swl r8, 002B(sp)
"0|L--|00100000(4): 1 instructions",
"1|L--|Mem0[sp + 0x2B<32>:word32] = __swl(Mem0[sp + 0x2B<32>:word32], r8)");
"1|L--|Mem0[sp + 43<i32>:word32] = __swl(Mem0[sp + 43<i32>:word32], r8)");

AssertCode(0xBBA80028, // swr r8, 0028(sp)
"0|L--|00100000(4): 1 instructions",
"1|L--|Mem0[sp + 43<i32>:word32] = __swr(Mem0[sp + 43<i32>:word32], r8)");
"1|L--|Mem0[sp + 40<i32>:word32] = __swr(Mem0[sp + 40<i32>:word32], r8)");
}

[Test(Description = "Oddly, we see production code that writes to the r0 register. We musn't allow that assignment result in invalid code")]
Expand Down Expand Up @@ -633,7 +633,7 @@ public void MipsRw_sdc1()
Given_Mips64_Architecture();
AssertCode(0xf7a10018, // sdc1 $f1,24(sp)
"0|L--|00100000(4): 1 instructions",
"1|L--|Mem0[sp + 0x18<64>:word64] = f1");
"1|L--|Mem0[sp + 24<i64>:word64] = f1");
}

[Test]
Expand Down
Loading

0 comments on commit cffdf0d

Please sign in to comment.