--- /dev/null
+/*
+ * Copyright (c) 2019 Lima Project
+ *
+ * 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, sub license,
+ * 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT. 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 "ppir.h"
+
+static void
+ppir_liveness_setup_def_use(ppir_compiler *comp)
+{
+ list_for_each_entry(ppir_block, block, &comp->block_list, list) {
+ list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
+ for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++) {
+ ppir_node *node = instr->slots[i];
+ if (!node)
+ continue;
+ switch (node->op) {
+ case ppir_op_const:
+ continue;
+ default:
+ break;
+ }
+
+ for (int i = 0; i < ppir_node_get_src_num(node); i++) {
+ ppir_src *src = ppir_node_get_src(node, i);
+ if (!src)
+ continue;
+ ppir_reg *reg = ppir_src_get_reg(src);
+ if (!reg)
+ continue;
+
+ reg->live_in = MIN2(reg->live_in, instr->seq);
+ reg->live_out = MAX2(reg->live_out, instr->seq);
+
+ if (BITSET_TEST(block->def, reg->regalloc_index))
+ continue;
+ BITSET_SET(block->use, reg->regalloc_index);
+ }
+
+ ppir_dest *dest = ppir_node_get_dest(node);
+ if (!dest)
+ continue;
+ ppir_reg *reg = ppir_dest_get_reg(dest);
+ if (!reg)
+ continue;
+
+ reg->live_in = MIN2(reg->live_in, instr->seq);
+ reg->live_out = MAX2(reg->live_out, instr->seq);
+
+ if (BITSET_TEST(block->use, reg->regalloc_index))
+ continue;
+ BITSET_SET(block->def, reg->regalloc_index);
+ }
+ }
+ }
+}
+
+static bool
+ppir_liveness_setup_live_in_out(ppir_compiler *comp, int bitset_words)
+{
+ bool cont = false;
+ list_for_each_entry_rev(ppir_block, block, &comp->block_list, list) {
+ /* Update live_out: Any successor using the variable
+ * on entrance needs us to have the variable live on
+ * exit.
+ */
+ for (int i = 0; i < 2; i++) {
+ ppir_block *succ = block->successors[i];
+ if (!succ)
+ continue;
+ for (int i = 0; i < bitset_words; i++) {
+ BITSET_WORD new_live_out = (succ->live_in[i] &
+ ~block->live_out[i]);
+ if (new_live_out) {
+ block->live_out[i] |= new_live_out;
+ cont = true;
+ }
+ }
+ }
+ /* Update live_in */
+ for (int i = 0; i < bitset_words; i++) {
+ BITSET_WORD new_live_in = (block->use[i] |
+ (block->live_out[i] &
+ ~block->def[i]));
+ if (new_live_in & ~block->live_in[i]) {
+ block->live_in[i] |= new_live_in;
+ cont = true;
+ }
+ }
+ }
+
+ return cont;
+}
+
+static void
+ppir_liveness_compute_start_end(ppir_compiler *comp)
+{
+ list_for_each_entry(ppir_block, block, &comp->block_list, list) {
+ list_for_each_entry(ppir_reg, reg, &comp->reg_list, list) {
+ if (!list_length(&block->instr_list))
+ continue;
+
+ if (BITSET_TEST(block->live_in, reg->regalloc_index)) {
+ ppir_instr *first = list_first_entry(&block->instr_list,
+ ppir_instr, list);
+ reg->live_in = MIN2(reg->live_in, first->seq);
+ reg->live_out = MAX2(reg->live_out, first->seq);
+ }
+
+ if (BITSET_TEST(block->live_out, reg->regalloc_index)) {
+ ppir_instr *last = list_last_entry(&block->instr_list,
+ ppir_instr, list);
+ reg->live_in = MIN2(reg->live_in, last->seq);
+ reg->live_out = MAX2(reg->live_out, last->seq);
+ }
+ }
+ }
+}
+
+/* Liveness analysis is based on https://en.wikipedia.org/wiki/Live_variable_analysis
+ * 1) Compute def and use for each block. 'Def' is variables that are set
+ * before they are read in block, 'set' is variables that are read before
+ * they're set in the block. Initial live_in and live_out values are set
+ * accordingly.
+ * 2) Compute live_in and live_out of blocks:
+ * live_in(block) = use(block) + (live_out(block) - set(block))
+ * live_out(block) = live_in(successors[0]) + live_in(successors[1])
+ * Loop walks blocks in reverse order and computes live_in/live_out of each
+ * block, loop is terminated when no live_in or live_out is updated.
+ * 3) Adjust live_in and live_out of variables to block boundaries if they
+ * appear in live_in or live_out.
+ */
+void
+ppir_liveness_analysis(ppir_compiler *comp)
+{
+ int bitset_words = BITSET_WORDS(list_length(&comp->reg_list));
+
+ ppir_liveness_setup_def_use(comp);
+
+ while (ppir_liveness_setup_live_in_out(comp, bitset_words))
+ ;
+
+ ppir_liveness_compute_start_end(comp);
+}
return ret;
}
-static ppir_reg *get_src_reg(ppir_src *src)
-{
- switch (src->type) {
- case ppir_target_ssa:
- return src->ssa;
- case ppir_target_register:
- return src->reg;
- default:
- return NULL;
- }
-}
-
static void ppir_regalloc_update_reglist_ssa(ppir_compiler *comp)
{
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
}
}
-static ppir_reg *ppir_regalloc_build_liveness_info(ppir_compiler *comp)
-{
- ppir_reg *ret = NULL;
-
- list_for_each_entry(ppir_block, block, &comp->block_list, list) {
- list_for_each_entry(ppir_node, node, &block->node_list, list) {
- if (node->op == ppir_op_store_color) {
- ppir_store_node *store = ppir_node_to_store(node);
- if (store->src.type == ppir_target_ssa)
- ret = store->src.ssa;
- else
- ret = store->src.reg;
- ret->live_out = INT_MAX;
- continue;
- }
-
- if (!node->instr || node->op == ppir_op_const)
- continue;
-
- /* update reg live_in from node dest (write) */
- ppir_dest *dest = ppir_node_get_dest(node);
- if (dest) {
- ppir_reg *reg = NULL;
-
- if (dest->type == ppir_target_ssa) {
- reg = &dest->ssa;
- }
- else if (dest->type == ppir_target_register)
- reg = dest->reg;
-
- if (reg && node->instr->seq < reg->live_in)
- reg->live_in = node->instr->seq;
- }
-
- /* update reg live_out from node src (read) */
- for (int i = 0; i < ppir_node_get_src_num(node); i++)
- {
- ppir_reg *reg = get_src_reg(ppir_node_get_src(node, i));
- if (reg && node->instr->seq > reg->live_out)
- reg->live_out = node->instr->seq;
- }
- }
- }
-
- return ret;
-}
-
static int get_phy_reg_index(int reg)
{
int i;
ppir_dest *dest = ppir_node_get_dest(node);
ppir_reg *reg = NULL;
if (dest) {
- if (dest->type == ppir_target_ssa)
- reg = &dest->ssa;
- else if (dest->type == ppir_target_register)
- reg = dest->reg;
-
+ reg = ppir_dest_get_reg(dest);
if (reg == chosen)
ppir_update_spilled_dest(comp, block, node, dest);
}
ppir_alu_node *move_alu = NULL;
ppir_alu_node *alu = ppir_node_to_alu(node);
for (int i = 0; i < alu->num_src; i++) {
- reg = get_src_reg(alu->src + i);
+ reg = ppir_src_get_reg(alu->src + i);
if (reg == chosen) {
move_alu = ppir_update_spilled_src(comp, block, node,
alu->src + i, move_alu);
{
for (int i = 0; i < ppir_node_get_src_num(node); i++) {
ppir_src *src = ppir_node_get_src(node, i);
- reg = get_src_reg(src);
+ reg = ppir_src_get_reg(src);
if (reg == chosen) {
ppir_update_spilled_src(comp, block, node, src, NULL);
}
static void ppir_regalloc_reset_liveness_info(ppir_compiler *comp)
{
+ int bitset_words = BITSET_WORDS(list_length(&comp->reg_list));
+ int idx = 0;
+
list_for_each_entry(ppir_reg, reg, &comp->reg_list, list) {
reg->live_in = INT_MAX;
reg->live_out = 0;
+ reg->regalloc_index = idx++;
+ }
+
+ list_for_each_entry(ppir_block, block, &comp->block_list, list) {
+ if (block->def)
+ ralloc_free(block->def);
+ block->def = rzalloc_array(comp, BITSET_WORD, bitset_words);
+
+ if (block->use)
+ ralloc_free(block->use);
+ block->use = rzalloc_array(comp, BITSET_WORD, bitset_words);
+
+ if (block->live_in)
+ ralloc_free(block->live_in);
+ block->live_in = rzalloc_array(comp, BITSET_WORD, bitset_words);
+
+ if (block->live_out)
+ ralloc_free(block->live_out);
+ block->live_out = rzalloc_array(comp, BITSET_WORD, bitset_words);
}
}
static bool ppir_regalloc_prog_try(ppir_compiler *comp, bool *spilled)
{
- ppir_reg *end_reg;
-
ppir_regalloc_reset_liveness_info(comp);
- end_reg = ppir_regalloc_build_liveness_info(comp);
+
+ ppir_liveness_analysis(comp);
struct ra_graph *g = ra_alloc_interference_graph(
comp->ra, list_length(&comp->reg_list));
- int n = 0, end_reg_index = 0;
+ int n = 0;
list_for_each_entry(ppir_reg, reg, &comp->reg_list, list) {
int c = ppir_ra_reg_class_vec1 + (reg->num_components - 1);
if (reg->is_head)
c += 4;
- if (reg == end_reg)
- end_reg_index = n;
ra_set_node_class(g, n++, c);
}
n1++;
}
- ra_set_node_reg(g, end_reg_index, ppir_ra_reg_base[ppir_ra_reg_class_vec4]);
-
*spilled = false;
bool ok = ra_allocate(g);
if (!ok || (comp->force_spilling-- > 0)) {