mirror of
https://github.com/Colton1skees/Dna
synced 2026-06-21 13:42:09 +00:00
control flow structuring
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
[*.cs]
|
||||
|
||||
# CS8603: Possible null reference return.
|
||||
dotnet_diagnostic.CS8603.severity = silent
|
||||
@@ -9,6 +9,13 @@ namespace Dna.ControlFlow.Analysis
|
||||
{
|
||||
public static class DominatorAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a dominator tree for the control flow graph.
|
||||
/// For each node {N}, yield a set of nodes all nodes which dominate {N}.
|
||||
/// </summary>
|
||||
/// <param name="graph"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public static IReadOnlyDictionary<Node, HashSet<Node>> GetDominatorTree(Graph graph)
|
||||
{
|
||||
if (graph.Nodes.First().IncomingEdges.Count > 0)
|
||||
|
||||
+50
-11
@@ -22,6 +22,8 @@ using ClangSharp.Interop;
|
||||
using ClangSharp;
|
||||
using Dna.Decompiler;
|
||||
using Dna.Emulation.Unicorn;
|
||||
using Dna.Decompilation;
|
||||
using Dna.Structuring.Stackify;
|
||||
|
||||
// Load the 64 bit PE file.
|
||||
// Note: This file is automatically copied to the build directory.
|
||||
@@ -54,21 +56,21 @@ for (int i = 0; i < 3; i++)
|
||||
//blockDcePass.Run();
|
||||
|
||||
// Print the optimized control flow graph.
|
||||
Console.WriteLine("Optimized cfg:\n{0}", GraphFormatter.FormatGraph(liftedCfg));
|
||||
//Console.WriteLine("Optimized cfg:\n{0}", GraphFormatter.FormatGraph(liftedCfg));
|
||||
|
||||
// Create a .DOT file for visualizing the IR cfg.
|
||||
var dotGraph = GraphVisualizer.GetDotGraph(liftedCfg);
|
||||
File.WriteAllText("graph.dot", dotGraph.Compile(false, false));
|
||||
//var dotGraph = GraphVisualizer.GetDotGraph(liftedCfg);
|
||||
//File.WriteAllText("graph.dot", dotGraph.Compile(false, false));
|
||||
|
||||
// Load the binary into unicorn engine.
|
||||
var emulator = new UnicornEmulator(architecture);
|
||||
PEMapper.MapBinary(emulator, binary);
|
||||
// var emulator = new UnicornEmulator(architecture);
|
||||
//PEMapper.MapBinary(emulator, binary);
|
||||
|
||||
// Setup the stack.
|
||||
ulong rsp = 0x100000000;
|
||||
emulator.MapMemory(rsp, 0x1000 * 12);
|
||||
rsp += 0x100;
|
||||
emulator.SetRegister(register_e.ID_REG_X86_RSP, rsp);
|
||||
//ulong rsp = 0x100000000;
|
||||
//emulator.MapMemory(rsp, 0x1000 * 12);
|
||||
//rsp += 0x100;
|
||||
//emulator.SetRegister(register_e.ID_REG_X86_RSP, rsp);
|
||||
|
||||
// Execute the function.
|
||||
//emulator.Start(0x140001747);
|
||||
@@ -99,7 +101,6 @@ passManager.AddInstructionCombiningPass();
|
||||
passManager.AddCFGSimplificationPass();
|
||||
passManager.AddDeadStoreEliminationPass();
|
||||
passManager.AddAggressiveDCEPass();
|
||||
|
||||
passManager.InitializeFunctionPassManager();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
@@ -113,8 +114,46 @@ bool printLLVM = false;
|
||||
if (printLLVM)
|
||||
llvmLifter.Module.Dump();
|
||||
|
||||
llvmLifter.Module.PrintToFile(@"C:\Users\colton\Downloads\dfgfgfgd\code.ll");
|
||||
var llPath = @"C:\Users\colton\Downloads\dfgfgfgd\code.ll";
|
||||
var compiledAsmPath = @"C:\Users\colton\Downloads\dfgfgfgd\code.asm";
|
||||
var wasmTextPath = @"C:\Users\colton\Downloads\dfgfgfgd\code.wat";
|
||||
var wasmBinaryPath = @"C:\Users\colton\Downloads\dfgfgfgd\code.wasm";
|
||||
var compiledExePath = @"C:\Users\colton\Downloads\dfgfgfgd\code.exe";
|
||||
llvmLifter.Module.PrintToFile(llPath);
|
||||
llvmLifter.Module.WriteBitcodeToFile(@"C:\Users\colton\Downloads\dfgfgfgd\lifted.bc");
|
||||
|
||||
var stackifier = new CfgStackifier();
|
||||
stackifier.Stackify(liftedCfg);
|
||||
|
||||
Console.WriteLine("Press enter.");
|
||||
Console.ReadLine();
|
||||
// Compile the bitcode to a native executable.
|
||||
var nativeDecompiler = new LLVMDecompiler();
|
||||
//nativeDecompiler.RunClang($"{llPath} -S -o {compiledAsmPath} -O3 -target x86_64");
|
||||
//nativeDecompiler.RunClang($"{compiledAsmPath} -target x86_64 -O3 -c -o {compiledExePath}");
|
||||
|
||||
// Compile the bitcode to wasm text.
|
||||
/*
|
||||
nativeDecompiler.RunClang($"{llPath} -o {compiledAsmPath} -O3 -target wasm64");
|
||||
var cmd = $"{llPath} -S -o {wasmTextPath} -O3 -target wasm64";
|
||||
Console.WriteLine(cmd);
|
||||
nativeDecompiler.RunClang(cmd);
|
||||
|
||||
// Compile the bitcode to wasm binary.
|
||||
var cmd2 = $"wat2wasm {wasmTextPath} -o {wasmBinaryPath}";
|
||||
Console.WriteLine(cmd2);
|
||||
nativeDecompiler.RunClang(cmd2);
|
||||
*/
|
||||
|
||||
|
||||
var cmd = $"{llPath} -S -o {wasmTextPath} -O3 -target wasm64";
|
||||
//nativeDecompiler.RunClang(cmd);
|
||||
//nativeDecompiler.RunClang($"{llPath} -S -o {wasmBinaryPath} -O3 -target wasm64");
|
||||
//nativeDecompiler.RunClang($"--target=wasm64 -O3 -nostdlib {llPath} -o {wasmBinaryPath} -Wl,--no-entry");
|
||||
// Decompile the bitcode to pseudo C.
|
||||
nativeDecompiler.Decompile(wasmBinaryPath);
|
||||
Console.WriteLine("Press enter to run rellic.");
|
||||
Console.ReadLine();
|
||||
// Optionally decompile the lifted function to go-to free pseudo C, via Rellic.
|
||||
// On my machine, a fork of Rellic runs under WSL2 and communiucates via gRPC.
|
||||
// If you are not hosting this server at localhost:50051, then the API
|
||||
|
||||
@@ -21,6 +21,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dna.Decompiler", "Dna.Decom
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unicorn.Net", "unicorn-net\src\Unicorn.Net\Unicorn.Net.csproj", "{47D397E7-098D-4058-9B19-DC01124ED897}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{09C09202-899C-4F09-9884-93AAAD5CF6A5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using LLVMSharp.Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WebAssembly;
|
||||
|
||||
namespace Dna.Decompilation
|
||||
{
|
||||
public class LLVMDecompiler
|
||||
{
|
||||
// TODO: Remove hardcoded path.
|
||||
private const string clangPath = @"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin\clang.exe";
|
||||
|
||||
public void Decompile(string wasmPath)
|
||||
{
|
||||
var module = Module.ReadFromBinary(wasmPath);
|
||||
Console.WriteLine(module);
|
||||
}
|
||||
|
||||
public void RunClang(string arguments)
|
||||
{
|
||||
var process = new Process();
|
||||
process.StartInfo.FileName = clangPath;
|
||||
process.StartInfo.Arguments = arguments;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
process.Start();
|
||||
|
||||
Console.WriteLine(clangPath + " " + arguments);
|
||||
process.WaitForExit();
|
||||
if(process.ExitCode != 0 )
|
||||
{
|
||||
throw new Exception("command failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@
|
||||
<PackageReference Include="LLVMSharp" Version="15.0.0-beta1" />
|
||||
<PackageReference Include="Microsoft.Z3" Version="4.11.2" />
|
||||
<PackageReference Include="Rivers" Version="0.1.0" />
|
||||
<PackageReference Include="WebAssembly" Version="1.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,9 +21,7 @@ namespace Dna.Lifting
|
||||
|
||||
private readonly LLVMBuilderRef builder;
|
||||
|
||||
private LLVMValueRef memoryPtr;
|
||||
|
||||
private LLVMValueRef percentOne;
|
||||
private readonly LLVMValueRef memoryPtr;
|
||||
|
||||
private readonly Func<IOperand, LLVMValueRef> load;
|
||||
|
||||
@@ -38,11 +36,6 @@ namespace Dna.Lifting
|
||||
this.storeToOperand = storeToOperand;
|
||||
}
|
||||
|
||||
public void SetMemoryPtr(LLVMValueRef percentOne)
|
||||
{
|
||||
this.percentOne = percentOne;
|
||||
}
|
||||
|
||||
public void LiftInstructionToLLVM(AbstractInst instruction, Func<ulong, LLVMBasicBlockRef> getBlockByAddress)
|
||||
{
|
||||
LLVMValueRef? one = null;
|
||||
@@ -210,13 +203,9 @@ namespace Dna.Lifting
|
||||
case InstLoad inst:
|
||||
// Cast the address to a pointer.
|
||||
var loadValType = LLVMTypeRef.CreateInt(inst.Bitsize);
|
||||
|
||||
var ptrType = LLVMTypeRef.CreatePointer(loadValType, 0);
|
||||
|
||||
//var percentFour = builder.BuildLoad2(ptrType, memoryPtr);
|
||||
|
||||
var loadPointer = builder.BuildInBoundsGEP2(ptrType, percentOne, new LLVMValueRef[] { op1() });
|
||||
//var loadPointer = builder.BuildIntToPtr(op1(), ptrType, "loadPtr");
|
||||
//var loadPointer = builder.BuildInBoundsGEP2(ptrType, op1(), new LLVMValueRef[] {});
|
||||
var loadPointer = builder.BuildIntToPtr(op1(), ptrType, "loadPtr");
|
||||
|
||||
// Dereference the pointer.
|
||||
var loadValue = builder.BuildLoad2(loadValType, loadPointer, "load");
|
||||
@@ -232,10 +221,7 @@ namespace Dna.Lifting
|
||||
// Cast the destination address to a pointer of the source value width.
|
||||
var storeIntType = LLVMTypeRef.CreateInt(inst.Op1.Bitsize);
|
||||
var storePtrType = LLVMTypeRef.CreatePointer(storeIntType, 0);
|
||||
|
||||
//var percentFour2 = builder.BuildLoad2(storePtrType, memoryPtr);
|
||||
//var storePtr = builder.BuildIntToPtr(storeAddr, storePtrType, "storePtr");
|
||||
var storePtr = builder.BuildInBoundsGEP2(storeIntType, percentOne, new LLVMValueRef[] { storeAddr });
|
||||
var storePtr = builder.BuildIntToPtr(storeAddr, storePtrType, "storePtr");
|
||||
|
||||
// Write to memory.
|
||||
builder.BuildStore(storeValue, storePtr);
|
||||
|
||||
@@ -53,14 +53,14 @@ namespace Dna.Lifting
|
||||
|
||||
// Constrct an LLVM module and builder.
|
||||
module = LLVMModuleRef.CreateWithName("TritonTranslator");
|
||||
module.Target = "x86_64";
|
||||
module.Target = "wasm64";
|
||||
builder = Module.Context.CreateBuilder();
|
||||
|
||||
// Create an i64* pointer to store memory.
|
||||
var memoryPtrType = LLVMTypeRef.CreatePointer(LLVMTypeRef.CreateInt(64), 0);
|
||||
var memoryPtrType = LLVMTypeRef.CreateInt(64);
|
||||
memoryPtr = Module.AddGlobal(memoryPtrType, "memory");
|
||||
memoryPtr.Linkage = LLVMLinkage.LLVMCommonLinkage;
|
||||
var memoryPtrNull = LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreateInt(64));
|
||||
var memoryPtrNull = LLVMValueRef.CreateConstInt(memoryPtrType, 0, false);
|
||||
memoryPtr.Initializer = memoryPtrNull;
|
||||
|
||||
// Construct an LLVM lifter for translating individual instructions.
|
||||
@@ -131,14 +131,6 @@ namespace Dna.Lifting
|
||||
// Lift each block to LLVM IR.
|
||||
foreach(var block in irBlocks)
|
||||
{
|
||||
if(block == irBlocks.First())
|
||||
{
|
||||
var storeIntType = LLVMTypeRef.CreateInt(64);
|
||||
var storePtrType = LLVMTypeRef.CreatePointer(storeIntType, 0);
|
||||
|
||||
var percentFour2 = builder.BuildLoad2(storePtrType, memoryPtr);
|
||||
lifter.SetMemoryPtr(percentFour2);
|
||||
}
|
||||
builder.PositionAtEnd(liftedBlockMapping[block]);
|
||||
foreach(var inst in block.Instructions)
|
||||
{
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring
|
||||
namespace Dna.Structuring.Revng
|
||||
{
|
||||
/// <summary>
|
||||
/// The following algorithm is a reimplementation from the paper:
|
||||
@@ -45,7 +45,7 @@ namespace Dna.Structuring
|
||||
{
|
||||
// Insert a dummy node for each retreating edge.
|
||||
// TODO: Add code to JMP to the correct destination.
|
||||
foreach(var backEdge in backEdges)
|
||||
foreach (var backEdge in backEdges)
|
||||
{
|
||||
var target = backEdge.TargetBlock;
|
||||
var dummy = NodeInserter.InsertArtificalNode(graph);
|
||||
@@ -6,7 +6,7 @@ using System.Reflection.Metadata;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring
|
||||
namespace Dna.Structuring.Revng
|
||||
{
|
||||
public static class EdgeUpdater
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring
|
||||
namespace Dna.Structuring.Revng
|
||||
{
|
||||
public interface IMetaRegion<T>
|
||||
{
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring
|
||||
namespace Dna.Structuring.Revng
|
||||
{
|
||||
public class MetaRegion<T> : IMetaRegion<T>
|
||||
{
|
||||
@@ -38,7 +38,7 @@ namespace Dna.Structuring
|
||||
{
|
||||
// Remove old SCS nodes.
|
||||
bool needSubstitution = false;
|
||||
foreach(var node in removal)
|
||||
foreach (var node in removal)
|
||||
{
|
||||
// Skip if the node does not need to be removed.
|
||||
if (!Nodes.Contains(node))
|
||||
@@ -84,7 +84,7 @@ namespace Dna.Structuring
|
||||
public bool IntersectsWith(IMetaRegion<T> region)
|
||||
{
|
||||
// If the region contains any node from our node list, there is an intersection.
|
||||
foreach(var node in Nodes)
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (region.Nodes.Contains(node))
|
||||
return true;
|
||||
@@ -97,7 +97,7 @@ namespace Dna.Structuring
|
||||
// TODO: Validate.
|
||||
public bool IsSubSet(IMetaRegion<T> region)
|
||||
{
|
||||
foreach(var node in region.Nodes)
|
||||
foreach (var node in region.Nodes)
|
||||
{
|
||||
if (!Nodes.Contains(node))
|
||||
return false;
|
||||
@@ -109,7 +109,7 @@ namespace Dna.Structuring
|
||||
// TODO: Validate.
|
||||
public bool IsSuperSet(IMetaRegion<T> region)
|
||||
{
|
||||
foreach(var node in Nodes)
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (!region.Nodes.Contains(node))
|
||||
return false;
|
||||
@@ -134,9 +134,9 @@ namespace Dna.Structuring
|
||||
|
||||
public BasicBlock<T> GetProbableEntry(IEnumerable<BasicBlock<T>> reversePostOrderTraveral)
|
||||
{
|
||||
foreach(var node in reversePostOrderTraveral)
|
||||
foreach (var node in reversePostOrderTraveral)
|
||||
{
|
||||
if(ContainsNode(node))
|
||||
if (ContainsNode(node))
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring
|
||||
namespace Dna.Structuring.Revng
|
||||
{
|
||||
public class NodeInserter
|
||||
{
|
||||
@@ -0,0 +1,251 @@
|
||||
using Dna.ControlFlow;
|
||||
using Dna.ControlFlow.Analysis;
|
||||
using Dna.Structuring.Stackify.Structured;
|
||||
using Iced.Intel;
|
||||
using Rivers;
|
||||
using Rivers.Analysis.Traversal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
using static Dna.Structuring.Stackify.Structured.CtrlEntry;
|
||||
|
||||
namespace Dna.Structuring.Stackify
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for structuring arbitrary control flow graphs using the stackify algorithm. References:
|
||||
/// - https://github.com/cfallin/waffle/blob/main/src/backend/stackify.rs
|
||||
/// </summary>
|
||||
public class CfgStackifier
|
||||
{
|
||||
private ControlFlowGraph<AbstractInst> cfg;
|
||||
|
||||
private Stack<CtrlEntry> ctrlStack = new Stack<CtrlEntry>();
|
||||
|
||||
private IReadOnlyDictionary<Node, HashSet<Node>> dominatorTree;
|
||||
|
||||
private HashSet<BasicBlock<AbstractInst>> loopHeaders;
|
||||
|
||||
private HashSet<BasicBlock<AbstractInst>> mergeNodes;
|
||||
|
||||
Dictionary<Node, int> rpoPositions;
|
||||
|
||||
public void Stackify(ControlFlowGraph<AbstractInst> cfg)
|
||||
{
|
||||
// Compute merge nodes and loop headers.
|
||||
this.cfg = cfg;
|
||||
dominatorTree = DominatorAnalysis.GetDominatorTree(cfg);
|
||||
var info = ComputeMergeNodesAndLoopHeaders();
|
||||
|
||||
var body = new List<WasmBlock>();
|
||||
HandleDomSubtree(cfg.GetBlocks().First(), ref body);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
|
||||
//Console.WriteLine(info);
|
||||
}
|
||||
|
||||
private (HashSet<BasicBlock<AbstractInst>> mergeNodes, HashSet<BasicBlock<AbstractInst>> loopHeaders) ComputeMergeNodesAndLoopHeaders()
|
||||
{
|
||||
loopHeaders = new HashSet<BasicBlock<AbstractInst>>();
|
||||
mergeNodes = new HashSet<BasicBlock<AbstractInst>>();
|
||||
var branchedOnce = new HashSet<BasicBlock<AbstractInst>>();
|
||||
|
||||
// Record a reverse postorder traversal of the control flow.
|
||||
var traversal = new DepthFirstTraversal();
|
||||
var recorder = new PostOrderRecorder(traversal);
|
||||
traversal.Run(cfg.Nodes.First());
|
||||
var rpo = recorder.GetOrder().Reverse().ToList();
|
||||
|
||||
// Store a position of each block in the RPO, if reachable.
|
||||
rpoPositions = new();
|
||||
for (int i = 0; i < rpo.Count; i++)
|
||||
rpoPositions.Add(rpo[i], i);
|
||||
|
||||
// Collect a set of merge nodes and loop headers.
|
||||
foreach(var entry in rpoPositions)
|
||||
{
|
||||
var block = entry.Key;
|
||||
var blockRpo = entry.Value;
|
||||
foreach(var successor in block.OutgoingEdges.Select(x => (BasicBlock<AbstractInst>)x.Target))
|
||||
{
|
||||
var succRpo = rpoPositions[successor];
|
||||
if(succRpo <= blockRpo)
|
||||
{
|
||||
// If the successor node does not dominate the current block.
|
||||
if (!dominatorTree[block].Contains(successor))
|
||||
{
|
||||
throw new InvalidOperationException("Encountered irreducible control flow.");
|
||||
}
|
||||
|
||||
loopHeaders.Add(successor);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if(!branchedOnce.Add(successor))
|
||||
mergeNodes.Add(successor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// I *think* the algorithm does some special case here for switch cases.
|
||||
// TODO: Handle switch statements.
|
||||
if (cfg.Nodes.Any(x => x.OutgoingEdges.Count > 2))
|
||||
throw new InvalidOperationException("TODO: Handle switch cases");
|
||||
return (mergeNodes, loopHeaders);
|
||||
}
|
||||
|
||||
private void Compute()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void HandleDomSubtree(BasicBlock<AbstractInst> block, ref List<WasmBlock> into)
|
||||
{
|
||||
var mergeNodeChildren = dominatorTree[block]
|
||||
.Where(x => mergeNodes.Contains(x))
|
||||
.Cast<BasicBlock<AbstractInst>>()
|
||||
.ToList();
|
||||
|
||||
// Sort merge nodes so the highest RPO number comes first.
|
||||
mergeNodeChildren.OrderByDescending(x => rpoPositions[x]);
|
||||
|
||||
var isLoopHeader = loopHeaders.Contains(block);
|
||||
|
||||
if(isLoopHeader)
|
||||
{
|
||||
ctrlStack.Push(new CtrlEntryLoop(block));
|
||||
var body = new List<WasmBlock>();
|
||||
NodeWithin(block, mergeNodeChildren, ref body);
|
||||
ctrlStack.Pop();
|
||||
into.Add(new Loop()
|
||||
{
|
||||
Body = body,
|
||||
Header = block,
|
||||
});
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
NodeWithin(block, mergeNodeChildren, ref into);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NodeWithin(BasicBlock<AbstractInst> block, IEnumerable<BasicBlock<AbstractInst>> mergeNodes, ref List<WasmBlock> into)
|
||||
{
|
||||
if (mergeNodes.Any())
|
||||
{
|
||||
var first = mergeNodes.First();
|
||||
ctrlStack.Push(new CtrlEntryBlock(first));
|
||||
var body = new List<WasmBlock>();
|
||||
NodeWithin(block, mergeNodes.Skip(1), ref body);
|
||||
into.Add(new Block()
|
||||
{
|
||||
Body = body,
|
||||
Out = first
|
||||
});
|
||||
|
||||
ctrlStack.Pop();
|
||||
HandleDomSubtree(first, ref into);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
into.Add(new Leaf()
|
||||
{
|
||||
Block = block,
|
||||
});
|
||||
|
||||
switch(block.ExitInstruction)
|
||||
{
|
||||
case InstJmp inst:
|
||||
var target = cfg.GetBlocks().Single(x => x.Address == inst.JumpDestination.Value);
|
||||
DoBranch(block, target, ref into);
|
||||
break;
|
||||
case InstJcc inst:
|
||||
// Compute the blocks which are taken depending on the JCC.
|
||||
var thenBlock = cfg.GetBlocks().Single(x => x.Address == inst.ThenOp.Value);
|
||||
var elseBlock = cfg.GetBlocks().Single(x => x.Address == inst.ElseOp.Value);
|
||||
|
||||
// Push an ITE node.
|
||||
ctrlStack.Push(new CtrlEntryIfThenElse());
|
||||
|
||||
// Handle the true block.
|
||||
var ifTrueBody = new List<WasmBlock>();
|
||||
DoBranch(block, thenBlock, ref ifTrueBody);
|
||||
|
||||
// Handle the false block.
|
||||
var ifFalseBody = new List<WasmBlock>();
|
||||
DoBranch(block, elseBlock, ref ifFalseBody);
|
||||
|
||||
// Push the if statement.
|
||||
ctrlStack.Pop();
|
||||
into.Add(new If()
|
||||
{
|
||||
Cond = inst.Op1,
|
||||
IfTrue = ifTrueBody,
|
||||
IfFalse = ifFalseBody,
|
||||
});
|
||||
break;
|
||||
case InstRet:
|
||||
into.Add(new Return());
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Handle handle terminator {block.ExitInstruction}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DoBranch(BasicBlock<AbstractInst> source, BasicBlock<AbstractInst> target, ref List<WasmBlock> into)
|
||||
{
|
||||
// This will be a branch to some entry in the control stack if
|
||||
// the target is either a merge block, or is a backward branch
|
||||
// (by RPO number).
|
||||
if(mergeNodes.Contains(target) || rpoPositions[target] <= rpoPositions[source])
|
||||
{
|
||||
var index = ResolveTarget(target);
|
||||
DoBlockParamTransfer(ref into);
|
||||
into.Add(new Br()
|
||||
{
|
||||
Target = index,
|
||||
});
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Otherwise, we must dominate the block, so just emit it inline.
|
||||
if (!dominatorTree[target].Contains(source))
|
||||
throw new InvalidOperationException($"Expected target node {target} to be dominated by source {source}.");
|
||||
|
||||
DoBlockParamTransfer(ref into);
|
||||
HandleDomSubtree(target, ref into);
|
||||
}
|
||||
}
|
||||
|
||||
private WasmLabel ResolveTarget(BasicBlock<AbstractInst> target)
|
||||
{
|
||||
// This is horribly inefficient. TODO: Refactor.
|
||||
var index = ctrlStack
|
||||
.Reverse()
|
||||
.ToList()
|
||||
.FindIndex(x => x.Label() == target);
|
||||
|
||||
if (index == -1)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return new WasmLabel(index);
|
||||
}
|
||||
|
||||
private void DoBlockParamTransfer(ref List<WasmBlock> into)
|
||||
{
|
||||
// Our IR does not have block params, so we do nothing.
|
||||
// Still, we have to push this empty entry since the rest
|
||||
// of the algorithm expects this to be on the stack.
|
||||
into.Add(new BlockParams());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Dna.ControlFlow;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Block : WasmBlock
|
||||
{
|
||||
public List<WasmBlock> Body { get; set; } = new();
|
||||
|
||||
public BasicBlock<AbstractInst> Out { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class BlockParams : WasmBlock
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Br : WasmBlock
|
||||
{
|
||||
public WasmLabel Target { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Dna.ControlFlow;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public enum CtrlEntryType
|
||||
{
|
||||
Block,
|
||||
Loop,
|
||||
IfThenElse,
|
||||
}
|
||||
|
||||
public abstract record CtrlEntry
|
||||
{
|
||||
public record CtrlEntryBlock(BasicBlock<AbstractInst> outBlock) : CtrlEntry();
|
||||
public record CtrlEntryLoop(BasicBlock<AbstractInst> header) : CtrlEntry();
|
||||
public record CtrlEntryIfThenElse() : CtrlEntry();
|
||||
|
||||
public BasicBlock<AbstractInst> Label()
|
||||
{
|
||||
return this switch
|
||||
{
|
||||
CtrlEntryBlock ctrl => ctrl.outBlock,
|
||||
CtrlEntryLoop ctrl => ctrl.header,
|
||||
CtrlEntryIfThenElse ctrl => null,
|
||||
_ => throw new InvalidOperationException($"Cannot get label for {this}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate.Operands;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class If : WasmBlock
|
||||
{
|
||||
public IOperand Cond { get; set; }
|
||||
|
||||
public List<WasmBlock> IfTrue { get; set; } = new();
|
||||
|
||||
public List<WasmBlock> IfFalse { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Dna.ControlFlow;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Leaf : WasmBlock
|
||||
{
|
||||
public BasicBlock<AbstractInst> Block { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Dna.ControlFlow;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Loop : WasmBlock
|
||||
{
|
||||
public List<WasmBlock> Body { get; set; } = new();
|
||||
|
||||
public BasicBlock<AbstractInst> Header { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate.Operands;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Return : WasmBlock
|
||||
{
|
||||
public List<IOperand> Values { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate.Operands;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Select : WasmBlock
|
||||
{
|
||||
public IOperand Selector { get; set; }
|
||||
|
||||
public List<WasmLabel> Targets { get; set; } = new();
|
||||
|
||||
public WasmLabel Default { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class Unreachable : WasmBlock
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Dna.ControlFlow;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class WasmBlock
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Dna.ControlFlow;
|
||||
using LLVMSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TritonTranslator.Intermediate;
|
||||
|
||||
namespace Dna.Structuring.Stackify.Structured
|
||||
{
|
||||
public class WasmLabel
|
||||
{
|
||||
public int Index { get; set; }
|
||||
|
||||
public WasmLabel(int index)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user