helper functions for doing custom abi construction in codeselection phase
authorMatthias Braun <matze@braunis.de>
Fri, 9 Jul 2010 16:17:06 +0000 (16:17 +0000)
committerMatthias Braun <matze@braunis.de>
Fri, 9 Jul 2010 16:17:06 +0000 (16:17 +0000)
[r27724]

ir/be/beabihelper.c [new file with mode: 0644]
ir/be/beabihelper.h [new file with mode: 0644]

diff --git a/ir/be/beabihelper.c b/ir/be/beabihelper.c
new file mode 100644 (file)
index 0000000..7cdd205
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * 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);
+}
diff --git a/ir/be/beabihelper.h b/ir/be/beabihelper.h
new file mode 100644 (file)
index 0000000..09f8a86
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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$
+ */
+#ifndef FIRM_BE_BEABI_HELPER_H
+#define FIRM_BE_BEABI_HELPER_H
+
+#include "firm_types.h"
+#include "be_types.h"
+
+typedef struct beabi_helper_env_t beabi_helper_env_t;
+
+beabi_helper_env_t *be_abihelper_prepare(ir_graph *irg);
+
+void be_abihelper_finish(beabi_helper_env_t *env);
+
+/**
+ * Mark a registers value at the beginning of the function as significant.
+ * This is necessary for things like:
+ *  - Callee-Save registers (we need to restore that value at the end)
+ *  - Parameters passed in registers
+ *  - stack pointer, base pointer, ...
+ * It is possible to specify additional irn flags (usefull to mark a value
+ * as ignore or produces_sp).
+ */
+void be_prolog_add_reg(beabi_helper_env_t *env, const arch_register_t *reg,
+                       arch_irn_flags_t flags);
+
+/**
+ * Creates a start node.
+ * Must be called after all prolog_add_reg calls
+ */
+ir_node *be_prolog_create_start(beabi_helper_env_t *env, dbg_info *dbgi,
+                                ir_node *block);
+
+/**
+ * Creates a barrier node which lets all registers specified by prolog_add_reg
+ * pass through
+ */
+ir_node *be_prolog_create_barrier(beabi_helper_env_t *env, ir_node *block);
+
+/**
+ * Get "value" of a register.
+ * This usually creates a Proj node for the start-node or barrier-node.
+ * Or returns the value set by a abi_helper_set_reg_value call
+ */
+ir_node *be_prolog_get_reg_value(beabi_helper_env_t *env,
+                                 const arch_register_t *reg);
+
+ir_node *be_prolog_get_memory(beabi_helper_env_t *env);
+
+/**
+ * Set current register value.
+ */
+void be_prolog_set_reg_value(beabi_helper_env_t *env,
+                             const arch_register_t *reg, ir_node *value);
+
+void be_prolog_set_memory(beabi_helper_env_t *env, ir_node *value);
+
+/**
+ * Set value of register at the end of the function. Necessary for:
+ *  - Callee-save registers
+ *  - Return values in registers
+ *  - stack pointer, base pointer
+ */
+void be_epilog_add_reg(beabi_helper_env_t *env, const arch_register_t *reg,
+                       arch_irn_flags_t flags, ir_node *value);
+
+void be_epilog_set_reg_value(beabi_helper_env_t *env,
+                             const arch_register_t *reg, ir_node *value);
+
+ir_node *be_epilog_get_reg_value(beabi_helper_env_t *env,
+                             const arch_register_t *reg);
+
+void be_epilog_set_memory(beabi_helper_env_t *env, ir_node *value);
+
+ir_node *be_epilog_get_memory(beabi_helper_env_t *env);
+
+void be_epilog_begin(beabi_helper_env_t *env);
+
+ir_node *be_epilog_create_barrier(beabi_helper_env_t *env, ir_node *block);
+
+/**
+ * Create return node and finishes epilog handling
+ */
+ir_node *be_epilog_create_return(beabi_helper_env_t *env, dbg_info *dbgi,
+                                 ir_node *block);
+
+
+/**
+ * Adds a X->Proj->Keep for each output value of X which has no Proj yet
+ */
+void be_add_missing_keeps(ir_graph *irg);
+
+#endif