Commit 6d0764bb authored by Matthias Braun's avatar Matthias Braun
Browse files

helper functions for doing custom abi construction in codeselection phase

[r27724]
parent 88e67644
/*
* 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);
}
/*
* 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
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment