aboutsummaryrefslogtreecommitdiffhomepage
path: root/src-backend/analyze.c
blob: c1b33442d469b97ce5e321ae21d5a207bb608242 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

/*

  analyze.c -- IR analysis functions

  Copyright © 2023-2024 Samuel Lidén Borell <samuel@kodafritt.se>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

*/

#include <assert.h>

#include "include/csbe.h"
#include "csbe_internal.h"

#define IS_USED(var) \
    (((var)->flags & CSBEVD_INTERN_USED) != 0)
#define IS_SINGLE_EBB(var) \
    (((var)->flags & CSBEVD_INTERN_SINGLE_EBB) != 0)
#define HAS_ADDRESS(var) \
    (((var)->flags & CSBEVD_INTERN_HAS_ADDRESS) != 0)
#define IS_CROSS_CALL(var) \
    (((var)->flags & CSBEVD_INTERN_LIVE_ACROSS_CALL) != 0)
#define IS_FUNCPARAM(var) \
    (((var)->flags & CSBEVD_INTERN_FUNCPARAM) != 0)

/**
 * Allocates var-lanes to variables that are only used in a single
 * extened-basic-block (EBB).
 *
 * This is simple heuristic to find non-overlapping variables, that can
 * share var-lanes (and hence: registers).
 *
 * \param fb    Function body.
 * \param allow_cross_call
 *      Whether to allocate var-lanes to variables that live across a function
 *      call. This is used to assign different sets var-lanes depending on
 *      this property. This is later used by the codegens to decide whether to
 *      put in a caller-saved or callee-saved register.
 * \param next_free_varlane
 *      The first unallocated var-lane. Updated by the function.
 */
static void alloc_single_ebb_varlanes(struct FuncIR *fb,
                                      int allow_cross_call,
                                      unsigned *next_free_varlane)
{
    struct Var *var = fb->vars;
    unsigned first_free_varlane = *next_free_varlane;
    unsigned next_varlane = first_free_varlane;
    int i;
    for (i = fb->num_vars; i--; var++) {
        struct Ebb *ebb;
        unsigned lane;
        if (var->lane) continue;
        assert(!IS_FUNCPARAM(var));
        if (HAS_ADDRESS(var) || !IS_USED(var) || !IS_SINGLE_EBB(var))
            continue;
        if (!allow_cross_call && IS_CROSS_CALL(var)) continue;

        ebb = &fb->ebbs[var->ebb_id];
        lane = ebb->next_varlane;               /* Local allocation in EBB */
        if (!lane) lane = first_free_varlane;   /* First variable in EBB */
        var->lane = lane++;
        if (lane > next_varlane) next_varlane = lane; /* Track max value */
        ebb->next_varlane = lane;
        assert(next_varlane > first_free_varlane);
    }
    *next_free_varlane = next_varlane;
}

/**
 * De-assigns varlanes from variables (typically temporaries) that are used
 * over a function call. This way, they can be assigned a callee-saved
 * var-lane instead.
 */
static void demote_cross_call_vars(struct FuncIR *fb)
{
    struct Var *var = fb->vars;
    int i;
    for (i = fb->num_vars; i--; var++) {
        if (var->lane && IS_CROSS_CALL(var)) {
            var->lane = 0;
        }
    }
}

enum CsbeErr analyze_ir(struct Csbe *csbe, struct FuncIR *fb)
{
    /*
     * This is a very crude allocation of "varlanes" (i.e. abstract registers).
     * EBB flow analyzis and graph coloring could give better results, but
     * would also require more effort.
     */
    struct Var *var;
    unsigned next_varlane;  /**< Next free varlane for allocation */
    unsigned i;

    (void)csbe;

    /* Temporaries that are used cross-call lose their varlane,
       so they get a callee-saved varlane instead */
    demote_cross_call_vars(fb);

    /* Temporary variables will already have been allocated var-lanes at
       this point */
    next_varlane = fb->first_non_temporary_varlane;

    /* Variables that are only used in a single EBB, and NOT across a
       function call are allocated the next var-lanes. These use
       caller-saved registers (since there is no need to save them).

       This group of var-lanes typically include temporaries,
       unless they are used across a function call, e.g. y=x+f() */
    alloc_single_ebb_varlanes(fb, 0, &next_varlane);
    fb->first_callee_saved_varlane = next_varlane;

    /* Single-EBB variables that ARE used across function calls are allocated
       var-lanes now. These typically use callee-saved registers. */
    alloc_single_ebb_varlanes(fb, 1, &next_varlane);

    /* Assign lanes to all variables that don't need an address */
    /* TODO prioritize variables in the likely path */
    var = fb->vars;
    for (i = fb->num_vars; i--; var++) {
        if (var->lane) continue;
        if (HAS_ADDRESS(var) || !IS_USED(var))
            continue;
        var->lane = next_varlane++;
    }

    /* TODO Possible optimization: Score and sort the varlanes. Variables
            that are used many times (or in loops) could be prioritized. */

    /* For leaf functions, we can use caller-saved regs
       for multi-EBB variables too */
    if (!fb->contains_likely_calls) {
        fb->first_callee_saved_varlane = next_varlane;
    }

    /* Any other variables get the remaining (higher numbered) lanes */
    fb->first_addressable_varlane = next_varlane;
    var = fb->vars;
    for (i = fb->num_vars; i--; var++) {
        if (var->lane || !IS_USED(var)) continue;
        var->lane = next_varlane++;
    }
    return CSBEE_OK;
}