aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/CodeGen/Optimizations
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/ARMeilleure/CodeGen/Optimizations
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/ARMeilleure/CodeGen/Optimizations')
-rw-r--r--src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs72
-rw-r--r--src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs346
-rw-r--r--src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs252
-rw-r--r--src/ARMeilleure/CodeGen/Optimizations/Simplification.cs183
-rw-r--r--src/ARMeilleure/CodeGen/Optimizations/TailMerge.cs83
5 files changed, 936 insertions, 0 deletions
diff --git a/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs b/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs
new file mode 100644
index 00000000..9e243d37
--- /dev/null
+++ b/src/ARMeilleure/CodeGen/Optimizations/BlockPlacement.cs
@@ -0,0 +1,72 @@
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System.Diagnostics;
+using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+
+namespace ARMeilleure.CodeGen.Optimizations
+{
+ static class BlockPlacement
+ {
+ public static void RunPass(ControlFlowGraph cfg)
+ {
+ bool update = false;
+
+ BasicBlock block;
+ BasicBlock nextBlock;
+
+ BasicBlock lastBlock = cfg.Blocks.Last;
+
+ // Move cold blocks at the end of the list, so that they are emitted away from hot code.
+ for (block = cfg.Blocks.First; block != null; block = nextBlock)
+ {
+ nextBlock = block.ListNext;
+
+ if (block.Frequency == BasicBlockFrequency.Cold)
+ {
+ cfg.Blocks.Remove(block);
+ cfg.Blocks.AddLast(block);
+ }
+
+ if (block == lastBlock)
+ {
+ break;
+ }
+ }
+
+ for (block = cfg.Blocks.First; block != null; block = nextBlock)
+ {
+ nextBlock = block.ListNext;
+
+ if (block.SuccessorsCount == 2)
+ {
+ Operation branchOp = block.Operations.Last;
+
+ Debug.Assert(branchOp.Instruction == Instruction.BranchIf);
+
+ BasicBlock falseSucc = block.GetSuccessor(0);
+ BasicBlock trueSucc = block.GetSuccessor(1);
+
+ // If true successor is next block in list, invert the condition. We avoid extra branching by
+ // making the true side the fallthrough (i.e, convert it to the false side).
+ if (trueSucc == block.ListNext)
+ {
+ Comparison comp = (Comparison)branchOp.GetSource(2).AsInt32();
+ Comparison compInv = comp.Invert();
+
+ branchOp.SetSource(2, Const((int)compInv));
+
+ block.SetSuccessor(0, trueSucc);
+ block.SetSuccessor(1, falseSucc);
+
+ update = true;
+ }
+ }
+ }
+
+ if (update)
+ {
+ cfg.Update();
+ }
+ }
+ }
+}
diff --git a/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs b/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs
new file mode 100644
index 00000000..c5a22a53
--- /dev/null
+++ b/src/ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs
@@ -0,0 +1,346 @@
+using ARMeilleure.IntermediateRepresentation;
+using System;
+using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+
+namespace ARMeilleure.CodeGen.Optimizations
+{
+ static class ConstantFolding
+ {
+ public static void RunPass(Operation operation)
+ {
+ if (operation.Destination == default || operation.SourcesCount == 0)
+ {
+ return;
+ }
+
+ if (!AreAllSourcesConstant(operation))
+ {
+ return;
+ }
+
+ OperandType type = operation.Destination.Type;
+
+ switch (operation.Instruction)
+ {
+ case Instruction.Add:
+ if (operation.GetSource(0).Relocatable ||
+ operation.GetSource(1).Relocatable)
+ {
+ break;
+ }
+
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x + y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x + y);
+ }
+ break;
+
+ case Instruction.BitwiseAnd:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x & y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x & y);
+ }
+ break;
+
+ case Instruction.BitwiseExclusiveOr:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x ^ y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x ^ y);
+ }
+ break;
+
+ case Instruction.BitwiseNot:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => ~x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => ~x);
+ }
+ break;
+
+ case Instruction.BitwiseOr:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x | y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x | y);
+ }
+ break;
+
+ case Instruction.ConvertI64ToI32:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => x);
+ }
+ break;
+
+ case Instruction.Compare:
+ if (type == OperandType.I32 &&
+ operation.GetSource(0).Type == type &&
+ operation.GetSource(1).Type == type)
+ {
+ switch ((Comparison)operation.GetSource(2).Value)
+ {
+ case Comparison.Equal:
+ EvaluateBinaryI32(operation, (x, y) => x == y ? 1 : 0);
+ break;
+ case Comparison.NotEqual:
+ EvaluateBinaryI32(operation, (x, y) => x != y ? 1 : 0);
+ break;
+ case Comparison.Greater:
+ EvaluateBinaryI32(operation, (x, y) => x > y ? 1 : 0);
+ break;
+ case Comparison.LessOrEqual:
+ EvaluateBinaryI32(operation, (x, y) => x <= y ? 1 : 0);
+ break;
+ case Comparison.GreaterUI:
+ EvaluateBinaryI32(operation, (x, y) => (uint)x > (uint)y ? 1 : 0);
+ break;
+ case Comparison.LessOrEqualUI:
+ EvaluateBinaryI32(operation, (x, y) => (uint)x <= (uint)y ? 1 : 0);
+ break;
+ case Comparison.GreaterOrEqual:
+ EvaluateBinaryI32(operation, (x, y) => x >= y ? 1 : 0);
+ break;
+ case Comparison.Less:
+ EvaluateBinaryI32(operation, (x, y) => x < y ? 1 : 0);
+ break;
+ case Comparison.GreaterOrEqualUI:
+ EvaluateBinaryI32(operation, (x, y) => (uint)x >= (uint)y ? 1 : 0);
+ break;
+ case Comparison.LessUI:
+ EvaluateBinaryI32(operation, (x, y) => (uint)x < (uint)y ? 1 : 0);
+ break;
+ }
+ }
+ break;
+
+ case Instruction.Copy:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => x);
+ }
+ break;
+
+ case Instruction.Divide:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => y != 0 ? x / y : 0);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => y != 0 ? x / y : 0);
+ }
+ break;
+
+ case Instruction.DivideUI:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => y != 0 ? (int)((uint)x / (uint)y) : 0);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => y != 0 ? (long)((ulong)x / (ulong)y) : 0);
+ }
+ break;
+
+ case Instruction.Multiply:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x * y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x * y);
+ }
+ break;
+
+ case Instruction.Negate:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => -x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => -x);
+ }
+ break;
+
+ case Instruction.ShiftLeft:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x << y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x << (int)y);
+ }
+ break;
+
+ case Instruction.ShiftRightSI:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x >> y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x >> (int)y);
+ }
+ break;
+
+ case Instruction.ShiftRightUI:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => (int)((uint)x >> y));
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => (long)((ulong)x >> (int)y));
+ }
+ break;
+
+ case Instruction.SignExtend16:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => (short)x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => (short)x);
+ }
+ break;
+
+ case Instruction.SignExtend32:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => (int)x);
+ }
+ break;
+
+ case Instruction.SignExtend8:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => (sbyte)x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => (sbyte)x);
+ }
+ break;
+
+ case Instruction.ZeroExtend16:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => (ushort)x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => (ushort)x);
+ }
+ break;
+
+ case Instruction.ZeroExtend32:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => (uint)x);
+ }
+ break;
+
+ case Instruction.ZeroExtend8:
+ if (type == OperandType.I32)
+ {
+ EvaluateUnaryI32(operation, (x) => (byte)x);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateUnaryI64(operation, (x) => (byte)x);
+ }
+ break;
+
+ case Instruction.Subtract:
+ if (type == OperandType.I32)
+ {
+ EvaluateBinaryI32(operation, (x, y) => x - y);
+ }
+ else if (type == OperandType.I64)
+ {
+ EvaluateBinaryI64(operation, (x, y) => x - y);
+ }
+ break;
+ }
+ }
+
+ private static bool AreAllSourcesConstant(Operation operation)
+ {
+ for (int index = 0; index < operation.SourcesCount; index++)
+ {
+ Operand srcOp = operation.GetSource(index);
+
+ if (srcOp.Kind != OperandKind.Constant)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static void EvaluateUnaryI32(Operation operation, Func<int, int> op)
+ {
+ int x = operation.GetSource(0).AsInt32();
+
+ operation.TurnIntoCopy(Const(op(x)));
+ }
+
+ private static void EvaluateUnaryI64(Operation operation, Func<long, long> op)
+ {
+ long x = operation.GetSource(0).AsInt64();
+
+ operation.TurnIntoCopy(Const(op(x)));
+ }
+
+ private static void EvaluateBinaryI32(Operation operation, Func<int, int, int> op)
+ {
+ int x = operation.GetSource(0).AsInt32();
+ int y = operation.GetSource(1).AsInt32();
+
+ operation.TurnIntoCopy(Const(op(x, y)));
+ }
+
+ private static void EvaluateBinaryI64(Operation operation, Func<long, long, long> op)
+ {
+ long x = operation.GetSource(0).AsInt64();
+ long y = operation.GetSource(1).AsInt64();
+
+ operation.TurnIntoCopy(Const(op(x, y)));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs
new file mode 100644
index 00000000..a45bb455
--- /dev/null
+++ b/src/ARMeilleure/CodeGen/Optimizations/Optimizer.cs
@@ -0,0 +1,252 @@
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+using System.Diagnostics;
+using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+
+namespace ARMeilleure.CodeGen.Optimizations
+{
+ static class Optimizer
+ {
+ public static void RunPass(ControlFlowGraph cfg)
+ {
+ // Scratch buffer used to store uses.
+ Span<Operation> buffer = default;
+
+ bool modified;
+
+ do
+ {
+ modified = false;
+
+ for (BasicBlock block = cfg.Blocks.Last; block != null; block = block.ListPrevious)
+ {
+ Operation node;
+ Operation prevNode;
+
+ for (node = block.Operations.Last; node != default; node = prevNode)
+ {
+ prevNode = node.ListPrevious;
+
+ if (IsUnused(node))
+ {
+ RemoveNode(block, node);
+
+ modified = true;
+
+ continue;
+ }
+ else if (node.Instruction == Instruction.Phi)
+ {
+ continue;
+ }
+
+ ConstantFolding.RunPass(node);
+ Simplification.RunPass(node);
+
+ if (DestIsSingleLocalVar(node))
+ {
+ if (IsPropagableCompare(node))
+ {
+ modified |= PropagateCompare(ref buffer, node);
+
+ if (modified && IsUnused(node))
+ {
+ RemoveNode(block, node);
+ }
+ }
+ else if (IsPropagableCopy(node))
+ {
+ PropagateCopy(ref buffer, node);
+
+ RemoveNode(block, node);
+
+ modified = true;
+ }
+ }
+ }
+ }
+ }
+ while (modified);
+ }
+
+ public static void RemoveUnusedNodes(ControlFlowGraph cfg)
+ {
+ bool modified;
+
+ do
+ {
+ modified = false;
+
+ for (BasicBlock block = cfg.Blocks.Last; block != null; block = block.ListPrevious)
+ {
+ Operation node;
+ Operation prevNode;
+
+ for (node = block.Operations.Last; node != default; node = prevNode)
+ {
+ prevNode = node.ListPrevious;
+
+ if (IsUnused(node))
+ {
+ RemoveNode(block, node);
+
+ modified = true;
+ }
+ }
+ }
+ }
+ while (modified);
+ }
+
+ private static bool PropagateCompare(ref Span<Operation> buffer, Operation compOp)
+ {
+ // Try to propagate Compare operations into their BranchIf uses, when these BranchIf uses are in the form
+ // of:
+ //
+ // - BranchIf %x, 0x0, Equal ;; i.e BranchIfFalse %x
+ // - BranchIf %x, 0x0, NotEqual ;; i.e BranchIfTrue %x
+ //
+ // The commutative property of Equal and NotEqual is taken into consideration as well.
+ //
+ // For example:
+ //
+ // %x = Compare %a, %b, comp
+ // BranchIf %x, 0x0, NotEqual
+ //
+ // =>
+ //
+ // BranchIf %a, %b, comp
+
+ static bool IsZeroBranch(Operation operation, out Comparison compType)
+ {
+ compType = Comparison.Equal;
+
+ if (operation.Instruction != Instruction.BranchIf)
+ {
+ return false;
+ }
+
+ Operand src1 = operation.GetSource(0);
+ Operand src2 = operation.GetSource(1);
+ Operand comp = operation.GetSource(2);
+
+ compType = (Comparison)comp.AsInt32();
+
+ return (src1.Kind == OperandKind.Constant && src1.Value == 0) ||
+ (src2.Kind == OperandKind.Constant && src2.Value == 0);
+ }
+
+ bool modified = false;
+
+ Operand dest = compOp.Destination;
+ Operand src1 = compOp.GetSource(0);
+ Operand src2 = compOp.GetSource(1);
+ Operand comp = compOp.GetSource(2);
+
+ Comparison compType = (Comparison)comp.AsInt32();
+
+ Span<Operation> uses = dest.GetUses(ref buffer);
+
+ foreach (Operation use in uses)
+ {
+ // If operation is a BranchIf and has a constant value 0 in its RHS or LHS source operands.
+ if (IsZeroBranch(use, out Comparison otherCompType))
+ {
+ Comparison propCompType;
+
+ if (otherCompType == Comparison.NotEqual)
+ {
+ propCompType = compType;
+ }
+ else if (otherCompType == Comparison.Equal)
+ {
+ propCompType = compType.Invert();
+ }
+ else
+ {
+ continue;
+ }
+
+ use.SetSource(0, src1);
+ use.SetSource(1, src2);
+ use.SetSource(2, Const((int)propCompType));
+
+ modified = true;
+ }
+ }
+
+ return modified;
+ }
+
+ private static void PropagateCopy(ref Span<Operation> buffer, Operation copyOp)
+ {
+ // Propagate copy source operand to all uses of the destination operand.
+ Operand dest = copyOp.Destination;
+ Operand source = copyOp.GetSource(0);
+
+ Span<Operation> uses = dest.GetUses(ref buffer);
+
+ foreach (Operation use in uses)
+ {
+ for (int index = 0; index < use.SourcesCount; index++)
+ {
+ if (use.GetSource(index) == dest)
+ {
+ use.SetSource(index, source);
+ }
+ }
+ }
+ }
+
+ private static void RemoveNode(BasicBlock block, Operation node)
+ {
+ // Remove a node from the nodes list, and also remove itself
+ // from all the use lists on the operands that this node uses.
+ block.Operations.Remove(node);
+
+ for (int index = 0; index < node.SourcesCount; index++)
+ {
+ node.SetSource(index, default);
+ }
+
+ Debug.Assert(node.Destination == default || node.Destination.UsesCount == 0);
+
+ node.Destination = default;
+ }
+
+ private static bool IsUnused(Operation node)
+ {
+ return DestIsSingleLocalVar(node) && node.Destination.UsesCount == 0 && !HasSideEffects(node);
+ }
+
+ private static bool DestIsSingleLocalVar(Operation node)
+ {
+ return node.DestinationsCount == 1 && node.Destination.Kind == OperandKind.LocalVariable;
+ }
+
+ private static bool HasSideEffects(Operation node)
+ {
+ return node.Instruction == Instruction.Call
+ || node.Instruction == Instruction.Tailcall
+ || node.Instruction == Instruction.CompareAndSwap
+ || node.Instruction == Instruction.CompareAndSwap16
+ || node.Instruction == Instruction.CompareAndSwap8;
+ }
+
+ private static bool IsPropagableCompare(Operation operation)
+ {
+ return operation.Instruction == Instruction.Compare;
+ }
+
+ private static bool IsPropagableCopy(Operation operation)
+ {
+ if (operation.Instruction != Instruction.Copy)
+ {
+ return false;
+ }
+
+ return operation.Destination.Type == operation.GetSource(0).Type;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs
new file mode 100644
index 00000000..a439d642
--- /dev/null
+++ b/src/ARMeilleure/CodeGen/Optimizations/Simplification.cs
@@ -0,0 +1,183 @@
+using ARMeilleure.IntermediateRepresentation;
+using System;
+using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+
+namespace ARMeilleure.CodeGen.Optimizations
+{
+ static class Simplification
+ {
+ public static void RunPass(Operation operation)
+ {
+ switch (operation.Instruction)
+ {
+ case Instruction.Add:
+ if (operation.GetSource(0).Relocatable ||
+ operation.GetSource(1).Relocatable)
+ {
+ break;
+ }
+
+ TryEliminateBinaryOpComutative(operation, 0);
+ break;
+
+ case Instruction.BitwiseAnd:
+ TryEliminateBitwiseAnd(operation);
+ break;
+
+ case Instruction.BitwiseOr:
+ TryEliminateBitwiseOr(operation);
+ break;
+
+ case Instruction.BitwiseExclusiveOr:
+ TryEliminateBitwiseExclusiveOr(operation);
+ break;
+
+ case Instruction.ConditionalSelect:
+ TryEliminateConditionalSelect(operation);
+ break;
+
+ case Instruction.Divide:
+ TryEliminateBinaryOpY(operation, 1);
+ break;
+
+ case Instruction.Multiply:
+ TryEliminateBinaryOpComutative(operation, 1);
+ break;
+
+ case Instruction.ShiftLeft:
+ case Instruction.ShiftRightSI:
+ case Instruction.ShiftRightUI:
+ case Instruction.Subtract:
+ TryEliminateBinaryOpY(operation, 0);
+ break;
+ }
+ }
+
+ private static void TryEliminateBitwiseAnd(Operation operation)
+ {
+ // Try to recognize and optimize those 3 patterns (in order):
+ // x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
+ // x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(x, AllOnes(x.Type)))
+ {
+ operation.TurnIntoCopy(y);
+ }
+ else if (IsConstEqual(y, AllOnes(y.Type)))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ else if (IsConstEqual(x, 0) || IsConstEqual(y, 0))
+ {
+ operation.TurnIntoCopy(Const(x.Type, 0));
+ }
+ }
+
+ private static void TryEliminateBitwiseOr(Operation operation)
+ {
+ // Try to recognize and optimize those 3 patterns (in order):
+ // x | 0x00000000 == x, 0x00000000 | y == y,
+ // x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(x, 0))
+ {
+ operation.TurnIntoCopy(y);
+ }
+ else if (IsConstEqual(y, 0))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ else if (IsConstEqual(x, AllOnes(x.Type)) || IsConstEqual(y, AllOnes(y.Type)))
+ {
+ operation.TurnIntoCopy(Const(AllOnes(x.Type)));
+ }
+ }
+
+ private static void TryEliminateBitwiseExclusiveOr(Operation operation)
+ {
+ // Try to recognize and optimize those 2 patterns (in order):
+ // x ^ y == 0x00000000 when x == y
+ // 0x00000000 ^ y == y, x ^ 0x00000000 == x
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (x == y && x.Type.IsInteger())
+ {
+ operation.TurnIntoCopy(Const(x.Type, 0));
+ }
+ else
+ {
+ TryEliminateBinaryOpComutative(operation, 0);
+ }
+ }
+
+ private static void TryEliminateBinaryOpY(Operation operation, ulong comparand)
+ {
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(y, comparand))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ }
+
+ private static void TryEliminateBinaryOpComutative(Operation operation, ulong comparand)
+ {
+ Operand x = operation.GetSource(0);
+ Operand y = operation.GetSource(1);
+
+ if (IsConstEqual(x, comparand))
+ {
+ operation.TurnIntoCopy(y);
+ }
+ else if (IsConstEqual(y, comparand))
+ {
+ operation.TurnIntoCopy(x);
+ }
+ }
+
+ private static void TryEliminateConditionalSelect(Operation operation)
+ {
+ Operand cond = operation.GetSource(0);
+
+ if (cond.Kind != OperandKind.Constant)
+ {
+ return;
+ }
+
+ // The condition is constant, we can turn it into a copy, and select
+ // the source based on the condition value.
+ int srcIndex = cond.Value != 0 ? 1 : 2;
+
+ Operand source = operation.GetSource(srcIndex);
+
+ operation.TurnIntoCopy(source);
+ }
+
+ private static bool IsConstEqual(Operand operand, ulong comparand)
+ {
+ if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger())
+ {
+ return false;
+ }
+
+ return operand.Value == comparand;
+ }
+
+ private static ulong AllOnes(OperandType type)
+ {
+ switch (type)
+ {
+ case OperandType.I32: return ~0U;
+ case OperandType.I64: return ~0UL;
+ }
+
+ throw new ArgumentException("Invalid operand type \"" + type + "\".");
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/CodeGen/Optimizations/TailMerge.cs b/src/ARMeilleure/CodeGen/Optimizations/TailMerge.cs
new file mode 100644
index 00000000..e94df159
--- /dev/null
+++ b/src/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 && fromPred.SuccessorsCount == 1)
+ {
+ 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;
+ }
+ }
+ }
+}