Skip to content

Commit

Permalink
WIP: Risc-V pcrel operands.
Browse files Browse the repository at this point in the history
  • Loading branch information
uxmal committed Oct 15, 2024
1 parent e4e973f commit a79b964
Show file tree
Hide file tree
Showing 15 changed files with 544 additions and 210 deletions.
167 changes: 167 additions & 0 deletions src/Arch/RiscV/LongConstantFuser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#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.Collections;
using Reko.Core.Machine;
using System;
using System.Collections;
using System.Collections.Generic;

namespace Reko.Arch.RiscV
{
public class LongConstantFuser : IEnumerable<MachineInstruction>
{
private RiscVDisassembler dasm;

public LongConstantFuser(RiscVDisassembler dasm)
{
this.dasm = dasm;
}

public IEnumerator<MachineInstruction> GetEnumerator()
{
ImmediateOperand immLo;
var e = new LookaheadEnumerator<RiscVInstruction>(dasm);
while (e.MoveNext())
{
var instrHi = e.Current;
switch (instrHi.Mnemonic)
{
case Mnemonic.auipc:
if (!e.TryPeek(1, out var instrLo))
{
yield return e.Current;
break;
}
if (instrLo.Mnemonic == Mnemonic.addi)
{
if (instrHi.Operands[0] == instrLo.Operands[0] &&
instrLo.Operands[0] == instrLo.Operands[1])
{
// Mutate the auipc
var immHi = (ImmediateOperand) instrHi.Operands[1];
immLo = (ImmediateOperand) instrLo.Operands[2];
var fullAddr = instrHi.Address +
((immHi.Value.ToInt32() << 12) +
immLo.Value.ToInt32());
var hiOp = new SliceOperand(SliceType.PcRelHi, immHi, fullAddr);
var loOp = new SliceOperand(SliceType.PcRelLo, immLo, fullAddr);
instrHi.Operands[1] = hiOp;
instrLo.Operands[2] = loOp;
}
}
else if (IsMemoryInstruction(instrLo.Mnemonic))
{
var memOp = (MemoryOperand) instrLo.Operands[1];
if (instrLo.Operands[0] == memOp.Base &&
memOp.Offset is ImmediateOperand imm)
{
var immHi = (ImmediateOperand) instrHi.Operands[1];
immLo = imm;
// Mutate the auipc and the memory operand
var fullAddr = instrHi.Address +
((immHi.Value.ToInt32() << 12) +
immLo.Value.ToInt32());
var hiOp = new SliceOperand(SliceType.PcRelHi, immHi, fullAddr);
var loOp = new SliceOperand(SliceType.PcRelLo, immLo, fullAddr);
instrHi.Operands[1] = hiOp;
instrLo.Operands[1] = loOp;
}
}
yield return e.Current;
break;
case Mnemonic.lui:
if (!e.TryPeek(1, out instrLo))
{
yield return e.Current;
break;
}
if (instrLo.Mnemonic == Mnemonic.addi)
{
if (instrHi.Operands[0] == instrLo.Operands[0] &&
instrLo.Operands[0] == instrLo.Operands[1])
{
// Mutate the auipc and the addi
var immHi = (ImmediateOperand) instrHi.Operands[1];
immLo = (ImmediateOperand) instrLo.Operands[2];
var fullAddr = ImmediateOperand.Word32(
(immHi.Value.ToInt32() << 12) +
immLo.Value.ToInt32());
var hiOp = new SliceOperand(SliceType.PcRelHi, immHi, fullAddr);
var loOp = new SliceOperand(SliceType.PcRelLo, immLo, fullAddr);
instrHi.Operands[1] = hiOp;
instrLo.Operands[2] = loOp;
}
}
else if (IsMemoryInstruction(instrLo.Mnemonic))
{
var memOp = (MemoryOperand) instrLo.Operands[1];
if (instrLo.Operands[0] == memOp.Base &&
memOp.Offset is ImmediateOperand imm)
{
immLo = imm;
// Mutate the auipc and the memory operand
var immHi = (ImmediateOperand) instrHi.Operands[1];
var fullAddr = ImmediateOperand.Word32(
(immHi.Value.ToInt32() << 12) +
immLo.Value.ToInt32());
var hiOp = new SliceOperand(SliceType.PcRelHi, immHi, fullAddr);
var loOp = new SliceOperand(SliceType.PcRelLo, immLo, fullAddr);
instrHi.Operands[1] = hiOp;
instrLo.Operands[1] = loOp;
}
}
yield return e.Current;
break;
default:
yield return e.Current;
break;
}
}
}

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

private static bool IsMemoryInstruction(Mnemonic mnemonic)
{
return mnemonic switch
{
Mnemonic.c_ld or
Mnemonic.c_lw or
Mnemonic.c_sd or
Mnemonic.c_sw or
Mnemonic.lb or
Mnemonic.lbu or
Mnemonic.lh or
Mnemonic.lhu or
Mnemonic.lw or
Mnemonic.lwu or
Mnemonic.ld or
Mnemonic.sb or
Mnemonic.sh or
Mnemonic.sw or
Mnemonic.sd or
Mnemonic.fsd or
Mnemonic.fsw => true,
_ => false,
};
}
}
}
15 changes: 10 additions & 5 deletions src/Arch/RiscV/MemoryOperand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,30 @@ namespace Reko.Arch.RiscV
public class MemoryOperand : AbstractMachineOperand
{

public MemoryOperand(PrimitiveType width, RegisterStorage baseRegister, int offset) : base(width)
public MemoryOperand(PrimitiveType width, RegisterStorage baseRegister, MachineOperand offset) : base(width)
{
this.Base = baseRegister;
this.Offset = offset;
}

public RegisterStorage Base { get; }
public int Offset { get; }
public MachineOperand Offset { get; }

protected override void DoRender(MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
{
if(Offset == 0)
if (Offset is SliceOperand slice)
{
renderer.WriteFormat("({0})", Base);
slice.Render(renderer, options);
}
else
{
renderer.WriteFormat("{0}({1})", Offset, Base);
int offset = ((ImmediateOperand) Offset).Value.ToInt32();
if (offset != 0)
{
renderer.WriteFormat("{0}", offset);
}
}
renderer.WriteFormat("({0})", Base.Name);
}
}
}
3 changes: 2 additions & 1 deletion src/Arch/RiscV/RiscVArchitecture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public RiscVArchitecture(IServiceProvider services, string archId, Dictionary<st

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

public override IEqualityComparer<MachineInstruction> CreateInstructionComparer(Normalize norm)
Expand Down
40 changes: 33 additions & 7 deletions src/Arch/RiscV/RiscVAssemblyRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ static RiscVAssemblyRenderer()
};
}

public string FormatValue(Constant c, bool forceSignForSignedIntegers = false)
public static string FormatValue(Constant c, bool forceSignForSignedIntegers = false)
{
var pt = (PrimitiveType) c.DataType;
if (pt.Domain == Domain.SignedInt)
Expand Down Expand Up @@ -269,7 +269,8 @@ public void Render(RiscVInstruction instr, MachineInstructionRenderer renderer,
RenderSyntheticInstruction("li", instr, renderer, options, ops_0_2);
return;
}
if (((ImmediateOperand) instr.Operands[2]).Value.IsZero)
if (instr.Operands[2] is ImmediateOperand imm &&
imm.Value.IsZero)
{
RenderSyntheticInstruction("mv", instr, renderer, options, ops_0_1);
return;
Expand Down Expand Up @@ -417,26 +418,44 @@ protected virtual void RenderOperand(
}
return;
case MemoryOperand memop:
RenderMemoryOperand(memop, renderer, options);
RenderMemoryOperand(instr, memop, renderer, options);
return;
case SliceOperand slice:
RenderSliceOperand(instr, slice, renderer, options);
return;
}
throw new NotImplementedException($"Risc-V operand type {op.GetType().Name} not implemented yet.");
}

protected virtual void RenderMemoryOperand(
RiscVInstruction instr,
MemoryOperand memop,
MachineInstructionRenderer renderer,
MachineInstructionRendererOptions options)
{
if(memop.Offset == 0)
if (memop.Offset is SliceOperand slice)
{
renderer.WriteFormat("({0:X})", memop.Base);
RenderSliceOperand(instr, slice, renderer, options);
}
else
{
var offset = FormatSignedValue(memop.Offset);
renderer.WriteFormat("{0}({1:X})", offset, memop.Base);
int offset = ((ImmediateOperand) memop.Offset).Value.ToInt32();
if (offset != 0)
{
var sOffset = FormatSignedValue(offset);
renderer.WriteFormat(sOffset);
}
}
renderer.WriteFormat("({0})", memop.Base.Name);
}


internal void RenderSliceOperand(RiscVInstruction instr, SliceOperand slice, MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
{
renderer.WriteString(slice.Slice.Format());
renderer.WriteChar('(');
this.RenderOperand(instr, slice.InferredValue, renderer, options);
renderer.WriteChar(')');
}

protected virtual void RenderRegister(string regName, MachineInstructionRenderer renderer)
Expand All @@ -448,6 +467,13 @@ protected virtual void RenderImmediate(
ImmediateOperand imm,
MachineInstructionRenderer renderer)
{
RenderImmediateOperand(imm, renderer);
}

public static void RenderImmediateOperand(
ImmediateOperand imm,
MachineInstructionRenderer renderer)
{
var pt = imm.Value.DataType;
if (pt.Domain == Domain.Offset)
renderer.WriteString(FormatUnsignedValue(imm.Value.ToUInt64(), "0x{0:X}"));
Expand Down
Loading

0 comments on commit a79b964

Please sign in to comment.