Commit f6750fd8 authored by Matthias Braun's avatar Matthias Braun
Browse files

- implement output constraint enforcement for new register allocator

- rename bespilloptions.* to bespill.* and bespill.* to bespillutil.*

[r26327]
parent 47b32a33
......@@ -32,6 +32,7 @@
#include "beinfo.h"
#include "ircons_t.h"
#include "irnode_t.h"
#include "irop_t.h"
#include "bitset.h"
#include "pset.h"
......@@ -65,7 +66,6 @@ static inline const arch_irn_ops_t *get_irn_ops(const ir_node *irn)
ops = get_irn_op(irn);
be_ops = get_op_ops(ops)->be_ops;
assert(be_ops);
return be_ops;
}
......@@ -78,7 +78,7 @@ const arch_register_req_t *arch_get_register_req(const ir_node *irn, int pos)
pos = -1-get_Proj_proj(irn);
irn = get_Proj_pred(irn);
}
ops = get_irn_ops(irn);
ops = get_irn_ops_simple(irn);
if (pos < 0) {
return ops->get_irn_reg_req_out(irn, -pos-1);
} else {
......
......@@ -32,6 +32,7 @@
#include "bitset.h"
#include "obst.h"
#include "raw_bitset.h"
#include "irop_t.h"
#include "be_types.h"
#include "beinfo.h"
......@@ -109,6 +110,10 @@ void arch_perform_memory_operand(ir_node *irn, ir_node *spill, unsign
/**
* Get the register requirements for a node.
* @note Deprecated API! Preferably use
* arch_get_in_register_req and
* arch_get_out_register_req.
*
* @param irn The node.
* @param pos The position of the operand you're interested in.
* @return A pointer to the register requirements. If NULL is returned, the
......@@ -740,4 +745,32 @@ static inline bool arch_irn_is_ignore(const ir_node *irn)
return !!(req->type & arch_register_req_type_ignore);
}
static inline const arch_irn_ops_t *get_irn_ops_simple(const ir_node *node)
{
const ir_op *ops = get_irn_op(node);
const arch_irn_ops_t *be_ops = get_op_ops(ops)->be_ops;
assert(!is_Proj(node));
return be_ops;
}
/**
* Get register constraints for an operand at position @p
*/
static inline const arch_register_req_t *arch_get_in_register_req(
const ir_node *node, int pos)
{
const arch_irn_ops_t *ops = get_irn_ops_simple(node);
return ops->get_irn_reg_req_in(node, pos);
}
/**
* Get register constraint for a produced result (the @p pos result)
*/
static inline const arch_register_req_t *arch_get_out_register_req(
const ir_node *node, int pos)
{
const arch_irn_ops_t *ops = get_irn_ops_simple(node);
return ops->get_irn_reg_req_out(node, pos);
}
#endif /* FIRM_BE_BEARCH_H */
......@@ -72,7 +72,7 @@
#include "beirg.h"
#include "bespillslots.h"
#include "bespilloptions.h"
#include "bespill.h"
#include "belower.h"
#include "becopystat.h"
......
......@@ -72,7 +72,7 @@
#include "beirg.h"
#include "benode_t.h"
#include "bespill.h"
#include "bespilloptions.h"
#include "bespillutil.h"
#include "beverify.h"
#include "bipartite.h"
......@@ -730,6 +730,41 @@ static void free_last_uses(ir_nodeset_t *live_nodes, ir_node *node)
}
}
/**
* Create a bitset of registers occupied with value living through an
* instruction
*/
static void determine_live_through_regs(unsigned *bitset, ir_node *node)
{
const allocation_info_t *info = get_allocation_info(node);
unsigned r;
int i;
int arity;
/* mark all used registers as potentially live-through */
for (r = 0; r < n_regs; ++r) {
const assignment_t *assignment = &assignments[r];
if (assignment->value == NULL)
continue;
rbitset_set(bitset, r);
}
/* remove registers of value dying at the instruction */
arity = get_irn_arity(node);
for (i = 0; i < arity; ++i) {
ir_node *op;
const arch_register_t *reg;
if (!rbitset_is_set(&info->last_uses, i))
continue;
op = get_irn_n(node, i);
reg = arch_get_irn_register(op);
rbitset_clear(bitset, arch_register_get_index(reg));
}
}
/**
* Enforce constraints at a node by live range splits.
*
......@@ -769,9 +804,62 @@ static void enforce_constraints(ir_nodeset_t *live_nodes, ir_node *node)
}
}
/* construct a list of register occupied by live-through values */
unsigned *live_through_regs = NULL;
unsigned *output_regs = NULL;
/* is any of the live-throughs using a constrainted output register? */
if (get_irn_mode(node) == mode_T) {
const ir_edge_t *edge;
foreach_out_edge(node, edge) {
ir_node *proj = get_edge_src_irn(edge);
const arch_register_req_t *req;
if (!arch_irn_consider_in_reg_alloc(cls, proj))
continue;
req = arch_get_register_req_out(proj);
if (! (req->type & arch_register_req_type_limited))
continue;
if (live_through_regs == NULL) {
rbitset_alloca(live_through_regs, n_regs);
determine_live_through_regs(live_through_regs, node);
rbitset_alloca(output_regs, n_regs);
}
rbitset_or(output_regs, req->limited, n_regs);
if (rbitsets_have_common(req->limited, live_through_regs, n_regs)) {
good = false;
break;
}
}
} else {
if (arch_irn_consider_in_reg_alloc(cls, node)) {
const arch_register_req_t *req = arch_get_register_req_out(node);
if (req->type & arch_register_req_type_limited) {
rbitset_alloca(live_through_regs, n_regs);
determine_live_through_regs(live_through_regs, node);
if (rbitsets_have_common(req->limited, live_through_regs, n_regs)) {
good = false;
rbitset_alloca(output_regs, n_regs);
rbitset_or(output_regs, req->limited, n_regs);
}
}
}
}
if (good)
return;
if (live_through_regs == NULL) {
rbitset_alloca(live_through_regs, n_regs);
rbitset_alloca(output_regs, n_regs);
}
/* swap values around */
bp = hungarian_new(n_regs, n_regs, HUNGARIAN_MATCH_PERFECT);
......@@ -785,6 +873,10 @@ static void enforce_constraints(ir_nodeset_t *live_nodes, ir_node *node)
for (r = 0; r < n_regs; ++r) {
if (bitset_is_set(ignore_regs, r))
continue;
/* livethrough values may not use constrainted output registers */
if (rbitset_is_set(live_through_regs, l)
&& rbitset_is_set(output_regs, r))
continue;
hungarian_add(bp, l, r, l == r ? 90 : 89);
}
......
This diff is collapsed.
......@@ -19,157 +19,55 @@
/**
* @file
* @brief higher level abstraction for the creation of spill and reload
* instructions and rematerialisation of values.
* @author Daniel Grund, Sebastian Hack, Matthias Braun
* @date 29.09.2005
* @brief Option handling for spiller.
* @author Matthias Braun
* @date 12.10.2006
* @version $Id$
*/
#ifndef FIRM_BE_BESPILL_H
#define FIRM_BE_BESPILL_H
#include "firm_types.h"
#include "debug.h"
#include "bearch.h"
#include "beirg.h"
typedef struct spill_env_t spill_env_t;
/**
* Creates a new spill environment.
*/
spill_env_t *be_new_spill_env(be_irg_t *birg);
extern int be_coalesce_spill_slots;
extern int be_do_remats;
/**
* Deletes a spill environment.
* An entry in the list of spill-algorithms.
*/
void be_delete_spill_env(spill_env_t *senv);
typedef struct be_spiller_t {
/**
* The spill function.
*
* @param birg the graph to spill on
* @param cls the register class to spill
*/
void (*spill)(be_irg_t *birg, const arch_register_class_t *cls);
} be_spiller_t;
/**
* Return the last control flow node of a block.
*/
ir_node *be_get_end_of_block_insertion_point(const ir_node *block);
/**
* Marks a point until which a node must be spilled.
*/
void be_add_spill(spill_env_t *senv, ir_node *to_spill, ir_node *after);
/**
* Inserts a new entry into the list of reloads to place (the real nodes will
* be created when be_insert_spills_reloads is run). You don't have to
* explicitly create spill nodes, they will be created automatically after
* the definition of a value as soon as a reload is created. (we should add a
* possibility for explicit spill placement in the future)
* Register a new spill algorithm.
*
* @param senv The spill environment
* @param to_spill The node which is about to be spilled
* @param before The node before the reload should be added
* @param reload_cls The register class the reloaded value will be put into
* @param allow_remat Set to 1 if the node may be rematerialized instead of
* reloaded
*/
void be_add_reload(spill_env_t *senv, ir_node *to_spill, ir_node *before,
const arch_register_class_t *reload_cls, int allow_remat);
void be_add_reload2(spill_env_t *senv, ir_node *to_spill, ir_node *before, ir_node *can_spill_after,
const arch_register_class_t *reload_cls, int allow_remat);
/**
* Add a reload at the end of a block.
* Similar to be_add_reload_on_edge().
*/
void be_add_reload_at_end(spill_env_t *env, ir_node *to_spill, const ir_node *block,
const arch_register_class_t *reload_cls,
int allow_remat);
/**
* Analog to be_add_reload, but places the reload "on an edge" between 2 blocks
* @see be_add_reload
* @param name the name of the spill algorithm,
* used to select it
* @param spiller a spill entry
*/
void be_add_reload_on_edge(spill_env_t *senv, ir_node *to_spill, ir_node *bl,
int pos, const arch_register_class_t *reload_cls,
int allow_remat);
void be_register_spiller(const char *name, be_spiller_t *spiller);
/**
* Analog to be_add_reload but adds an already created rematerialized node.
*/
void be_add_remat(spill_env_t *env, ir_node *to_spill, ir_node *before,
ir_node *rematted_node);
/**
* The main function that places real spills/reloads (or rematerializes values)
* for all values where be_add_reload was called. It then rebuilds the
* SSA-form and updates liveness information
*/
void be_insert_spills_reloads(spill_env_t *senv);
/**
* There are 2 possibilities to spill a phi node: Only it's value, or replacing
* the whole phi-node with a memory phi. Normally only the value of a phi will
* be spilled unless you mark the phi with be_spill_phi.
* (Remember that each phi needs a register, so you have to spill phis when
* there are more phis than registers in a block)
*/
void be_spill_phi(spill_env_t *env, ir_node *node);
/**
* Returns the estimated costs if a node would ge spilled. This does only return
* the costs for the spill instructions, not the costs for needed reload
* instructions. The value is weighted by the estimated execution frequency of
* the spill.
*/
double be_get_spill_costs(spill_env_t *env, ir_node *to_spill, ir_node *before);
/**
* Returns the estimated costs if a node would get reloaded at a specific place
* This returns the costs for a reload instructions, or when possible the costs
* for a rematerialisation. The value is weighted by the estimated execution
* frequency of the reload/rematerialisation.
*/
double be_get_reload_costs(spill_env_t *env, ir_node *to_spill,
ir_node *before);
unsigned be_get_reload_costs_no_weight(spill_env_t *env, const ir_node *to_spill,
const ir_node *before);
/**
* Analog to be_get_reload_costs but returns the cost if the reload would be
* placed "on an edge" between 2 blocks
*/
double be_get_reload_costs_on_edge(spill_env_t *env, ir_node *to_spill,
ir_node *block, int pos);
typedef struct {
unsigned n_spills;
unsigned n_reloads;
double spill_costs;
double reload_costs;
} be_total_spill_costs_t;
/**
* Insert a spill after the definition of the given node if there is a reload that is not dominated by some spill.
* This function checks whether there is a reload that is not dominated by some spill for that node.
* If so, it inserts a spill right after the definition of the node.
* @param env The spill environment.
* @param irn The node to check for.
*/
void make_spill_locations_dominate_irn(spill_env_t *env, ir_node *irn);
/**
* Collect spill/reload cost statistics for a graph.
* @param birg The backend graph.
* @param costs A struct which will be filled with the costs.
* Execute the selected spill algorithm
*
* @param birg the graph to spill on
* @param cls the register class to spill
*/
void be_get_total_spill_costs(be_irg_t *birg, be_total_spill_costs_t *costs);
void be_do_spill(be_irg_t *birg, const arch_register_class_t *cls);
/**
* Check, if a node is rematerializable.
* @param env The spill env.
* Adds additional copies, so constraints needing additional registers to be
* solved correctly induce the additional register pressure.
*/
int be_is_rematerializable(spill_env_t *env, const ir_node *to_remat, const ir_node *before);
void be_pre_spill_prepare_constr(be_irg_t *birg,
const arch_register_class_t *cls);
#endif
......@@ -48,10 +48,10 @@
#include "belive_t.h"
#include "benode_t.h"
#include "bechordal_t.h"
#include "bespilloptions.h"
#include "bespill.h"
#include "beloopana.h"
#include "beirg.h"
#include "bespill.h"
#include "bespillutil.h"
#include "bemodule.h"
#define DBG_SPILL 1
......
......@@ -60,11 +60,11 @@
#include "belive_t.h"
#include "benode_t.h"
#include "bechordal_t.h"
#include "bespilloptions.h"
#include "bespill.h"
#include "beloopana.h"
#include "beirg.h"
#include "bemodule.h"
#include "bespill.h"
#include "bespillutil.h"
#include "lc_opts.h"
#include "lc_opts_enum.h"
......
......@@ -47,7 +47,7 @@
#include "bemodule.h"
#include "bespill.h"
#include "beutil.h"
#include "bespilloptions.h"
#include "bespillutil.h"
#include "besched.h"
#include "be_t.h"
......
......@@ -44,8 +44,8 @@
#include "error.h"
#include "beirg.h"
#include "bespilloptions.h"
#include "bespill.h"
#include "bespillutil.h"
#include "bemodule.h"
#include "besched.h"
#include "bearch.h"
......
/*
* Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
*
* This file is part of libFirm.
*
* This file may be distributed and/or modified under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation and appearing in the file LICENSE.GPL included in the
* packaging of this file.
*
* Licensees holding valid libFirm Professional Edition licenses may use
* this file in accordance with the libFirm Commercial License.
* Agreement provided with the Software.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*/
/**
* @file
* @brief Option handling for spiller.
* @author Daniel Grund, Sebastian Hack, Matthias Braun
* @date 29.09.2005
* @version $Id$
*/
#include "config.h"
#include "irtools.h"
#include "debug.h"
#include "iredges_t.h"
#include "adt/raw_bitset.h"
#include "statev.h"
#include "irgwalk.h"
#include "bespilloptions.h"
#include "bemodule.h"
#include "be.h"
#include "belive_t.h"
#include "beirg.h"
#include "bearch.h"
#include "benode_t.h"
#include "besched.h"
#include "bera.h"
#include "beintlive_t.h"
#include "lc_opts.h"
#include "lc_opts_enum.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
typedef struct be_pre_spill_env_t {
be_irg_t *birg;
const arch_register_class_t *cls;
} be_pre_spill_env_t;
static void prepare_constr_insn(be_pre_spill_env_t *env, ir_node *node)
{
const arch_register_class_t *cls = env->cls;
ir_node *block = get_nodes_block(node);
const be_irg_t *birg = env->birg;
be_lv_t *lv = birg->lv;
unsigned *tmp = NULL;
unsigned *def_constr = NULL;
int arity = get_irn_arity(node);
int i, i2;
/* Insert a copy for constraint inputs attached to a value which can't
* fullfil the constraint
* (typical example: stack pointer as input to copyb)
* TODO: This really just checks precolored registers at the moment and
* ignore the general case of not matching in/out constraints
*/
for (i = 0; i < arity; ++i) {
ir_node *op = get_irn_n(node, i);
ir_node *copy;
const arch_register_t *reg;
const arch_register_req_t *req;
req = arch_get_register_req(node, i);
if (req->cls != cls)
continue;
reg = arch_get_irn_register(op);
if (reg == NULL)
continue;
/* precolored with an ignore register (which is not a joker like
unknown/noreg) */
if (arch_register_type_is(reg, joker)
|| !arch_register_type_is(reg, ignore))
continue;
if (! (req->type & arch_register_req_type_limited))
continue;
if (rbitset_is_set(req->limited, reg->index))
continue;
copy = be_new_Copy(cls, block, op);
stat_ev_int("constr_copy", 1);
sched_add_before(node, copy);
set_irn_n(node, i, copy);
DBG((dbg, LEVEL_3, "inserting ignore arg copy %+F for %+F pos %d\n", copy, node, i));
}
/* insert copies for nodes that occur constrained more than once. */
for (i = 0; i < arity; ++i) {
ir_node *in;
ir_node *copy;
const arch_register_req_t *req;
req = arch_get_register_req(node, i);
if (req->cls != cls)
continue;
if (! (req->type & arch_register_req_type_limited))
continue;
in = get_irn_n(node, i);
if (!arch_irn_consider_in_reg_alloc(cls, in))
continue;
for (i2 = i + 1; i2 < arity; ++i2) {
ir_node *in2;
const arch_register_req_t *req2;
req2 = arch_get_register_req(node, i2);
if (req2->cls != cls)
continue;
if (! (req2->type & arch_register_req_type_limited))
continue;
in2 = get_irn_n(node, i2);
if (in2 != in)
continue;
/* if the constraint is the same, no copy is necessary
* TODO generalise unequal but overlapping constraints */
if (rbitset_equal(req->limited, req2->limited, cls->n_regs))
continue;
#if 0
/* Matze: looks fishy to me disabled it for now */
if (be_is_Copy(get_irn_n(insn->irn, a_op->pos)))
continue;
#endif
copy = be_new_Copy(cls, block, in);
stat_ev_int("constr_copy", 1);
sched_add_before(node, copy);
set_irn_n(node, i2, copy);
DBG((dbg, LEVEL_3,
"inserting multiple constr copy %+F for %+F pos %d\n",
copy, node, i2));
}
}
/* collect all registers occurring in out constraints. */
if (get_irn_mode(node) == mode_T) {
const ir_edge_t *edge;
foreach_out_edge(node, edge) {
ir_node *proj = get_edge_src_irn(edge);
const arch_register_req_t *req = arch_get_register_req_out(proj);
if (! (req->type & arch_register_req_type_limited))
continue;
if (def_constr == NULL) {
rbitset_alloca(def_constr, cls->n_regs);
}
rbitset_or(def_constr, req->limited, cls->n_regs);
}
} else {
const arch_register_req_t *req = arch_get_register_req_out(node);
if (req->type & arch_register_req_type_limited) {
rbitset_alloca(def_constr, cls->n_regs);
rbitset_or(def_constr, req->limited, cls->n_regs);
}
}
/* no output constraints => we're good */