Move intermediate instruction decompiling to decompiler class

This commit is contained in:
HoLLy 2019-11-23 00:10:49 +01:00
Родитель e803b5ea5b
Коммит 83f9e771ba
2 изменённых файлов: 71 добавлений и 72 удалений

Просмотреть файл

@ -1,6 +1,4 @@
using System.Diagnostics;
using WasmLib.FileFormat;
using WasmLib.Utils;
namespace WasmLib.Decompilation.Intermediate
{
@ -24,75 +22,6 @@ namespace WasmLib.Decompilation.Intermediate
public abstract string OperationStringFormat { get; }
public virtual string? Comment => null;
// TODO: this should be in IntermediateRepresentationDecompiler or IntermediateContext
public void Handle(ref IntermediateContext context)
{
if (context.RestOfBlockUnreachable && IsImplicit) {
#if DEBUG
context.WriteFull("// omitted implicit instruction because rest of block is unreachable");
#endif
return;
}
var args = new Variable[PopCount];
for (int i = 0; i < PopCount; i++) {
args[i] = context.Pop();
Debug.Assert(PopTypes[i] == args[i].Type || PopTypes[i] == ValueKind.Any || args[i].Type == ValueKind.Any);
}
context.RestOfBlockUnreachable = RestOfBlockUnreachable;
// NOTE: really ugly and slow, but can't be replaced with string.format since input is dynamic and can contain {}
string s = OperationStringFormat.SafeFormat(args);
Debug.Assert(PushCount <= 1);
if (PushCount > 0) {
s = $"{context.Push(PushTypes[0])} = {s}";
}
if (HasBlock) {
s += " {";
}
if (Comment != null) {
s += " // " + Comment;
}
context.WriteFull(s);
if (HasBlock) {
HandleBlock(ref context, Block1!);
if (Block2 != null) {
context.WriteFull("} else {");
HandleBlock(ref context, Block2);
}
context.WriteFull("}");
}
static void HandleBlock(ref IntermediateContext context2, in ControlBlock block)
{
context2.EnterBlock();
foreach (IntermediateInstruction instruction in block.Instructions) {
instruction.Handle(ref context2);
}
// if stack has values left on it, and we expect a return value
if (block.HasReturn && !context2.RestOfBlockUnreachable) {
Debug.Assert(context2.StackIndices.Peek() != context2.Stack.Count);
var popped = context2.Pop();
Debug.Assert(popped.Type == block.ValueKind);
context2.WriteFull($"block_return {popped}");
}
context2.ExitBlock();
}
}
public override string ToString() => GetType().Name.Replace("Instruction", string.Empty);
}
}

Просмотреть файл

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using WasmLib.Decompilation.Intermediate;
using WasmLib.FileFormat;
using WasmLib.Utils;
namespace WasmLib.Decompilation
{
@ -29,7 +31,7 @@ namespace WasmLib.Decompilation
// write all IR while simulating the stack
foreach (IntermediateInstruction instruction in instructions) {
instruction.Handle(ref context);
HandleInstruction(ref context, instruction);
}
output.WriteLine("}");
@ -44,5 +46,73 @@ namespace WasmLib.Decompilation
output.WriteLine();
}
private static void HandleInstruction(ref IntermediateContext context, IntermediateInstruction instruction)
{
if (context.RestOfBlockUnreachable && instruction.IsImplicit) {
#if DEBUG
context.WriteFull("// omitted implicit instruction because rest of block is unreachable");
#endif
return;
}
var args = new Variable[instruction.PopCount];
for (int i = 0; i < instruction.PopCount; i++) {
args[i] = context.Pop();
Debug.Assert(instruction.PopTypes[i] == args[i].Type || instruction.PopTypes[i] == ValueKind.Any || args[i].Type == ValueKind.Any);
}
context.RestOfBlockUnreachable = instruction.RestOfBlockUnreachable;
// NOTE: really ugly and slow, but can't be replaced with string.format since input is dynamic and can contain {}
string s = instruction.OperationStringFormat.SafeFormat(args);
Debug.Assert(instruction.PushCount <= 1);
if (instruction.PushCount > 0) {
s = $"{context.Push(instruction.PushTypes[0])} = {s}";
}
if (instruction.HasBlock) {
s += " {";
}
if (instruction.Comment != null) {
s += " // " + instruction.Comment;
}
context.WriteFull(s);
if (instruction.HasBlock) {
HandleBlock(ref context, instruction.Block1!);
if (instruction.Block2 != null) {
context.WriteFull("} else {");
HandleBlock(ref context, instruction.Block2);
}
context.WriteFull("}");
}
static void HandleBlock(ref IntermediateContext context2, in ControlBlock block)
{
context2.EnterBlock();
foreach (IntermediateInstruction instr in block.Instructions) {
HandleInstruction(ref context2, instr);
}
// if stack has values left on it, and we expect a return value
if (block.HasReturn && !context2.RestOfBlockUnreachable) {
Debug.Assert(context2.StackIndices.Peek() != context2.Stack.Count);
var popped = context2.Pop();
Debug.Assert(popped.Type == block.ValueKind);
context2.WriteFull($"block_return {popped}");
}
context2.ExitBlock();
}
}
}
}