Commit 62dc2b04 authored by Christoph Mallon's avatar Christoph Mallon
Browse files

be: Use the 2-address code helper.

parent b35bb555
......@@ -846,6 +846,5 @@ void be_init_arch_amd64(void)
lc_opt_entry_t *amd64_grp = lc_opt_get_grp(be_grp, "amd64");
lc_opt_add_table(amd64_grp, options);
amd64_init_finish();
amd64_init_transform();
}
......@@ -13,10 +13,10 @@
#include "amd64_new_nodes.h"
#include "amd64_nodes_attr.h"
#include "amd64_transform.h"
#include "be2addr.h"
#include "bearch.h"
#include "benode.h"
#include "besched.h"
#include "debug.h"
#include "panic.h"
#include "gen_amd64_new_nodes.h"
#include "gen_amd64_regalloc_if.h"
......@@ -25,21 +25,6 @@
#include "util.h"
#include "irgmod.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
/**
* Returns the index of the first "same" register.
*/
static unsigned get_first_same(arch_register_req_t const *const req)
{
unsigned const other = req->should_be_same;
for (unsigned i = 0; i != 32; ++i) {
if (other & (1U << i))
return i;
}
panic("same position not found");
}
static bool is_commutative(const ir_node *node)
{
return arch_get_irn_flags(node) & amd64_arch_irn_flag_commutative_binop;
......@@ -172,78 +157,31 @@ static void amd64_turn_back_am(ir_node *const node, arch_register_t const *const
sched_add_before(node, load);
}
/**
* Insert copies for all amd64 nodes where the should_be_same requirement is
* not fulfilled.
*/
static void assure_should_be_same_requirements(ir_node *const node)
static bool amd64_handle_2addr(ir_node *const node, arch_register_req_t const *const req, arch_register_t const *const out_reg)
{
/* Check all OUT requirements, if there is a should_be_same. */
be_foreach_out(node, i) {
arch_register_req_t const *const req
= arch_get_irn_register_req_out(node, i);
if (req->should_be_same == 0)
continue;
unsigned const same_pos = get_first_same(req);
ir_node *const in_node = get_irn_n(node, same_pos);
arch_register_t const *const in_reg = arch_get_irn_register(in_node);
arch_register_t const *const out_reg
= arch_get_irn_register_out(node, i);
if (in_reg == out_reg)
continue;
/* test if any other input is using the out register */
foreach_irn_in(node, i2, in) {
arch_register_t const *const reg = arch_get_irn_register(in);
if (reg == out_reg && (unsigned)i2 != same_pos) {
if (!is_amd64_irn(node))
panic("cannot fulfill should_be_same on non-amd64 node");
/* see what role this register has */
const amd64_attr_t *attr = get_amd64_attr_const(node);
if (attr->op_mode == AMD64_OP_ADDR
|| attr->op_mode == AMD64_OP_REG
|| attr->op_mode == AMD64_OP_REG_IMM) {
panic("unexpected op_mode");
} else if (attr->op_mode == AMD64_OP_REG_REG) {
swap:;
bool res = try_swap_inputs(node);
if (res)
return;
if (is_amd64_sub(node) || is_amd64_subs(node)) {
transform_sub_to_neg_add(node, out_reg);
return;
}
panic("couldn't swap inputs of %+F", node);
} else {
assert(attr->op_mode == AMD64_OP_REG_ADDR);
/* extract load into an own instruction */
amd64_turn_back_am(node, out_reg);
goto swap;
}
(void)req;
amd64_attr_t const *const attr = get_amd64_attr_const(node);
if (attr->op_mode == AMD64_OP_REG_ADDR) {
amd64_addr_t const *const addr = &get_amd64_addr_attr_const(node)->addr;
if ((x86_addr_variant_has_base( addr->variant) && arch_get_irn_register_in(node, addr->base_input) == out_reg) ||
(x86_addr_variant_has_index(addr->variant) && arch_get_irn_register_in(node, addr->index_input) == out_reg)) {
amd64_turn_back_am(node, out_reg);
goto swap;
}
} else if (attr->op_mode == AMD64_OP_REG_REG) {
if (arch_get_irn_register_in(node, 1) == out_reg) {
swap:
if (try_swap_inputs(node)) {
return true;
} else if (is_amd64_sub(node) || is_amd64_subs(node)) {
transform_sub_to_neg_add(node, out_reg);
return true;
}
}
ir_node *const copy = be_new_Copy_before_reg(in_node, node, out_reg);
/* Set copy as in. */
set_irn_n(node, same_pos, copy);
DBG((dbg, LEVEL_1, "created copy %+F for should be same argument at input %d of %+F\n", copy, same_pos, node));
}
}
/**
* Block walker: finishes a block.
*/
static void amd64_finish_irg_walker(ir_node *const block, void *const env)
{
(void) env;
/* Insert copies for should_be_same constraints. */
sched_foreach_safe(block, irn) {
if (is_amd64_irn(irn) || be_is_Asm(irn))
assure_should_be_same_requirements(irn);
}
return false;
}
/**
......@@ -251,10 +189,5 @@ static void amd64_finish_irg_walker(ir_node *const block, void *const env)
*/
void amd64_finish_irg(ir_graph *const irg)
{
irg_block_walk_graph(irg, 0, amd64_finish_irg_walker, 0);
}
void amd64_init_finish(void)
{
FIRM_DBG_REGISTER(dbg, "firm.be.amd64.finish");
be_handle_2addr(irg, &amd64_handle_2addr);
}
......@@ -18,7 +18,4 @@
*/
void amd64_finish_irg(ir_graph *irg);
/** Initialize the finisher. */
void amd64_init_finish(void);
#endif
......@@ -12,6 +12,7 @@
#include "arm_new_nodes.h"
#include "arm_optimize.h"
#include "be2addr.h"
#include "beirg.h"
#include "benode.h"
#include "besched.h"
......@@ -90,42 +91,6 @@ static void introduce_prolog_epilog(ir_graph *irg)
sched_add_after(start, incsp);
}
static int get_first_same(const arch_register_req_t* req)
{
const unsigned other = req->should_be_same;
for (int i = 0; i < 32; ++i) {
if (other & (1U << i))
return i;
}
panic("same position not found");
}
static void fix_should_be_same(ir_node *block, void *data)
{
(void)data;
sched_foreach(block, node) {
/* ignore non-arm nodes like Copy */
if (!is_arm_irn(node))
continue;
be_foreach_out(node, i) {
const arch_register_req_t *req
= arch_get_irn_register_req_out(node, i);
if (req->should_be_same == 0)
continue;
int same_pos = get_first_same(req);
const arch_register_t *out_reg = arch_get_irn_register_out(node, i);
ir_node *in_node = get_irn_n(node, same_pos);
const arch_register_t *in_reg = arch_get_irn_register(in_node);
if (in_reg == out_reg)
continue;
panic("arm: should_be_same fixup not implemented yet");
}
}
}
/**
* This function is called by the generic backend to correct offsets for
* nodes accessing the stack.
......@@ -191,5 +156,5 @@ void arm_finish_graph(ir_graph *irg)
/* do peephole optimizations and fix stack offsets */
arm_peephole_optimization(irg);
irg_block_walk_graph(irg, NULL, fix_should_be_same, NULL);
be_handle_2addr(irg, NULL);
}
......@@ -1742,7 +1742,6 @@ void be_init_arch_ia32(void)
be_register_isa_if("ia32", &ia32_isa_if);
ia32_init_emitter();
ia32_init_finish();
ia32_init_optimize();
ia32_init_transform();
x86_init_x87();
......
......@@ -10,9 +10,9 @@
*/
#include "ia32_finish.h"
#include "be2addr.h"
#include "bearch.h"
#include "besched.h"
#include "debug.h"
#include "gen_ia32_regalloc_if.h"
#include "ia32_bearch_t.h"
#include "ia32_new_nodes.h"
......@@ -23,8 +23,6 @@
#include "irnode_t.h"
#include "panic.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
static bool reads_carry(x86_condition_code_t code)
{
x86_condition_code_t c2 = code & ~x86_cc_negated;
......@@ -38,31 +36,19 @@ static bool reads_carry(x86_condition_code_t code)
* Transforms a Sub or xSub into Neg--Add iff OUT_REG != SRC1_REG && OUT_REG == SRC2_REG.
* THIS FUNCTIONS MUST BE CALLED AFTER REGISTER ALLOCATION.
*/
static void ia32_transform_sub_to_neg_add(ir_node *irn)
static bool ia32_transform_sub_to_neg_add(ir_node *const irn, arch_register_t const *const out_reg)
{
/* fix_am will solve this for AddressMode variants */
if (get_ia32_op_type(irn) != ia32_Normal)
return;
ir_graph *irg = get_irn_irg(irn);
ir_node *noreg = ia32_new_NoReg_gp(irg);
ir_node *noreg_fp = ia32_new_NoReg_xmm(irg);
ir_node *nomem = get_irg_no_mem(irg);
ir_node *in1 = get_irn_n(irn, n_ia32_binary_left);
ir_node *in2 = get_irn_n(irn, n_ia32_binary_right);
const arch_register_t *in1_reg = arch_get_irn_register(in1);
const arch_register_t *in2_reg = arch_get_irn_register(in2);
const arch_register_t *out_reg = arch_get_irn_register_out(irn, 0);
if (out_reg == in1_reg)
return;
/* in case of sub and OUT == SRC2 we can transform the sequence into neg src2 -- add */
if (out_reg != in2_reg)
return;
ir_node *const in2 = get_irn_n(irn, n_ia32_binary_right);
if (out_reg != arch_get_irn_register(in2))
return false;
ir_node *block = get_nodes_block(irn);
dbg_info *dbgi = get_irn_dbg_info(irn);
dbg_info *const dbgi = get_irn_dbg_info(irn);
ir_node *const block = get_nodes_block(irn);
ir_graph *const irg = get_irn_irg(irn);
ir_node *const noreg = ia32_new_NoReg_gp(irg);
ir_node *const nomem = get_irg_no_mem(irg);
ir_node *const in1 = get_irn_n(irn, n_ia32_binary_left);
/* generate the neg src2 */
ir_node *res;
......@@ -70,6 +56,7 @@ static void ia32_transform_sub_to_neg_add(ir_node *irn)
ir_mode *op_mode = get_ia32_ls_mode(irn);
assert(get_irn_mode(irn) != mode_T);
ir_node *const noreg_fp = ia32_new_NoReg_xmm(irg);
res = new_bd_ia32_xXor(dbgi, block, noreg, noreg, nomem, in2, noreg_fp);
int size = get_mode_size_bits(op_mode);
ir_entity *entity = ia32_gen_fp_known_const(size == 32 ? ia32_SSIGN : ia32_DSIGN);
......@@ -77,7 +64,7 @@ static void ia32_transform_sub_to_neg_add(ir_node *irn)
set_ia32_op_type(res, ia32_AddrModeS);
set_ia32_ls_mode(res, op_mode);
arch_set_irn_register(res, in2_reg);
arch_set_irn_register(res, out_reg);
/* add to schedule */
sched_add_before(irn, res);
......@@ -123,7 +110,7 @@ static void ia32_transform_sub_to_neg_add(ir_node *irn)
carry:;
ir_node *nnot = new_bd_ia32_Not(dbgi, block, in2);
arch_set_irn_register(nnot, in2_reg);
arch_set_irn_register(nnot, out_reg);
sched_add_before(irn, nnot);
arch_set_irn_register(carry, &ia32_registers[REG_EFLAGS]);
......@@ -147,7 +134,7 @@ carry:;
res = adc;
} else {
res = new_bd_ia32_Neg(dbgi, block, in2);
arch_set_irn_register(res, in2_reg);
arch_set_irn_register(res, out_reg);
/* add to schedule */
sched_add_before(irn, res);
......@@ -166,20 +153,14 @@ carry:;
/* exchange the add and the sub */
sched_replace(irn, res);
exchange(irn, res);
return true;
}
static void ia32_transform_ShlD_to_ShrD_imm(ir_node *const irn)
static bool ia32_transform_ShlD_to_ShrD_imm(ir_node *const irn, arch_register_t const *const out_reg)
{
ir_node *const in0 = get_irn_n(irn, n_ia32_ShlD_val_high);
arch_register_t const *const in0_reg = arch_get_irn_register(in0);
arch_register_t const *const out_reg = arch_get_irn_register_out(irn, pn_ia32_ShlD_res);
if (out_reg == in0_reg)
return; /* should_be_same fulfilled. */
ir_node *const in1 = get_irn_n(irn, n_ia32_ShlD_val_low);
arch_register_t const *const in1_reg = arch_get_irn_register(in1);
if (out_reg != in1_reg)
return; /* res uses third register. Must be resolved with a Copy. */
ir_node *const in1 = get_irn_n(irn, n_ia32_ShlD_val_low);
if (arch_get_irn_register(in1) != out_reg)
return false;
/* a = ShlD(b, a, c) -> a = ShrD(a, b, 32 - c) */
ir_node *const lcount = get_irn_n(irn, n_ia32_ShlD_count);
......@@ -188,10 +169,12 @@ static void ia32_transform_ShlD_to_ShrD_imm(ir_node *const irn)
ir_node *const count = ia32_create_Immediate(irg, 32 - attr->imm.offset);
dbg_info *const dbgi = get_irn_dbg_info(irn);
ir_node *const block = get_nodes_block(irn);
ir_node *const in0 = get_irn_n(irn, n_ia32_ShlD_val_high);
ir_node *const res = new_bd_ia32_ShrD_imm(dbgi, block, in1, in0, count);
arch_set_irn_register_out(res, pn_ia32_ShrD_res, out_reg);
sched_replace(irn, res);
exchange(irn, res);
return true;
}
static inline int need_constraint_copy(ir_node *irn)
......@@ -210,73 +193,6 @@ static inline int need_constraint_copy(ir_node *irn)
return be_is_Asm(irn);
}
/**
* Returns the index of the "same" register.
* On the x86, we should have only one.
*/
static int get_first_same(const arch_register_req_t* req)
{
const unsigned other = req->should_be_same;
for (int i = 0; i < 32; ++i) {
if (other & (1U << i))
return i;
}
panic("same position not found");
}
/**
* Insert copies for all ia32 nodes where the should_be_same requirement
* is not fulfilled.
* Transform Sub into Neg -- Add if IN2 == OUT
*/
static void assure_should_be_same_requirements(ir_node *node)
{
/* check all OUT requirements, if there is a should_be_same */
be_foreach_out(node, i) {
arch_register_req_t const *const req = arch_get_irn_register_req_out(node, i);
if (req->should_be_same == 0)
continue;
/* get in and out register */
int const same_pos = get_first_same(req);
ir_node *const in_node = get_irn_n(node, same_pos);
arch_register_t const *const in_reg = arch_get_irn_register(in_node);
arch_register_t const *const out_reg = arch_get_irn_register_out(node, i);
/* requirement already fulfilled? */
if (in_reg == out_reg)
continue;
/* check if any other input operands uses the out register */
int uses_out_reg_pos = -1;
foreach_irn_in(node, i2, in) {
arch_register_t const *const other_in_reg = arch_get_irn_register(in);
if (other_in_reg == out_reg) {
if (uses_out_reg_pos >= 0)
panic("unresolved should_be_same constraint");
uses_out_reg_pos = i2;
}
}
if (uses_out_reg_pos < 0) {
/* no-one else is using the out reg, we can simply copy it
* (the register can't be live since the operation will override it
* anyway) */
ir_node *const copy = be_new_Copy_before_reg(in_node, node, out_reg);
/* set copy as in */
set_irn_n(node, same_pos, copy);
DBG((dbg, LEVEL_1, "created copy %+F for should be same argument at input %d of %+F\n", copy, same_pos, node));
} else if (uses_out_reg_pos == n_ia32_binary_right && is_ia32_commutative(node)) {
/* for commutative nodes we can simply swap the left/right */
ia32_swap_left_right(node);
DBG((dbg, LEVEL_1, "swapped left/right input of %+F to resolve should be same constraint\n", node));
} else {
panic("unresolved should_be_same constraint");
}
}
}
/**
* Following Problem:
* We have a source address mode node with base or index register equal to
......@@ -285,74 +201,38 @@ static void assure_should_be_same_requirements(ir_node *node)
* register -> base or index is broken then.
* Solution: Turn back this address mode into explicit Load + Operation.
*/
static void fix_am_source(ir_node *irn)
static void fix_am_source(ir_node *const irn, arch_register_t const *const out_reg)
{
/* check only ia32 nodes with source address mode */
if (!is_ia32_irn(irn) || get_ia32_op_type(irn) != ia32_AddrModeS)
/* Only need to fix operations with source address mode. */
if (get_ia32_op_type(irn) != ia32_AddrModeS)
return;
/* only need to fix binary operations */
if (get_ia32_am_support(irn) != ia32_am_binary)
/* Only need to fix if the out reg is the same as base or index register. */
if (out_reg != arch_get_irn_register_in(irn, n_ia32_base) &&
out_reg != arch_get_irn_register_in(irn, n_ia32_index))
return;
be_foreach_out(irn, i) {
const arch_register_req_t *req = arch_get_irn_register_req_out(irn, i);
if (req->should_be_same == 0)
continue;
/* get in and out register */
const arch_register_t *out_reg = arch_get_irn_register_out(irn, i);
int same_pos = get_first_same(req);
ir_node *same_node = get_irn_n(irn, same_pos);
const arch_register_t *same_reg = arch_get_irn_register(same_node);
/* should_be same constraint is fullfilled, nothing to do */
if (out_reg == same_reg)
continue;
/* we only need to do something if the out reg is the same as base
or index register */
if (out_reg != arch_get_irn_register(get_irn_n(irn, n_ia32_base)) &&
out_reg != arch_get_irn_register(get_irn_n(irn, n_ia32_index)))
continue;
ir_node *load_res = ia32_turn_back_am(irn);
arch_set_irn_register(load_res, out_reg);
DBG((dbg, LEVEL_3,
"irg %+F: build back AM source for node %+F, inserted load %+F\n",
get_irn_irg(irn), irn, get_Proj_pred(load_res)));
break;
}
ir_node *const load_res = ia32_turn_back_am(irn);
arch_set_irn_register(load_res, out_reg);
}
/**
* Block walker: finishes a block
*/
static void ia32_finish_irg_walker(ir_node *block, void *env)
static bool ia32_handle_2addr(ir_node *const node, arch_register_req_t const *const req, arch_register_t const *const reg)
{
(void) env;
/* first: turn back AM source if necessary */
sched_foreach_safe(block, irn) {
fix_am_source(irn);
}
sched_foreach_safe(block, irn) {
/* check if there is a sub which need to be transformed */
if (is_ia32_Sub(irn) || is_ia32_Sbb(irn) || is_ia32_xSub(irn)) {
ia32_transform_sub_to_neg_add(irn);
} else if (is_ia32_ShlD(irn)) {
ia32_transform_ShlD_to_ShrD_imm(irn);
/* Some nodes are just a bit less efficient, but need no fixing if the
* should_be_same requirement is not fulfilled. */
if (!need_constraint_copy(node))
return true;
fix_am_source(node, reg);
if (req->should_be_same == (1U << n_ia32_binary_left | 1U << n_ia32_binary_right)) {
if (reg == arch_get_irn_register_in(node, n_ia32_binary_right)) {
ia32_swap_left_right(node);
return true;
}
} else if (is_ia32_ShlD(node)) {
return ia32_transform_ShlD_to_ShrD_imm(node, reg);
} else if (is_ia32_Sub(node) || is_ia32_Sbb(node) || is_ia32_xSub(node)) {
return ia32_transform_sub_to_neg_add(node, reg);
}
/* second: insert copies and finish irg */
sched_foreach_safe(block, irn) {
/* some nodes are just a bit less efficient, but need no fixing if the
* should be same requirement is not fulfilled */
if (need_constraint_copy(irn))
assure_should_be_same_requirements(irn);
}
return false;
}
/**
......@@ -360,10 +240,5 @@ static void ia32_finish_irg_walker(ir_node *block, void *env)
*/
void ia32_finish_irg(ir_graph *irg)
{
irg_block_walk_graph(irg, NULL, ia32_finish_irg_walker, NULL);
}
void ia32_init_finish(void)
{
FIRM_DBG_REGISTER(dbg, "firm.be.ia32.finish");
be_handle_2addr(irg, &ia32_handle_2addr);
}
......@@ -19,7 +19,4 @@
*/
void ia32_finish_irg(ir_graph *irg);
/** Initialize the finisher. */
void ia32_init_finish(void);
#endif
......@@ -24,6 +24,7 @@
*/
#include "sparc_bearch_t.h"
#include "be2addr.h"
#include "gen_sparc_regalloc_if.h"
#include "sparc_new_nodes.h"
#include "sparc_transform.h"
......@@ -43,73 +44,6 @@
#include "panic.h"
#include "util.h"
static int get_first_same(const arch_register_req_t *req)
{
const unsigned other = req->should_be_same;
for (int i = 0; i < 32; ++i) {
if (other & (1U << i))
return i;
}
panic("same position not found");
}
/**
* Insert copies for all SPARC nodes where the should_be_same requirement
* is not fulfilled.
* Transform Sub into Neg -- Add if IN2 == OUT
*/
static void assure_should_be_same_requirements(ir_node *node)
{
/* check all OUT requirements, if there is a should_be_same */
be_foreach_out(node, i) {
const arch_register_req_t *req = arch_get_irn_register_req_out(node, i);
if (req->should_be_same == 0)
continue;
int same_pos = get_first_same(req);
/* get in and out register */
const arch_register_t *out_reg = arch_get_irn_register_out(node, i);
ir_node *in_node = get_irn_n(node, same_pos);
const arch_register_t *in_reg = arch_get_irn_register(in_node);
/* requirement already fulfilled? */
if (in_reg == out_reg)
continue;
assert(in_reg->cls == out_reg->cls);
/* check if any other input operands uses the out register */
ir_node *uses_out_reg = NULL;
int uses_out_reg_pos = -1;
foreach_irn_in(node, i2, in) {
const arch_register_t *other_in_reg = arch_get_irn_register(in);
if (other_in_reg != out_reg)
continue;
if (uses_out_reg != NULL && in != uses_out_reg) {
panic("invalid register allocation");
}
uses_out_reg = in;
if (uses_out_reg_pos >= 0)
uses_out_reg_pos = -1; /* multiple inputs... */
else
uses_out_reg_pos = i2;
}
/* no-one else is using the out reg, we can simply copy it
* (the register can't be live since the operation will override it
* anyway) */
if (uses_out_reg == NULL) {
ir_node *const copy = be_new_Copy_before_reg(in_node, node, out_reg);
/* set copy as in */