+static ir_tarval *get_modulo_tv_value(ir_tarval *tv, int modulo_val)
+{
+ ir_mode *mode = get_tarval_mode(tv);
+ ir_tarval *modulo_tv = new_tarval_from_long(modulo_val, mode);
+ return tarval_mod(tv, modulo_tv);
+}
+
+typedef ir_node*(*new_shift_func)(dbg_info *dbgi, ir_node *block,
+ ir_node *left, ir_node *right, ir_mode *mode);
+
+/**
+ * Normalisation: if we have a shl/shr with modulo_shift behaviour
+ * then we can use that to minimize the value of Add(x, const) or
+ * Sub(Const, x). In particular this often avoids 1 instruction in some
+ * backends for the Shift(x, Sub(Const, y)) case because it can be replaced
+ * by Shift(x, Minus(y)) which doesnt't need an explicit Const constructed.
+ */
+static ir_node *transform_node_shift_modulo(ir_node *n,
+ new_shift_func new_shift)
+{
+ ir_mode *mode = get_irn_mode(n);
+ int modulo = get_mode_modulo_shift(mode);
+ ir_node *newop = NULL;
+ ir_mode *mode_right;
+ ir_node *block;
+ ir_node *right;
+ ir_graph *irg;
+
+ if (modulo == 0)
+ return n;
+ if (get_mode_arithmetic(mode) != irma_twos_complement)
+ return n;
+ if (!is_po2(modulo))
+ return n;
+
+ irg = get_irn_irg(n);
+ block = get_nodes_block(n);
+ right = get_binop_right(n);
+ mode_right = get_irn_mode(right);
+ if (is_Const(right)) {
+ ir_tarval *tv = get_Const_tarval(right);
+ ir_tarval *tv_mod = get_modulo_tv_value(tv, modulo);
+
+ if (tv_mod == tv)
+ return n;
+
+ newop = new_r_Const(irg, tv_mod);
+ } else if (is_Add(right)) {
+ ir_node *add_right = get_Add_right(right);
+ if (is_Const(add_right)) {
+ ir_tarval *tv = get_Const_tarval(add_right);
+ ir_tarval *tv_mod = get_modulo_tv_value(tv, modulo);
+ ir_node *newconst;
+ if (tv_mod == tv)
+ return n;
+
+ newconst = new_r_Const(irg, tv_mod);
+ newop = new_r_Add(block, get_Add_left(right), newconst,
+ mode_right);
+ }
+ } else if (is_Sub(right)) {
+ ir_node *sub_left = get_Sub_left(right);
+ if (is_Const(sub_left)) {
+ ir_tarval *tv = get_Const_tarval(sub_left);
+ ir_tarval *tv_mod = get_modulo_tv_value(tv, modulo);
+ ir_node *newconst;
+ if (tv_mod == tv)
+ return n;
+
+ newconst = new_r_Const(irg, tv_mod);
+ newop = new_r_Sub(block, newconst, get_Sub_right(right),
+ mode_right);
+ }
+ } else {
+ return n;
+ }
+
+ if (newop != NULL) {
+ dbg_info *dbgi = get_irn_dbg_info(n);
+ ir_node *left = get_binop_left(n);
+ return new_shift(dbgi, block, left, newop, mode);
+ }
+ return n;
+}
+