+/*
+ * 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 Helper functions for handling ABI constraints in the code
+ * selection phase.
+ * @author Matthias Braun
+ * @version $Id$
+ */
+#include "config.h"
+
+#include "beabihelper.h"
+#include "bearch.h"
+#include "benode.h"
+#include "besched.h"
+#include "ircons.h"
+#include "iredges.h"
+#include "irgwalk.h"
+
+typedef struct reg_flag_t {
+ const arch_register_t *reg; /**< register at an input position.
+ may be NULL in case of memory input */
+ arch_irn_flags_t flags;
+} reg_flag_t;
+
+/**
+ * A register state mapping keeps track of the symbol values (=firm nodes)
+ * to registers. This is usefull when constructing straight line code
+ * which like the function prolog or epilog in some architectures.
+ */
+typedef struct register_state_mapping_t {
+ ir_node **value_map; /**< mapping of state indices to values */
+ int **reg_index_map; /**< mapping of regclass,regnum to an index
+ into the value_map */
+ reg_flag_t *regs; /**< registers (and memory values) that form a
+ state */
+ ir_node *last_barrier;
+} register_state_mapping_t;
+
+struct beabi_helper_env_t {
+ ir_graph *irg;
+ register_state_mapping_t prolog;
+ register_state_mapping_t epilog;
+};
+
+static void prepare_rsm(register_state_mapping_t *rsm,
+ const arch_env_t *arch_env)
+{
+ unsigned n_reg_classes = arch_env_get_n_reg_class(arch_env);
+ unsigned c;
+ reg_flag_t memory = { NULL, 0 };
+
+ rsm->regs = NEW_ARR_F(reg_flag_t, 0);
+ /* memory input at 0 */
+ ARR_APP1(reg_flag_t, rsm->regs, memory);
+
+ rsm->value_map = NULL;
+ rsm->reg_index_map = XMALLOCN(int*, n_reg_classes);
+ for (c = 0; c < n_reg_classes; ++c) {
+ const arch_register_class_t *cls = arch_env_get_reg_class(arch_env, c);
+ unsigned n_regs = arch_register_class_n_regs(cls);
+ unsigned r;
+
+ rsm->reg_index_map[c] = XMALLOCN(int, n_regs);
+ for (r = 0; r < n_regs; ++r) {
+ rsm->reg_index_map[c][r] = -1;
+ }
+ }
+}
+
+static void free_rsm(register_state_mapping_t *rsm, const arch_env_t *arch_env)
+{
+ unsigned n_reg_classes = arch_env_get_n_reg_class(arch_env);
+ unsigned c;
+
+ for (c = 0; c < n_reg_classes; ++c) {
+ free(rsm->reg_index_map[c]);
+ }
+
+ free(rsm->reg_index_map);
+ if (rsm->value_map != NULL)
+ DEL_ARR_F(rsm->value_map);
+ DEL_ARR_F(rsm->regs);
+
+ rsm->regs = NULL;
+ rsm->reg_index_map = NULL;
+ rsm->value_map = NULL;
+}
+
+static void rsm_clear_regs(register_state_mapping_t *rsm,
+ const arch_env_t *arch_env)
+{
+ unsigned n_reg_classes = arch_env_get_n_reg_class(arch_env);
+ unsigned c;
+ reg_flag_t memory = { NULL, 0 };
+
+ for (c = 0; c < n_reg_classes; ++c) {
+ const arch_register_class_t *cls = arch_env_get_reg_class(arch_env, c);
+ unsigned n_regs = arch_register_class_n_regs(cls);
+ unsigned r;
+
+ for (r = 0; r < n_regs; ++r) {
+ rsm->reg_index_map[c][r] = -1;
+ }
+ }
+ ARR_RESIZE(reg_flag_t, rsm->regs, 0);
+ ARR_APP1(reg_flag_t, rsm->regs, memory);
+
+ if (rsm->value_map != NULL) {
+ DEL_ARR_F(rsm->value_map);
+ rsm->value_map = NULL;
+ }
+}
+
+static void rsm_add_reg(register_state_mapping_t *rsm,
+ const arch_register_t *reg, arch_irn_flags_t flags)
+{
+ int input_idx = ARR_LEN(rsm->regs);
+ int cls_idx = reg->reg_class->index;
+ int reg_idx = reg->index;
+ reg_flag_t regflag = { reg, flags };
+
+ /* we must not have used get_value yet */
+ assert(rsm->reg_index_map[cls_idx][reg_idx] == -1);
+ rsm->reg_index_map[cls_idx][reg_idx] = input_idx;
+ ARR_APP1(reg_flag_t, rsm->regs, regflag);
+}
+
+
+static ir_node *rsm_get_value(register_state_mapping_t *rsm, int index)
+{
+ assert(index < ARR_LEN(rsm->value_map));
+ return rsm->value_map[index];
+}
+
+static ir_node *rsm_get_reg_value(register_state_mapping_t *rsm,
+ const arch_register_t *reg)
+{
+ int cls_idx = reg->reg_class->index;
+ int reg_idx = reg->index;
+ int input_idx = rsm->reg_index_map[cls_idx][reg_idx];
+
+ return rsm_get_value(rsm, input_idx);
+}
+
+static void rsm_set_value(register_state_mapping_t *rsm, int index,
+ ir_node *value)
+{
+ assert(index < ARR_LEN(rsm->value_map));
+ rsm->value_map[index] = value;
+}
+
+static void rsm_set_reg_value(register_state_mapping_t *rsm,
+ const arch_register_t *reg, ir_node *value)
+{
+ int cls_idx = reg->reg_class->index;
+ int reg_idx = reg->index;
+ int input_idx = rsm->reg_index_map[cls_idx][reg_idx];
+ rsm_set_value(rsm, input_idx, value);
+}
+
+static ir_node *rsm_create_barrier(register_state_mapping_t *rsm,
+ ir_node *block)
+{
+ int n_barrier_outs = ARR_LEN(rsm->regs);
+ ir_node **in = rsm->value_map;
+ ir_node *barrier;
+ int o;
+
+ assert(ARR_LEN(rsm->value_map) == n_barrier_outs);
+
+ barrier = be_new_Barrier(block, n_barrier_outs, in);
+
+ for (o = 0; o < n_barrier_outs; ++o) {
+ const reg_flag_t *regflag = &rsm->regs[o];
+ const arch_register_t *reg = regflag->reg;
+ ir_node *proj;
+ if (reg == NULL) {
+ arch_set_out_register_req(barrier, o, arch_no_register_req);
+ proj = new_r_Proj(barrier, mode_M, o);
+ } else {
+ be_set_constr_single_reg_in(barrier, o, reg, 0);
+ be_set_constr_single_reg_out(barrier, o, reg, regflag->flags);
+ proj = new_r_Proj(barrier, reg->reg_class->mode, o);
+ }
+ rsm->value_map[o] = proj;
+ }
+
+ rsm->last_barrier = barrier;
+
+ return barrier;
+}
+
+
+
+
+
+beabi_helper_env_t *be_abihelper_prepare(ir_graph *irg)
+{
+ const arch_env_t *arch_env = be_get_irg_arch_env(irg);
+ beabi_helper_env_t *env = XMALLOCZ(beabi_helper_env_t);
+
+ env->irg = irg;
+ prepare_rsm(&env->prolog, arch_env);
+ prepare_rsm(&env->epilog, arch_env);
+
+ return env;
+}
+
+void be_abihelper_finish(beabi_helper_env_t *env)
+{
+ const arch_env_t *arch_env = be_get_irg_arch_env(env->irg);
+
+ free_rsm(&env->prolog, arch_env);
+ if (env->epilog.reg_index_map != NULL) {
+ free_rsm(&env->epilog, arch_env);
+ }
+ free(env);
+}
+
+void be_prolog_add_reg(beabi_helper_env_t *env, const arch_register_t *reg,
+ arch_irn_flags_t flags)
+{
+ rsm_add_reg(&env->prolog, reg, flags);
+}
+
+ir_node *be_prolog_create_start(beabi_helper_env_t *env, dbg_info *dbgi,
+ ir_node *block)
+{
+ int n_start_outs = ARR_LEN(env->prolog.regs);
+ ir_node *start = be_new_Start(dbgi, block, n_start_outs);
+ int o;
+
+ assert(env->prolog.value_map == NULL);
+ env->prolog.value_map = NEW_ARR_F(ir_node*, n_start_outs);
+
+ for (o = 0; o < n_start_outs; ++o) {
+ const reg_flag_t *regflag = &env->prolog.regs[o];
+ const arch_register_t *reg = regflag->reg;
+ ir_node *proj;
+ if (reg == NULL) {
+ arch_set_out_register_req(start, o, arch_no_register_req);
+ proj = new_r_Proj(start, mode_M, o);
+ } else {
+ be_set_constr_single_reg_out(start, o, regflag->reg,
+ regflag->flags);
+ arch_irn_set_register(start, o, regflag->reg);
+ proj = new_r_Proj(start, reg->reg_class->mode, o);
+ }
+ env->prolog.value_map[o] = proj;
+ }
+
+ /* start node should really be the first thing constructed */
+ assert(env->prolog.last_barrier == NULL);
+ env->prolog.last_barrier = start;
+
+ return start;
+}
+
+ir_node *be_prolog_create_barrier(beabi_helper_env_t *env, ir_node *block)
+{
+ return rsm_create_barrier(&env->prolog, block);
+}
+
+ir_node *be_prolog_get_reg_value(beabi_helper_env_t *env,
+ const arch_register_t *reg)
+{
+ return rsm_get_reg_value(&env->prolog, reg);
+}
+
+ir_node *be_prolog_get_memory(beabi_helper_env_t *env)
+{
+ return rsm_get_value(&env->prolog, 0);
+}
+
+void be_prolog_set_reg_value(beabi_helper_env_t *env,
+ const arch_register_t *reg, ir_node *value)
+{
+ rsm_set_reg_value(&env->prolog, reg, value);
+}
+
+void be_prolog_set_memory(beabi_helper_env_t *env, ir_node *value)
+{
+ rsm_set_value(&env->prolog, 0, value);
+}
+
+
+
+void be_epilog_begin(beabi_helper_env_t *env)
+{
+ const arch_env_t *arch_env = be_get_irg_arch_env(env->irg);
+ rsm_clear_regs(&env->epilog, arch_env);
+ env->epilog.value_map = NEW_ARR_F(ir_node*, 1);
+ env->epilog.value_map[0] = NULL;
+}
+
+void be_epilog_add_reg(beabi_helper_env_t *env, const arch_register_t *reg,
+ arch_irn_flags_t flags, ir_node *value)
+{
+ rsm_add_reg(&env->epilog, reg, flags);
+ ARR_APP1(ir_node*, env->epilog.value_map, value);
+}
+
+void be_epilog_set_reg_value(beabi_helper_env_t *env,
+ const arch_register_t *reg, ir_node *value)
+{
+ rsm_set_reg_value(&env->epilog, reg, value);
+}
+
+void be_epilog_set_memory(beabi_helper_env_t *env, ir_node *value)
+{
+ rsm_set_value(&env->epilog, 0, value);
+}
+
+ir_node *be_epilog_get_reg_value(beabi_helper_env_t *env,
+ const arch_register_t *reg)
+{
+ return rsm_get_reg_value(&env->epilog, reg);
+}
+
+ir_node *be_epilog_get_memory(beabi_helper_env_t *env)
+{
+ return rsm_get_value(&env->epilog, 0);
+}
+
+ir_node *be_epilog_create_barrier(beabi_helper_env_t *env, ir_node *block)
+{
+ return rsm_create_barrier(&env->epilog, block);
+}
+
+ir_node *be_epilog_create_return(beabi_helper_env_t *env, dbg_info *dbgi,
+ ir_node *block)
+{
+ int n_return_in = ARR_LEN(env->epilog.regs);
+ ir_node **in = env->epilog.value_map;
+ int n_res = 1; /* TODO */
+ unsigned pop = 0; /* TODO */
+ int i;
+ ir_node *ret;
+
+ assert(ARR_LEN(env->epilog.value_map) == n_return_in);
+
+ ret = be_new_Return(dbgi, get_irn_irg(block), block, n_res, pop,
+ n_return_in, in);
+ for (i = 0; i < n_return_in; ++i) {
+ const reg_flag_t *regflag = &env->epilog.regs[i];
+ const arch_register_t *reg = regflag->reg;
+ if (reg != NULL) {
+ be_set_constr_single_reg_in(ret, i, reg, 0);
+ }
+ }
+
+ rsm_clear_regs(&env->epilog, be_get_irg_arch_env(env->irg));
+ env->epilog.last_barrier = NULL;
+
+ return ret;
+}
+
+static void add_missing_keep_walker(ir_node *node, void *data)
+{
+ int n_outs, i;
+ unsigned found_projs = 0;
+ const ir_edge_t *edge;
+ ir_mode *mode = get_irn_mode(node);
+ ir_node *last_keep;
+ (void) data;
+ if (mode != mode_T)
+ return;
+
+ n_outs = arch_irn_get_n_outs(node);
+ if (n_outs <= 0)
+ return;
+
+ assert(n_outs < (int) sizeof(unsigned) * 8);
+ foreach_out_edge(node, edge) {
+ ir_node *node = get_edge_src_irn(edge);
+ int pn;
+
+ /* The node could be kept */
+ if (is_End(node) || is_Anchor(node))
+ continue;
+
+ if (get_irn_mode(node) == mode_M)
+ continue;
+
+ pn = get_Proj_proj(node);
+ assert(pn < n_outs);
+ found_projs |= 1 << pn;
+ }
+
+
+ /* are keeps missing? */
+ last_keep = NULL;
+ for (i = 0; i < n_outs; ++i) {
+ ir_node *block;
+ ir_node *in[1];
+ const arch_register_req_t *req;
+ const arch_register_class_t *cls;
+
+ if (found_projs & (1 << i)) {
+ continue;
+ }
+
+ req = arch_get_out_register_req(node, i);
+ cls = req->cls;
+ if (cls == NULL) {
+ continue;
+ }
+
+ block = get_nodes_block(node);
+ in[0] = new_r_Proj(node, arch_register_class_mode(cls), i);
+ if (last_keep != NULL) {
+ be_Keep_add_node(last_keep, cls, in[0]);
+ } else {
+ last_keep = be_new_Keep(block, 1, in);
+ if (sched_is_scheduled(node)) {
+ sched_add_after(node, last_keep);
+ }
+ }
+ }
+}
+
+void be_add_missing_keeps(ir_graph *irg)
+{
+ irg_walk_graph(irg, add_missing_keep_walker, NULL, NULL);
+}