aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/CodeGen/Optimizations/TailMerge.cs
blob: e63c4da0d50d3f9470aceacfd00b806c2d59285a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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;
            }
        }
    }
}