#endif
#include "ia32_address_mode.h"
+#include "ia32_transform.h"
#include "irtypes.h"
#include "irnode_t.h"
#include "irprintf.h"
#include "error.h"
#include "iredges_t.h"
+#include "irgwalk.h"
#include "../benode_t.h"
-#undef AGGRESSIVE_AM
+#define AGGRESSIVE_AM
-static int is_immediate_2(const ir_node *node, int *symconsts)
+/* gas/ld don't support negative symconsts :-( */
+#undef SUPPORT_NEGATIVE_SYMCONSTS
+
+static bitset_t *non_address_mode_nodes;
+
+static int do_is_immediate(const ir_node *node, int *symconsts, int negate)
{
ir_node *left;
ir_node *right;
case iro_Const:
if(!tarval_is_long(get_Const_tarval(node))) {
#ifdef DEBUG_libfirm
- ir_fprintf(stderr, "Optimisation warning tarval of %+F is not a "
- "long.");
+ ir_fprintf(stderr, "Optimisation warning tarval of %+F(%+F) is not "
+ "a long.\n", node, current_ir_graph);
#endif
return 0;
}
return 1;
case iro_SymConst:
+#ifndef SUPPORT_NEGATIVE_SYMCONSTS
+ /* unfortunately the assembler/linker doesn't support -symconst */
+ if(negate)
+ return 0;
+#endif
+
if(get_SymConst_kind(node) != symconst_addr_ent)
return 0;
(*symconsts)++;
return 1;
case iro_Add:
case iro_Sub:
+ if(bitset_is_set(non_address_mode_nodes, get_irn_idx(node)))
+ return 0;
+
left = get_binop_left(node);
right = get_binop_right(node);
- if(!is_immediate_2(left, symconsts))
+ if(!do_is_immediate(left, symconsts, negate))
return 0;
- if(!is_immediate_2(right, symconsts))
+ if(!do_is_immediate(right, symconsts, is_Sub(node) ? !negate : negate))
return 0;
return 1;
return 0;
}
-static int is_immediate(ia32_address_t *addr, const ir_node *node)
+static int is_immediate_simple(const ir_node *node)
+{
+ int symconsts = 0;
+ return do_is_immediate(node, &symconsts, 0);
+}
+
+static int is_immediate(ia32_address_t *addr, const ir_node *node, int negate)
{
int symconsts = 0;
if(addr->symconst_ent != NULL)
symconsts = 1;
- return is_immediate_2(node, &symconsts);
+ return do_is_immediate(node, &symconsts, negate);
}
static void eat_immediate(ia32_address_t *addr, ir_node *node, int negate)
tarval *tv;
ir_node *left;
ir_node *right;
+ long val;
switch(get_irn_opcode(node)) {
case iro_Const:
tv = get_Const_tarval(node);
+ val = get_tarval_long(tv);
if(negate) {
- addr->offset -= get_tarval_long(tv);
+ addr->offset -= val;
} else {
- addr->offset += get_tarval_long(tv);
+ addr->offset += val;
}
break;
case iro_SymConst:
"calculation");
}
addr->symconst_ent = get_SymConst_entity(node);
+#ifndef SUPPORT_NEGATIVE_SYMCONSTS
+ assert(!negate);
+#endif
addr->symconst_sign = negate;
break;
case iro_Add:
+ assert(!bitset_is_set(non_address_mode_nodes, get_irn_idx(node)));
left = get_Add_left(node);
right = get_Add_right(node);
eat_immediate(addr, left, negate);
eat_immediate(addr, right, negate);
break;
case iro_Sub:
+ assert(!bitset_is_set(non_address_mode_nodes, get_irn_idx(node)));
left = get_Sub_left(node);
right = get_Sub_right(node);
eat_immediate(addr, left, negate);
static ir_node *eat_immediates(ia32_address_t *addr, ir_node *node, int force)
{
+ if(!force && bitset_is_set(non_address_mode_nodes, get_irn_idx(node)))
+ return node;
+
if(is_Add(node)) {
ir_node *left = get_Add_left(node);
ir_node *right = get_Add_right(node);
-#if 0
-#ifndef AGGRESSIVE_AM
- if(!force && get_irn_n_edges(node) > 1)
- return node;
-#else
- (void) force;
-#endif
-#endif
-
- if(is_immediate(addr, left)) {
+ if(is_immediate(addr, left, 0)) {
eat_immediate(addr, left, 0);
return eat_immediates(addr, right, 0);
}
- if(is_immediate(addr, right)) {
+ if(is_immediate(addr, right, 0)) {
eat_immediate(addr, right, 0);
return eat_immediates(addr, left, 0);
}
ir_node *left = get_Sub_left(node);
ir_node *right = get_Sub_right(node);
-#if 0
-#ifndef AGGRESSIVE_AM
- if(!force && get_irn_n_edges(node) > 1)
- return node;
-#endif
-#endif
-
- if(is_immediate(addr, right)) {
+ if(is_immediate(addr, right, 1)) {
eat_immediate(addr, right, 1);
return eat_immediates(addr, left, 0);
}
if(val == 0) {
ir_fprintf(stderr, "Optimisation warning: unoptimized Shl(,0) found\n");
}
+ if(bitset_is_set(non_address_mode_nodes, get_irn_idx(node)))
+ return 0;
#ifndef AGGRESSIVE_AM
if(get_irn_n_edges(node) > 1)
return 1;
}
+/**
+ * Returns non-zero if a value of a given mode can be stored in GP registers.
+ */
+static INLINE int mode_needs_gp_reg(ir_mode *mode) {
+ if(mode == mode_fpcw)
+ return 0;
+ if(get_mode_size_bits(mode) > 32)
+ return 0;
+ return mode_is_int(mode) || mode_is_reference(mode) || mode == mode_b;
+}
+
+/**
+ * Check, if a given done is a Down-Conv, ie a integer Conv
+ * from a mode with mode to a mode with lesser bits.
+ * Moreover, we return only true if the node has not more than 1 user.
+ */
+static int is_downconv(const ir_node *node)
+{
+ ir_mode *src_mode;
+ ir_mode *dest_mode;
+
+ if(!is_Conv(node))
+ return 0;
+
+ /* we only want to skip the conv when we're the only user
+ * (not optimal but for now...)
+ */
+ if(get_irn_n_edges(node) > 1)
+ return 0;
+
+ src_mode = get_irn_mode(get_Conv_op(node));
+ dest_mode = get_irn_mode(node);
+ return mode_needs_gp_reg(src_mode)
+ && mode_needs_gp_reg(dest_mode)
+ && get_mode_size_bits(dest_mode) < get_mode_size_bits(src_mode);
+}
+
+/**
+ * Skip all Donw-Conv's on a given node.
+ */
+static ir_node *skip_downconv(ir_node *node)
+{
+ while(is_downconv(node))
+ node = get_Conv_op(node);
+
+ return node;
+}
+
void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
{
int res = 0;
ir_node *eat_imms;
- if(is_immediate(addr, node)) {
+ if(is_immediate(addr, node, 0)) {
eat_immediate(addr, node, 0);
return;
}
}
#endif
+ if(!force && bitset_is_set(non_address_mode_nodes, get_irn_idx(node))) {
+ addr->base = node;
+ return;
+ }
+
eat_imms = eat_immediates(addr, node, force);
if(eat_imms != node) {
+ if(force) {
+ eat_imms = skip_downconv(eat_imms);
+ }
+
res = 1;
node = eat_imms;
#ifndef AGGRESSIVE_AM
return;
}
#endif
+ if(bitset_is_set(non_address_mode_nodes, get_irn_idx(node))) {
+ addr->base = node;
+ return;
+ }
}
/* starting point Add, Sub or Shl, FrameAddr */
if(is_Shl(node)) {
if(eat_shl(addr, node))
return;
- } else if(is_immediate(addr, node)) {
+ } else if(is_immediate(addr, node, 0)) {
eat_immediate(addr, node, 0);
return;
} else if(be_is_FrameAddr(node)) {
} else if(is_Add(node)) {
ir_node *left = get_Add_left(node);
ir_node *right = get_Add_right(node);
- assert(force || !is_immediate(addr, left));
- assert(force || !is_immediate(addr, right));
+
+ if(force) {
+ left = skip_downconv(left);
+ right = skip_downconv(right);
+ }
+
+ assert(force || !is_immediate(addr, left, 0));
+ assert(force || !is_immediate(addr, right, 0));
if(is_Shl(left) && eat_shl(addr, left)) {
left = NULL;
} else if(is_Shl(right) && eat_shl(addr, right)) {
right = NULL;
}
- if(left != NULL && be_is_FrameAddr(left)) {
+ if(left != NULL && be_is_FrameAddr(left)
+ && !bitset_is_set(non_address_mode_nodes, get_irn_idx(left))) {
assert(addr->base == NULL);
assert(addr->frame_entity == NULL);
addr->base = be_get_FrameAddr_frame(left);
addr->use_frame = 1;
addr->frame_entity = be_get_FrameAddr_entity(left);
left = NULL;
- } else if(right != NULL && be_is_FrameAddr(right)) {
+ } else if(right != NULL && be_is_FrameAddr(right)
+ && !bitset_is_set(non_address_mode_nodes, get_irn_idx(right))) {
assert(addr->base == NULL);
assert(addr->frame_entity == NULL);
addr->base = be_get_FrameAddr_frame(right);
}
-typedef struct mark_evn_t mark_evn_t;
-struct mark_evn_t {
- bitset_t *non_address_nodes;
-};
-
-static void mark_non_address_nodes(mark_evn_t *env, ir_node *node)
+static void mark_non_address_nodes(ir_node *node, void *env)
{
- bitset_set(env->non_address_nodes, get_irn_idx(node));
-
- if(is_Load(node)) {
- ir_node *ptr = get_Load_ptr(node);
- ir_node *mem = get_Load_mem(node);
-
- mark_non_address_nodes(env, mem);
- (void) ptr;
- } else if(is_Store(node)) {
- ir_node *val = get_Store_value(node);
- ir_node *ptr = get_Store_ptr(node);
- ir_node *mem = get_Store_mem(node);
-
- mark_non_address_nodes(env, val);
- mark_non_address_nodes(env, mem);
- (void) ptr;
- } else {
- int i;
- int arity = get_irn_arity(node);
+ int i, arity;
+ ir_node *ptr;
+ ir_node *mem;
+ ir_node *val;
+ ir_node *left;
+ ir_node *right;
+ (void) env;
+
+ switch(get_irn_opcode(node)) {
+ case iro_Load:
+ ptr = get_Load_ptr(node);
+ mem = get_Load_mem(node);
+
+ bitset_set(non_address_mode_nodes, get_irn_idx(mem));
+ break;
+
+ case iro_Store:
+ val = get_Store_value(node);
+ ptr = get_Store_ptr(node);
+ mem = get_Store_mem(node);
+
+ bitset_set(non_address_mode_nodes, get_irn_idx(val));
+ bitset_set(non_address_mode_nodes, get_irn_idx(mem));
+ break;
+
+ case iro_Add:
+ left = get_Add_left(node);
+ right = get_Add_right(node);
+ /* if we can do source address mode then we will never fold the add
+ * into address mode */
+ if(!mode_is_float(get_irn_mode(node)) && (is_immediate_simple(right) ||
+ (!use_source_address_mode(get_nodes_block(node), left, right)
+ && !use_source_address_mode(get_nodes_block(node), right, left))))
+ {
+ break;
+ }
+ bitset_set(non_address_mode_nodes, get_irn_idx(node));
+ /* fallthrough */
+
+ default:
+ arity = get_irn_arity(node);
for(i = 0; i < arity; ++i) {
ir_node *in = get_irn_n(node, i);
- mark_non_address_nodes(env, in);
+ bitset_set(non_address_mode_nodes, get_irn_idx(in));
}
+ break;
}
}
+
+void calculate_non_address_mode_nodes(ir_graph *irg)
+{
+ non_address_mode_nodes = bitset_malloc(get_irg_last_idx(irg));
+
+ irg_walk_graph(irg, NULL, mark_non_address_nodes, NULL);
+}
+
+void free_non_address_mode_nodes(void)
+{
+ bitset_free(non_address_mode_nodes);
+}