aboutsummaryrefslogtreecommitdiff
path: root/rcc-legacy/src/mid/ifopt.cc
blob: c82fb4c929689dc6afde8a6ad532af21bf1ad5be (plain)
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* Source code for RCC-Legacy.
   Copyright (C) 2026 Richard Qin

This file is part of RCC.

RCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

RCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with RCC; see the file LICENSE.  If not see
<http://www.gnu.org/licenses/>.  */

/// @file ifopt.cc
/// @brief If-conversion: collapses if-then[-else] diamond CFG into straight-line block + sel
/// instructions.
///
/// See docs/ifopt.md for architectural notes.

#include "rcc/all.h"

namespace rcc {

namespace {

enum {
    MaxIns = 2,
    MaxPhis = 2,
};

bool okbranch(Blk* b) {
    int n = 0;
    for (Ins* i = b->ins; i < &b->ins[b->nins]; ++i) {
        if (i->op == Odbgloc)
            continue;
        if (pinned(i))
            return false;
        if (i->op != Onop)
            ++n;
    }
    return n <= MaxIns;
}

bool okjoin(Blk* b) {
    int n = 0;
    for (Phi* p = b->phi; p; p = p->link) {
        if (KBASE(p->cls) != 0)
            return false;
        ++n;
    }
    return n <= MaxPhis;
}

bool okgraph(Blk* ifb, Blk* thenb, Blk* elseb, Blk* joinb) {
    assert(thenb != elseb);
    return joinb->npred == 2 && okjoin(joinb) && (thenb == ifb || okbranch(thenb)) &&
           (elseb == ifb || okbranch(elseb));
}

void convert(Ctx* ctx, Blk* ifb, Blk* thenb, Blk* elseb, Blk* joinb) {
    Ins* ins = static_cast<Ins*>(vnew(ctx, 0, sizeof ins[0], PHeap));
    uint nins = 0;

    addbins(ctx, &ins, &nins, ifb);
    if (thenb != ifb)
        addbins(ctx, &ins, &nins, thenb);
    if (elseb != ifb)
        addbins(ctx, &ins, &nins, elseb);

    assert(joinb->npred == 2);
    Ins sel = {};
    if (joinb->phi) {
        sel.op = Osel0;
        sel.cls = static_cast<uint>(Kw);
        sel.arg[0] = ifb->jmp.arg;
        addins(ctx, &ins, &nins, &sel);
    }

    sel = {};
    sel.op = Osel1;
    for (Phi* p = joinb->phi; p; p = p->link) {
        sel.to = p->to;
        sel.cls = static_cast<uint>(p->cls);
        sel.arg[0] = phiarg(p, thenb);
        sel.arg[1] = phiarg(p, elseb);
        addins(ctx, &ins, &nins, &sel);
    }

    idup(ctx, ifb, ins, nins);
    ifb->jmp.type = Jjmp;
    ifb->jmp.arg = Ref::none();
    ifb->s1 = joinb;
    ifb->s2 = nullptr;
    joinb->npred = 1;
    joinb->pred[0] = ifb;
    joinb->phi = nullptr;
    vfree(ins);
}

}  // namespace

void ifconvert(Ctx* ctx, Fn* fn) {
    if (debug['K'])
        fputs("\n> If-conversion:\n", ctx->dbg_buf);

    Blk *thenb, *elseb, *joinb;
    for (Blk* ifb = fn->start; ifb; ifb = ifb->link)
        if (ifgraph(ifb, &thenb, &elseb, &joinb) && okgraph(ifb, thenb, elseb, joinb)) {
            if (debug['K'])
                fprintf(ctx->dbg_buf, "    @%s -> @%s, @%s -> @%s\n", ifb->name, thenb->name,
                        elseb->name, joinb->name);
            convert(ctx, ifb, thenb, elseb, joinb);
        }

    if (debug['K']) {
        fprintf(ctx->dbg_buf, "\n> After if-conversion:\n");
        printfn(fn, ctx->dbg_buf);
    }
}

}  // namespace rcc