From ff92ec08d7dfa2039760fdd5c5092c04aae1bb26 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sat, 23 Nov 2019 17:08:33 +0100 Subject: [PATCH] Implement basic control block support --- WasmLib/Decompilation/GenericDecompiler.cs | 42 ++++++++++++++----- .../Intermediate/Graph/InstructionNode.cs | 6 ++- .../SourceCode/GenericExpression.cs | 7 +++- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/WasmLib/Decompilation/GenericDecompiler.cs b/WasmLib/Decompilation/GenericDecompiler.cs index dc270d7..102a11a 100644 --- a/WasmLib/Decompilation/GenericDecompiler.cs +++ b/WasmLib/Decompilation/GenericDecompiler.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using Rivers; using Rivers.Analysis; +using Rivers.Serialization.Dot; using WasmLib.Decompilation.Intermediate; using WasmLib.Decompilation.Intermediate.Graph; using WasmLib.Decompilation.SourceCode; @@ -49,17 +50,18 @@ namespace WasmLib.Decompilation int instructionNum = 0; foreach (IntermediateInstruction instruction in instructions) { - if (instruction.HasBlock) { - throw new NotImplementedException(); - } - - InstructionNode node = new InstructionNode(instruction, instructionNum++); + InstructionNode node = !instruction.HasBlock + ? new InstructionNode(instruction, instructionNum++) + : instruction.Block2 == null + ? new InstructionNode(instruction, instructionNum++, CreateGraph(instruction.Block1!.Instructions)) + : new InstructionNode(instruction, instructionNum++, CreateGraph(instruction.Block1!.Instructions), CreateGraph(instruction.Block2.Instructions)); + graph.Nodes.Add(node); Debug.Assert(instruction.PushCount <= 1, "Instruction pushed multiple variables to stack, which shouldn't happen."); for (int i = 0; i < instruction.PopCount; i++) { (InstructionNode sourceInstruction, ValueKind type) = stack.Pop(); - Debug.Assert(type == instruction.PopTypes[i]); + Debug.Assert(type == instruction.PopTypes[i] || type == ValueKind.Any || instruction.PopTypes[i] == ValueKind.Any); sourceInstruction.OutgoingEdges.Add(new StackVariableEdge(sourceInstruction, node, type)); } @@ -79,13 +81,16 @@ namespace WasmLib.Decompilation // BUG: see Washi1337/Rivers#6 // Debug.Assert(!graph.IsCyclic(), "Got cyclic dependency in function!"); if (!graph.IsConnected()) { + #if DEBUG + using (StreamWriter sw = new StreamWriter(File.OpenWrite("temp_unconnected.dot"))) new DotWriter(sw).Write(graph); + #endif throw new NotImplementedException(); } return graph; } - private static void OutputAsCode(Graph graph, TextWriter output) + private static void OutputAsCode(Graph graph, TextWriter output, int tabCount = 1) { var varCounts = new Dictionary { {ValueKind.I32, 0}, @@ -120,17 +125,32 @@ namespace WasmLib.Decompilation } } - statements[currentNode.Index] = new GenericExpression(currentNode.Instruction, parameters); + statements[currentNode.Index] = new GenericExpression(currentNode.Instruction, parameters, currentNode.Block1, currentNode.Block2); } else { - statements[currentNode.Index] = new GenericExpression(currentNode.Instruction); + statements[currentNode.Index] = new GenericExpression(currentNode.Instruction, null, currentNode.Block1, currentNode.Block2); } - } foreach (IExpression expression in statements.Values) { // TODO: support comments - output.WriteLine(new string('\t', 1) + expression.GetStringRepresentation()); + if (expression is GenericExpression ge && ge.Block1 != null) { + output.WriteLine(new string('\t', tabCount) + expression.GetStringRepresentation() + " {"); + + OutputAsCode(ge.Block1, output, tabCount + 1); + + if (ge.Block2 == null) { + output.WriteLine(new string('\t', tabCount) + "}"); + } + else { + output.WriteLine(new string('\t', tabCount) + "} else {"); + OutputAsCode(ge.Block2, output, tabCount + 1); + output.WriteLine(new string('\t', tabCount) + "}"); + } + } + else { + output.WriteLine(new string('\t', tabCount) + expression.GetStringRepresentation()); + } } } } diff --git a/WasmLib/Decompilation/Intermediate/Graph/InstructionNode.cs b/WasmLib/Decompilation/Intermediate/Graph/InstructionNode.cs index 5bca9d3..e1ecaa8 100644 --- a/WasmLib/Decompilation/Intermediate/Graph/InstructionNode.cs +++ b/WasmLib/Decompilation/Intermediate/Graph/InstructionNode.cs @@ -15,11 +15,15 @@ namespace WasmLib.Decompilation.Intermediate.Graph public IEnumerable IncomingImpurityEdges => IncomingEdges.OfType(); public IEnumerable OutgoingVariableEdges => OutgoingEdges.OfType(); public IEnumerable IncomingVariableEdges => IncomingEdges.OfType(); + public Rivers.Graph? Block1 { get; } + public Rivers.Graph? Block2 { get; } - public InstructionNode(IntermediateInstruction instruction, int idx) : base($"_{idx:X4}") + public InstructionNode(IntermediateInstruction instruction, int idx, Rivers.Graph? block1 = null, Rivers.Graph? block2 = null) : base($"_{idx:X4}") { Instruction = instruction; Index = idx; + Block1 = block1; + Block2 = block2; AddUserData(); } diff --git a/WasmLib/Decompilation/SourceCode/GenericExpression.cs b/WasmLib/Decompilation/SourceCode/GenericExpression.cs index 5aab8cd..6021dd5 100644 --- a/WasmLib/Decompilation/SourceCode/GenericExpression.cs +++ b/WasmLib/Decompilation/SourceCode/GenericExpression.cs @@ -1,4 +1,5 @@ using System.Linq; +using Rivers; using WasmLib.Decompilation.Intermediate; using WasmLib.Utils; @@ -8,11 +9,15 @@ namespace WasmLib.Decompilation.SourceCode { public IntermediateInstruction BaseInstruction { get; } public IExpression[]? Parameters { get; } + public Graph? Block1 { get; } + public Graph? Block2 { get; } - public GenericExpression(IntermediateInstruction baseInstruction, IExpression[]? parameters = null) + public GenericExpression(IntermediateInstruction baseInstruction, IExpression[]? parameters = null, Graph? block1 = null, Graph? block2 = null) { BaseInstruction = baseInstruction; Parameters = parameters; + Block1 = block1; + Block2 = block2; } public string GetStringRepresentation() => BaseInstruction.OperationStringFormat.SafeFormat(Parameters?.Select(x => x.GetStringRepresentation()).ToArray());