aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/CodeGen/Optimizations/TailMerge.cs
diff options
context:
space:
mode:
authorFICTURE7 <FICTURE7@gmail.com>2021-10-19 02:51:22 +0400
committerGitHub <noreply@github.com>2021-10-18 19:51:22 -0300
commitfbf40424f4d3a9aecc7fe528d7503619738ce542 (patch)
treedf9ba920add4d744fe527e78cc040ff4658a6d7e /ARMeilleure/CodeGen/Optimizations/TailMerge.cs
parentd512ce122cb1c9a7fe7cb40d3f85d642ee37f897 (diff)
Add an early `TailMerge` pass (#2721)
* Add an early `TailMerge` pass Some translations can have a lot of guest calls and since for each guest call there is a call guard which may return. This can produce a lot of epilogue code for returns. This pass merges the epilogue into a single block. ``` Using filter 'hcq'. Using metric 'code size'. Total diff: -1648111 (-7.19 %) (bytes): Base: 22913847 Diff: 21265736 Improved: 4567, regressed: 14, unchanged: 144 ``` * Set PTC version * Address feedback * Handle `void` returning functions * Actually handle `void` returning functions * Fix `RegisterToLocal` logging
Diffstat (limited to 'ARMeilleure/CodeGen/Optimizations/TailMerge.cs')
-rw-r--r--ARMeilleure/CodeGen/Optimizations/TailMerge.cs83
1 files changed, 83 insertions, 0 deletions
diff --git a/ARMeilleure/CodeGen/Optimizations/TailMerge.cs b/ARMeilleure/CodeGen/Optimizations/TailMerge.cs
new file mode 100644
index 00000000..f85b9c69
--- /dev/null
+++ b/ARMeilleure/CodeGen/Optimizations/TailMerge.cs
@@ -0,0 +1,83 @@
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
+
+namespace ARMeilleure.CodeGen.Optimizations
+{
+ static class TailMerge
+ {
+ public static void RunPass(in CompilerContext cctx)
+ {
+ ControlFlowGraph cfg = cctx.Cfg;
+
+ BasicBlock mergedReturn = new(cfg.Blocks.Count);
+
+ Operand returnValue;
+ Operation returnOp;
+
+ if (cctx.FuncReturnType == OperandType.None)
+ {
+ returnValue = default;
+ returnOp = Operation(Instruction.Return, default);
+ }
+ else
+ {
+ returnValue = cfg.AllocateLocal(cctx.FuncReturnType);
+ returnOp = Operation(Instruction.Return, default, returnValue);
+ }
+
+ mergedReturn.Frequency = BasicBlockFrequency.Cold;
+ mergedReturn.Operations.AddLast(returnOp);
+
+ for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
+ {
+ Operation op = block.Operations.Last;
+
+ if (op != default && op.Instruction == Instruction.Return)
+ {
+ block.Operations.Remove(op);
+
+ if (cctx.FuncReturnType == OperandType.None)
+ {
+ PrepareMerge(block, mergedReturn);
+ }
+ else
+ {
+ Operation copyOp = Operation(Instruction.Copy, returnValue, op.GetSource(0));
+
+ PrepareMerge(block, mergedReturn).Append(copyOp);
+ }
+ }
+ }
+
+ cfg.Blocks.AddLast(mergedReturn);
+ cfg.Update();
+ }
+
+ private static BasicBlock PrepareMerge(BasicBlock from, BasicBlock to)
+ {
+ BasicBlock fromPred = from.Predecessors.Count == 1 ? from.Predecessors[0] : null;
+
+ // If the block is empty, we can try to append to the predecessor and avoid unnecessary jumps.
+ if (from.Operations.Count == 0 && fromPred != null)
+ {
+ for (int i = 0; i < fromPred.SuccessorsCount; i++)
+ {
+ if (fromPred.GetSuccessor(i) == from)
+ {
+ fromPred.SetSuccessor(i, to);
+ }
+ }
+
+ // NOTE: `from` becomes unreachable and the call to `cfg.Update()` will remove it.
+ return fromPred;
+ }
+ else
+ {
+ from.AddSuccessor(to);
+
+ return from;
+ }
+ }
+ }
+}