+typedef enum _ia32_take_lea_attr {
+ IA32_LEA_ATTR_NONE = 0,
+ IA32_LEA_ATTR_BASE = (1 << 0),
+ IA32_LEA_ATTR_INDEX = (1 << 1),
+ IA32_LEA_ATTR_OFFS = (1 << 2),
+ IA32_LEA_ATTR_SCALE = (1 << 3),
+ IA32_LEA_ATTR_AMSC = (1 << 4),
+ IA32_LEA_ATTR_FENT = (1 << 5)
+} ia32_take_lea_attr;
+
+/**
+ * Decides if we have to keep the LEA operand or if we can assimilate it.
+ */
+static int do_new_lea(ir_node *irn, ir_node *base, ir_node *index, ir_node *lea,
+ int have_am_sc, ia32_code_gen_t *cg)
+{
+ entity *irn_ent = get_ia32_frame_ent(irn);
+ entity *lea_ent = get_ia32_frame_ent(lea);
+ int ret_val = 0;
+ int is_noreg_base = be_is_NoReg(cg, base);
+ int is_noreg_index = be_is_NoReg(cg, index);
+ ia32_am_flavour_t am_flav = get_ia32_am_flavour(lea);
+
+ /* If the Add and the LEA both have a different frame entity set: keep */
+ if (irn_ent && lea_ent && (irn_ent != lea_ent))
+ return IA32_LEA_ATTR_NONE;
+ else if (! irn_ent && lea_ent)
+ ret_val |= IA32_LEA_ATTR_FENT;
+
+ /* If the Add and the LEA both have already an address mode symconst: keep */
+ if (have_am_sc && get_ia32_am_sc(lea))
+ return IA32_LEA_ATTR_NONE;
+ else if (get_ia32_am_sc(lea))
+ ret_val |= IA32_LEA_ATTR_AMSC;
+
+ /* Check the different base-index combinations */
+
+ if (! is_noreg_base && ! is_noreg_index) {
+ /* Assimilate if base is the lea and the LEA is just a Base + Offset calculation */
+ if ((base == lea) && ! (am_flav & ia32_I ? 1 : 0)) {
+ if (am_flav & ia32_O)
+ ret_val |= IA32_LEA_ATTR_OFFS;
+
+ ret_val |= IA32_LEA_ATTR_BASE;
+ }
+ else
+ return IA32_LEA_ATTR_NONE;
+ }
+ else if (! is_noreg_base && is_noreg_index) {
+ /* Base is set but index not */
+ if (base == lea) {
+ /* Base points to LEA: assimilate everything */
+ if (am_flav & ia32_O)
+ ret_val |= IA32_LEA_ATTR_OFFS;
+ if (am_flav & ia32_S)
+ ret_val |= IA32_LEA_ATTR_SCALE;
+ if (am_flav & ia32_I)
+ ret_val |= IA32_LEA_ATTR_INDEX;
+
+ ret_val |= IA32_LEA_ATTR_BASE;
+ }
+ else if (am_flav & ia32_B ? 0 : 1) {
+ /* Base is not the LEA but the LEA is an index only calculation: assimilate */
+ if (am_flav & ia32_O)
+ ret_val |= IA32_LEA_ATTR_OFFS;
+ if (am_flav & ia32_S)
+ ret_val |= IA32_LEA_ATTR_SCALE;
+
+ ret_val |= IA32_LEA_ATTR_INDEX;
+ }
+ else
+ return IA32_LEA_ATTR_NONE;
+ }
+ else if (is_noreg_base && ! is_noreg_index) {
+ /* Index is set but not base */
+ if (index == lea) {
+ /* Index points to LEA: assimilate everything */
+ if (am_flav & ia32_O)
+ ret_val |= IA32_LEA_ATTR_OFFS;
+ if (am_flav & ia32_S)
+ ret_val |= IA32_LEA_ATTR_SCALE;
+ if (am_flav & ia32_B)
+ ret_val |= IA32_LEA_ATTR_BASE;
+
+ ret_val |= IA32_LEA_ATTR_INDEX;
+ }
+ else if (am_flav & ia32_I ? 0 : 1) {
+ /* Index is not the LEA but the LEA is a base only calculation: assimilate */
+ if (am_flav & ia32_O)
+ ret_val |= IA32_LEA_ATTR_OFFS;
+ if (am_flav & ia32_S)
+ ret_val |= IA32_LEA_ATTR_SCALE;
+
+ ret_val |= IA32_LEA_ATTR_BASE;
+ }
+ else
+ return IA32_LEA_ATTR_NONE;
+ }
+ else {
+ assert(0 && "There must have been set base or index");
+ }
+
+ return ret_val;
+}
+
+/**
+ * Adds res before irn into schedule if irn was scheduled.
+ * @param irn The schedule point
+ * @param res The node to be scheduled
+ */
+static INLINE void try_add_to_sched(ir_node *irn, ir_node *res) {
+ if (sched_is_scheduled(irn))
+ sched_add_before(irn, res);
+}
+
+/**
+ * Removes irn from schedule if it was scheduled. If irn is a mode_T node
+ * all it's Projs are removed as well.
+ * @param irn The irn to be removed from schedule
+ */
+static INLINE void try_remove_from_sched(ir_node *irn) {
+ int i, arity;
+
+ if (sched_is_scheduled(irn)) {
+ if (get_irn_mode(irn) == mode_T) {
+ const ir_edge_t *edge;
+ foreach_out_edge(irn, edge) {
+ ir_node *proj = get_edge_src_irn(edge);
+ if (sched_is_scheduled(proj)) {
+ set_irn_n(proj, 0, new_Bad());
+ sched_remove(proj);
+ }
+ }
+ }
+
+ arity = get_irn_arity(irn);
+ for(i = 0; i < arity; ++i) {
+ set_irn_n(irn, i, new_Bad());
+ }
+ sched_remove(irn);
+ }
+}
+