Commit fdce0699 authored by Michael Beck's avatar Michael Beck
Browse files

- Add a peephole phase that fixes all stack offsets

[r19823]
parent 4818a421
......@@ -47,6 +47,7 @@
#include "arm_nodes_attr.h"
#include "arm_new_nodes.h"
#include "arm_optimize.h"
#include "../beabi.h"
#include "bearch_arm_t.h"
......
......@@ -205,11 +205,6 @@ void set_arm_SwitchJmp_default_proj_num(ir_node *node, long default_proj_num);
*/
arm_shift_modifier get_arm_shift_modifier(const ir_node *node);
/**
* Decode an immediate with shifter operand
*/
unsigned int arm_decode_imm_w_shift(long imm_value);
/* Include the generated headers */
#include "gen_arm_new_nodes.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 Implements several optimizations for ARM.
* @author Michael Beck
* @version $Id: $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "irgmod.h"
#include "ircons.h"
#include "error.h"
#include "benode_t.h"
#include "bepeephole.h"
#include "besched.h"
#include "arm_optimize.h"
#include "gen_arm_regalloc_if.h"
#include "gen_arm_new_nodes.h"
static const arch_env_t *arch_env;
static arm_code_gen_t *cg;
/** Execute ARM ROL. */
static unsigned arm_rol(unsigned v, unsigned rol) {
return (v << rol) | (v >> (32 - rol));
}
/*
* construct 8bit values and rot amounts for a value.
*/
void arm_gen_vals_from_word(unsigned int value, arm_vals *result)
{
int initial = 0;
memset(result, 0, sizeof(*result));
/* special case: we prefer shift amount 0 */
if (value < 0x100) {
result->values[0] = value;
result->ops = 1;
return;
}
while (value != 0) {
if (value & 0xFF) {
unsigned v = arm_rol(value, 8) & 0xFFFFFF;
int shf = 0;
for (;;) {
if ((v & 3) != 0)
break;
shf += 2;
v >>= 2;
}
v &= 0xFF;
shf = (initial + shf - 8) & 0x1F;
result->values[result->ops] = v;
result->shifts[result->ops] = shf;
++result->ops;
value ^= arm_rol(v, shf) >> initial;
}
else {
value >>= 8;
initial += 8;
}
}
}
/**
* Encodes an immediate with shifter operand
*/
unsigned int arm_encode_imm_w_shift(unsigned int shift, unsigned int immediate) {
return immediate | ((shift>>1)<<8);
}
/**
* Decode an immediate with shifter operand
*/
unsigned int arm_decode_imm_w_shift(long imm_value) {
unsigned l = (unsigned)imm_value;
unsigned rol = (l & ~0xFF) >> 7;
return arm_rol(l & 0xFF, rol);
}
/**
* Creates a Mov node.
*/
static ir_node *create_mov_node(ir_node *sched_point, dbg_info *dbg, ir_node *block, long value) {
ir_graph *irg = current_ir_graph;
ir_node *mov;
mov = new_rd_arm_Mov_i(dbg, irg, block, mode_Iu, value);
arch_set_irn_register(arch_env, mov, &arm_gp_regs[REG_R12]);
sched_add_before(sched_point, mov);
return mov;
}
/**
* Creates a Mvn node.
*/
static ir_node *create_mvn_node(ir_node *sched_point, dbg_info *dbg, ir_node *block, long value) {
ir_graph *irg = current_ir_graph;
ir_node *mvn;
mvn = new_rd_arm_Mvn_i(dbg, irg, block, mode_Iu, value);
arch_set_irn_register(arch_env, mvn, &arm_gp_regs[REG_R12]);
sched_add_before(sched_point, mvn);
return mvn;
}
/**
* Creates a possible DAG for an constant and schedule it before
* the node sched_point.
* The Dag deliveres it's result in register R12.
*/
static ir_node *create_const_graph_value(ir_node *sched_point, unsigned int value) {
dbg_info *dbg;
ir_node *block, *result;
arm_vals v, vn;
int cnt;
arm_gen_vals_from_word(value, &v);
arm_gen_vals_from_word(~value, &vn);
dbg = get_irn_dbg_info(sched_point);
block = get_nodes_block(sched_point);
if (vn.ops < v.ops) {
/* remove bits */
result = create_mvn_node(sched_point, dbg, block, arm_encode_imm_w_shift(vn.shifts[0], vn.values[0]));
for (cnt = 1; cnt < vn.ops; ++cnt) {
long value = arm_encode_imm_w_shift(vn.shifts[cnt], vn.values[cnt]);
ir_node *bic_i_node = new_rd_arm_Bic_i(dbg, current_ir_graph, block, result, mode_Iu, value);
arch_set_irn_register(arch_env, bic_i_node, &arm_gp_regs[REG_R12]);
sched_add_before(sched_point, bic_i_node);
result = bic_i_node;
}
}
else {
/* add bits */
result = create_mov_node(sched_point, dbg, block, arm_encode_imm_w_shift(v.shifts[0], v.values[0]));
for (cnt = 1; cnt < v.ops; ++cnt) {
long value = arm_encode_imm_w_shift(v.shifts[cnt], v.values[cnt]);
ir_node *orr_i_node = new_rd_arm_Or_i(dbg, current_ir_graph, block, result, mode_Iu, value);
arch_set_irn_register(arch_env, orr_i_node, &arm_gp_regs[REG_R12]);
sched_add_before(sched_point, orr_i_node);
result = orr_i_node;
}
}
return result;
}
/**
* Returns non.zero if the given offset can be directly encoded into an ARM instruction.
*/
static int allowed_arm_immediate(int offset, arm_vals *result) {
arm_gen_vals_from_word(offset, result);
return result->ops <= 1;
}
/**
* Fix an IncSP node if the offset gets too big
*/
static void peephole_be_IncSP(ir_node *node) {
ir_graph *irg;
ir_node *block;
int offset, cnt, align, sign = 1;
arm_vals v;
/* first optimize incsp->incsp combinations */
be_peephole_IncSP_IncSP(node);
offset = be_get_IncSP_offset(node);
/* can be transformed into Add OR Sub */
if (offset < 0) {
sign = -1;
offset = -offset;
}
if (allowed_arm_immediate(offset, &v))
return;
be_set_IncSP_offset(node, (int)arm_encode_imm_w_shift(v.shifts[0], v.values[0]) * sign);
irg = current_ir_graph;
block = get_nodes_block(node);
align = be_get_IncSP_align(node);
for (cnt = 1; cnt < v.ops; ++cnt) {
int value = (int)arm_encode_imm_w_shift(v.shifts[cnt], v.values[cnt]);
ir_node *next = be_new_IncSP(&arm_gp_regs[REG_SP], irg, block, node, value * sign, align);
sched_add_after(node, next);
node = next;
}
}
/**
* creates the address by Adds
*/
static ir_node *gen_ptr_add(ir_node *node, ir_node *frame, arm_vals *v)
{
ir_graph *irg = current_ir_graph;
dbg_info *dbg = get_irn_dbg_info(node);
ir_node *block = get_nodes_block(node);
int cnt;
ir_node *ptr;
ptr = new_rd_arm_Add_i(dbg, irg, block, frame, mode_Iu, arm_encode_imm_w_shift(v->shifts[0], v->values[0]));
arch_set_irn_register(arch_env, ptr, &arm_gp_regs[REG_R12]);
sched_add_before(node, ptr);
for (cnt = 1; cnt < v->ops; ++cnt) {
long value = arm_encode_imm_w_shift(v->shifts[cnt], v->values[cnt]);
ir_node *next = new_rd_arm_Add_i(dbg, irg, block, ptr, mode_Iu, value);
arch_set_irn_register(arch_env, next, &arm_gp_regs[REG_R12]);
sched_add_before(node, next);
ptr = next;
}
return ptr;
}
/**
* creates the address by Subs
*/
static ir_node *gen_ptr_sub(ir_node *node, ir_node *frame, arm_vals *v)
{
ir_graph *irg = current_ir_graph;
dbg_info *dbg = get_irn_dbg_info(node);
ir_node *block = get_nodes_block(node);
int cnt;
ir_node *ptr;
ptr = new_rd_arm_Sub_i(dbg, irg, block, frame, mode_Iu, arm_encode_imm_w_shift(v->shifts[0], v->values[0]));
arch_set_irn_register(arch_env, ptr, &arm_gp_regs[REG_R12]);
sched_add_before(node, ptr);
for (cnt = 1; cnt < v->ops; ++cnt) {
long value = arm_encode_imm_w_shift(v->shifts[cnt], v->values[cnt]);
ir_node *next = new_rd_arm_Sub_i(dbg, irg, block, ptr, mode_Iu, value);
arch_set_irn_register(arch_env, next, &arm_gp_regs[REG_R12]);
sched_add_before(node, next);
ptr = next;
}
return ptr;
}
/**
* Fix an be_Spill node if the offset gets too big
*/
static void peephole_be_Spill(ir_node *node) {
ir_entity *ent = be_get_frame_entity(node);
int use_add = 1, offset = get_entity_offset(ent);
ir_node *block, *ptr, *frame, *value, *store;
ir_mode *mode;
dbg_info *dbg;
ir_graph *irg;
arm_vals v;
if (allowed_arm_immediate(offset, &v))
return;
if (offset < 0) {
use_add = 0;
offset = -offset;
}
frame = be_get_Spill_frame(node);
if (use_add) {
ptr = gen_ptr_add(node, frame, &v);
} else {
ptr = gen_ptr_sub(node, frame, &v);
}
value = be_get_Spill_val(node);
mode = get_irn_mode(value);
irg = current_ir_graph;
dbg = get_irn_dbg_info(node);
block = get_nodes_block(node);
if (mode_is_float(mode)) {
if (USE_FPA(cg->isa)) {
/* transform into fpaStf */
store = new_rd_arm_fpaStf(dbg, irg, block, ptr, value, get_irg_no_mem(irg), mode);
sched_add_before(node, store);
} else {
panic("peephole_be_Spill: spill not supported for this mode");
}
} else if (mode_is_dataM(mode)) {
/* transform into Store */;
store = new_rd_arm_Store(dbg, irg, block, ptr, value, get_irg_no_mem(irg));
sched_add_before(node, store);
} else {
panic("peephole_be_Spill: spill not supported for this mode");
}
be_peephole_before_exchange(node, store);
sched_remove(node);
exchange(node, store);
be_peephole_after_exchange(store);
}
/**
* Fix an be_Reload node if the offset gets too big
*/
static void peephole_be_Reload(ir_node *node) {
ir_entity *ent = be_get_frame_entity(node);
int use_add = 1, offset = get_entity_offset(ent);
ir_node *block, *ptr, *frame, *load, *mem, *proj;
ir_mode *mode;
dbg_info *dbg;
ir_graph *irg;
arm_vals v;
const arch_register_t *reg;
if (allowed_arm_immediate(offset, &v))
return;
if (offset < 0) {
use_add = 0;
offset = -offset;
}
frame = be_get_Reload_frame(node);
if (use_add) {
ptr = gen_ptr_add(node, frame, &v);
} else {
ptr = gen_ptr_sub(node, frame, &v);
}
reg = arch_get_irn_register(arch_env, node);
mem = be_get_Reload_mem(node);
mode = get_irn_mode(node);
irg = current_ir_graph;
dbg = get_irn_dbg_info(node);
block = get_nodes_block(node);
if (mode_is_float(mode)) {
if (USE_FPA(cg->isa)) {
/* transform into fpaLdf */
load = new_rd_arm_fpaLdf(dbg, irg, block, ptr, mem, mode);
sched_add_before(node, load);
proj = new_rd_Proj(dbg, irg, block, load, mode, pn_arm_fpaLdf_res);
arch_set_irn_register(arch_env, proj, reg);
} else {
panic("peephole_be_Spill: spill not supported for this mode");
}
} else if (mode_is_dataM(mode)) {
/* transform into Store */;
load = new_rd_arm_Load(dbg, irg, block, ptr, mem);
sched_add_before(node, load);
proj = new_rd_Proj(dbg, irg, block, load, mode_Iu, pn_arm_Load_res);
arch_set_irn_register(arch_env, proj, reg);
} else {
panic("peephole_be_Spill: spill not supported for this mode");
}
be_peephole_before_exchange(node, proj);
sched_remove(node);
exchange(node, proj);
be_peephole_after_exchange(proj);
}
/**
* Register a peephole optimization function.
*/
static void register_peephole_optimisation(ir_op *op, peephole_opt_func func) {
assert(op->ops.generic == NULL);
op->ops.generic = (op_func)func;
}
/* Perform peephole-optimizations. */
void arm_peephole_optimization(arm_code_gen_t *new_cg)
{
cg = new_cg;
arch_env = cg->arch_env;
/* register peephole optimizations */
clear_irp_opcodes_generic_func();
register_peephole_optimisation(op_be_IncSP, peephole_be_IncSP);
register_peephole_optimisation(op_be_Spill, peephole_be_Spill);
register_peephole_optimisation(op_be_Reload, peephole_be_Reload);
be_peephole_opt(cg->birg);
}
/*
* 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 Implements several optimizations for ARM.
* @author Michael Beck
* @version $Id: $
*/
#ifndef FIRM_BE_ARM_ARM_OPTIMIZE_H
#define FIRM_BE_ARM_ARM_OPTIMIZE_H
#include "irgraph.h"
#include "bearch_arm_t.h"
typedef struct arm_vals {
int ops;
unsigned char values[4];
unsigned char shifts[4];
} arm_vals;
/**
* Encodes an immediate with shifter operand
*/
unsigned int arm_encode_imm_w_shift(unsigned int shift, unsigned int immediate);
/**
* Decode an immediate with shifter operand
*/
unsigned int arm_decode_imm_w_shift(long imm_value);
/**
* construct 8bit values and rot amounts for a 32bit value.
*/
void arm_gen_vals_from_word(unsigned int value, arm_vals *result);
/**
* Performs Peephole Optimizations an a graph.
*
* @param irg the graph
* @param cg the code generator object
*/
void arm_peephole_optimization(arm_code_gen_t *cg);
#endif /* FIRM_BE_ARM_ARM_OPTIMIZE_H */
......@@ -49,6 +49,7 @@
#include "arm_nodes_attr.h"
#include "archop.h"
#include "arm_transform.h"
#include "arm_optimize.h"
#include "arm_new_nodes.h"
#include "arm_map_regs.h"
......@@ -77,62 +78,10 @@ static INLINE int mode_needs_gp_reg(ir_mode *mode) {
return mode_is_int(mode) || mode_is_reference(mode);
}
typedef struct vals_ {
int ops;
unsigned char values[4];
unsigned char shifts[4];
} vals;
/** Execute ROL. */
static unsigned do_rol(unsigned v, unsigned rol) {
return (v << rol) | (v >> (32 - rol));
}
/**
* construct 8bit values and rot amounts for a value
*/
static void gen_vals_from_word(unsigned int value, vals *result)
{
int initial = 0;
memset(result, 0, sizeof(*result));
/* special case: we prefer shift amount 0 */
if (value < 0x100) {
result->values[0] = value;
result->ops = 1;
return;
}
while (value != 0) {
if (value & 0xFF) {
unsigned v = do_rol(value, 8) & 0xFFFFFF;
int shf = 0;
for (;;) {
if ((v & 3) != 0)
break;
shf += 2;
v >>= 2;
}
v &= 0xFF;
shf = (initial + shf - 8) & 0x1F;
result->values[result->ops] = v;
result->shifts[result->ops] = shf;
++result->ops;
value ^= do_rol(v, shf) >> initial;
}
else {
value >>= 8;
initial += 8;
}
}
}
/**
* Creates a arm_Const node.
*/
static ir_node *create_const_node(dbg_info *dbg, ir_node *block, long value) {
static ir_node *create_mov_node(dbg_info *dbg, ir_node *block, long value) {
ir_mode *mode = mode_Iu;
ir_graph *irg = current_ir_graph;
ir_node *res;
......@@ -148,7 +97,7 @@ static ir_node *create_const_node(dbg_info *dbg, ir_node *block, long value) {
/**
* Creates a arm_Const_Neg node.
*/
static ir_node *create_const_neg_node(dbg_info *dbg, ir_node *block, long value) {
static ir_node *create_mvn_node(dbg_info *dbg, ir_node *block, long value) {
ir_mode *mode = mode_Iu;
ir_graph *irg = current_ir_graph;
ir_node *res;
......@@ -163,38 +112,21 @@ static ir_node *create_const_neg_node(dbg_info *dbg, ir_node *block, long value)
#define NEW_BINOP_NODE(opname, env, op1, op2) new_rd_arm_##opname(env->dbg, current_ir_graph, env->block, op1, op2, env->mode)
/**
* Encodes an immediate with shifter operand
*/
static unsigned int arm_encode_imm_w_shift(unsigned int shift, unsigned int immediate) {
return immediate | ((shift>>1)<<8);
}
/**
* Decode an immediate with shifter operand
*/
unsigned int arm_decode_imm_w_shift(long imm_value) {
unsigned l = (unsigned)imm_value;
unsigned rol = (l & ~0xFF) >> 7;
return do_rol(l & 0xFF, rol);
}
/**
* Creates a possible DAG for an constant.
*/
static ir_node *create_const_graph_value(dbg_info *dbg, ir_node *block, unsigned int value) {
ir_node *result;
vals v, vn;
arm_vals v, vn;
int cnt;
ir_mode *mode = mode_Iu;
gen_vals_from_word(value, &v);
gen_vals_from_word(~value, &vn);
arm_gen_vals_from_word(value, &v);
arm_gen_vals_from_word(~value, &vn);
if (vn.ops < v.ops) {
/* remove bits */
result = create_const_neg_node(dbg, block, arm_encode_imm_w_shift(vn.shifts[0], vn.values[0]));
result = create_mvn_node(dbg, block, arm_encode_imm_w_shift(vn.shifts[0], vn.values[0]));
for (cnt = 1; cnt < vn.ops; ++cnt) {
long value = arm_encode_imm_w_shift(vn.shifts[cnt], vn.values[cnt]);
......@@ -204,7 +136,7 @@ static ir_node *create_const_graph_value(dbg_info *dbg, ir_node *block, unsigned
}
else {
/* add bits */
result = create_const_node(dbg, block, arm_encode_imm_w_shift(v.shifts[0], v.values[0]));