- tarval_sub() now has an additional parameter, needed for
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Fri, 1 Aug 2008 10:26:43 +0000 (10:26 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Fri, 1 Aug 2008 10:26:43 +0000 (10:26 +0000)
  mode_ref - mode_ref = mode_int operations
- more doxygen docu added
- allow reference mode arithmetic

[r20888]

include/libfirm/tv.h
ir/ana/irmemory.c
ir/ir/irarch.c
ir/ir/iropt.c
ir/opt/boolopt.c
ir/opt/combo.c
ir/opt/opt_osr.c
ir/tv/tv.c

index 870dc06..a5ff791 100644 (file)
@@ -364,6 +364,8 @@ typedef enum _tarval_int_overflow_mode_t {
 
 /**
  * Sets the overflow mode for integer operations.
+ *
+ * @param ov_mode  one of teh overflow modes
  */
 void tarval_set_integer_overflow_mode(tarval_int_overflow_mode_t ov_mode);
 
@@ -442,55 +444,173 @@ tarval *tarval_convert_to(tarval *src, ir_mode *mode);
  *   The sort member of the struct mode defines which operations are valid
  */
 
-/** Bitwise Negation of a tarval. */
+/**
+ * Bitwise Negation of a tarval.
+ *
+ * @param a  the first tarval
+ *
+ * @return ~a or tarval_bad
+ */
 tarval *tarval_not(tarval *a);
 
-/** Arithmetic Negation of a tarval. */
+/**
+ * Arithmetic Negation of a tarval.
+ *
+ * @param a  the first tarval
+ *
+ * @return -a or tarval_bad
+ */
 tarval *tarval_neg(tarval *a);
 
-/** Addition of two tarvals. */
+/**
+ * Addition of two tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a + b or tarval_bad
+ */
 tarval *tarval_add(tarval *a, tarval *b);
 
-/** Subtraction from a tarval. */
-tarval *tarval_sub(tarval *a, tarval *b);
+/**
+ * Subtraction from a tarval.
+ *
+ * @param a         the first tarval
+ * @param b         the second tarval
+ * @param dst_mode  the mode of the result, needed for mode_P - mode_P, else NULL
+ *
+ * @return a - b or tarval_bad
+ */
+tarval *tarval_sub(tarval *a, tarval *b, ir_mode *dst_mode);
 
-/** Multiplication of tarvals. */
+/**
+ * Multiplication of tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a * b or tarval_bad
+ */
 tarval *tarval_mul(tarval *a, tarval *b);
 
-/** 'Exact' division of two tarvals. */
+/**
+ * Division of two floating point tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a / b or tarval_bad
+ */
 tarval *tarval_quo(tarval *a, tarval *b);
 
-/** Integer division of two tarvals. */
+/**
+ * Integer division of two tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a / b or tarval_bad
+ */
 tarval *tarval_div(tarval *a, tarval *b);
 
-/** Remainder of integer division. */
+/**
+ * Remainder of integer division.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a % b or tarval_bad
+ */
 tarval *tarval_mod(tarval *a, tarval *b);
 
-/** Integer division AND remainder. */
+/**
+ * Integer division AND remainder.
+ *
+ * @param a        the first tarval
+ * @param b        the second tarval
+ * @param mod_res  after return, contains the remainder result, a % b or tarval_bad
+ *
+ * @return a / b or tarval_bad
+ */
 tarval *tarval_divmod(tarval *a, tarval *b, tarval **mod_res);
 
-/** Absolute value of a tarval. */
+/**
+ * Absolute value of a tarval.
+ *
+ * @param a  the first tarval
+ *
+ * @return |a| or tarval_bad
+ */
 tarval *tarval_abs(tarval *a);
 
-/** Bitwise and. */
+/**
+ * Bitwise and of two integer tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a & b or tarval_bad
+ */
 tarval *tarval_and(tarval *a, tarval *b);
 
-/** Bitwise or. */
+/**
+ * Bitwise or of two integer tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a | b or tarval_bad
+ */
 tarval *tarval_or(tarval *a, tarval *b);
 
-/** Bitwise exclusive or. */
+/**
+ * Bitwise exclusive or of two integer tarvals.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a ^ b or tarval_bad
+ */
 tarval *tarval_eor(tarval *a, tarval *b);
 
-/** Left shift. */
+/**
+ * Logical Left shift.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a << b or tarval_bad
+ */
 tarval *tarval_shl(tarval *a, tarval *b);
 
-/** Unsigned (logical) right shift. */
+/**
+ * Unsigned (logical) right shift.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a >>u b or tarval_bad
+ */
 tarval *tarval_shr(tarval *a, tarval *b);
 
-/** Signed (arithmetic) right shift. */
+/**
+ * Signed (arithmetic) right shift.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a >>s b or tarval_bad
+ */
 tarval *tarval_shrs(tarval *a, tarval *b);
 
-/** Rotation to left. */
+/**
+ * Rotation to left.
+ *
+ * @param a  the first tarval
+ * @param b  the second tarval
+ *
+ * @return a <<L>> b or tarval_bad
+ */
 tarval *tarval_rotl(tarval *a, tarval *b);
 
 /**
index 0f7ee48..c3f8503 100644 (file)
@@ -181,7 +181,7 @@ static ir_alias_relation different_index(ir_node *idx1, ir_node *idx2, int size)
                                                return ir_no_alias;
                                        }
                                        /* tv_size > tv2, so we can subtract without overflow */
-                                       tv2 = tarval_sub(tv_size, tv2);
+                                       tv2 = tarval_sub(tv_size, tv2, NULL);
 
                                        /* tv1 is < 0, so we can negate it */
                                        tv1 = tarval_neg(tv1);
@@ -200,7 +200,7 @@ static ir_alias_relation different_index(ir_node *idx1, ir_node *idx2, int size)
                        tv2 = t;
                }
                /* tv1 is now the "smaller" one */
-               tv      = tarval_sub(tv2, tv1);
+               tv      = tarval_sub(tv2, tv1, NULL);
                tv_size = new_tarval_from_long(size, get_tarval_mode(tv));
                return tarval_cmp(tv_size, tv) & (pn_Cmp_Eq|pn_Cmp_Lt) ? ir_no_alias : ir_may_alias;
        }
index 5f9fdfe..549b747 100644 (file)
@@ -634,7 +634,7 @@ static int tv_ld2(tarval *tv, int bits) {
 #define SHL(a, b) tarval_shl(a, b)
 #define SHR(a, b) tarval_shr(a, b)
 #define ADD(a, b) tarval_add(a, b)
-#define SUB(a, b) tarval_sub(a, b)
+#define SUB(a, b) tarval_sub(a, b, NULL)
 #define MUL(a, b) tarval_mul(a, b)
 #define DIV(a, b) tarval_div(a, b)
 #define MOD(a, b) tarval_mod(a, b)
index 7603a49..d997ac0 100644 (file)
@@ -119,7 +119,7 @@ static tarval *computed_value_Add(ir_node *n) {
        tarval *ta = value_of(a);
        tarval *tb = value_of(b);
 
-       if ((ta != tarval_bad) && (tb != tarval_bad) && (get_irn_mode(a) == get_irn_mode(b)))
+       if ((ta != tarval_bad) && (tb != tarval_bad))
                return tarval_add(ta, tb);
 
        return tarval_bad;
@@ -130,20 +130,21 @@ static tarval *computed_value_Add(ir_node *n) {
  * Special case: a - a
  */
 static tarval *computed_value_Sub(ir_node *n) {
-       ir_node *a = get_Sub_left(n);
-       ir_node *b = get_Sub_right(n);
-       tarval *ta;
-       tarval *tb;
+       ir_mode *mode = get_irn_mode(n);
+       ir_node *a    = get_Sub_left(n);
+       ir_node *b    = get_Sub_right(n);
+       tarval  *ta;
+       tarval  *tb;
 
        /* a - a */
        if (a == b && !is_Bad(a))
-               return get_mode_null(get_irn_mode(n));
+               return get_mode_null(mode);
 
        ta = value_of(a);
        tb = value_of(b);
 
-       if ((ta != tarval_bad) && (tb != tarval_bad) && (get_irn_mode(a) == get_irn_mode(b)))
-               return tarval_sub(ta, tb);
+       if ((ta != tarval_bad) && (tb != tarval_bad))
+               return tarval_sub(ta, tb, mode);
 
        return tarval_bad;
 }  /* computed_value_Sub */
@@ -243,8 +244,7 @@ static tarval *computed_value_Quot(ir_node *n) {
        tarval *ta = value_of(a);
        tarval *tb = value_of(b);
 
-       /* This was missing in original implementation. Why? */
-       if ((ta != tarval_bad) && (tb != tarval_bad) && (get_irn_mode(a) == get_irn_mode(b))) {
+       if ((ta != tarval_bad) && (tb != tarval_bad)) {
                if (tb != get_mode_null(get_tarval_mode(tb)))   /* div by zero: return tarval_bad */
                        return tarval_quo(ta, tb);
        }
@@ -1798,6 +1798,24 @@ static int is_const_Phi(ir_node *n) {
                return 1;
 }  /* is_const_Phi */
 
+typedef tarval *(*tarval_sub_type)(tarval *a, tarval *b, ir_mode *mode);
+typedef tarval *(*tarval_binop_type)(tarval *a, tarval *b);
+
+/**
+ * Wrapper for the tarval binop evaluation, tarval_sub has one more parameter.
+ */
+static tarval *do_eval(tarval *(*eval)(), tarval *a, tarval *b, ir_mode *mode) {
+       if (eval == tarval_sub) {
+               tarval_sub_type func = (tarval_sub_type)eval;
+
+               return func(a, b, mode);
+       } else {
+               tarval_binop_type func = (tarval_binop_type)eval;
+
+               return func(a, b);
+       }
+}
+
 /**
  * Apply an evaluator on a binop with a constant operators (and one Phi).
  *
@@ -1809,7 +1827,7 @@ static int is_const_Phi(ir_node *n) {
  *
  * @return a new Phi node if the conversion was successful, NULL else
  */
-static ir_node *apply_binop_on_phi(ir_node *phi, tarval *other, tarval *(*eval)(tarval *, tarval *), ir_mode *mode, int left) {
+static ir_node *apply_binop_on_phi(ir_node *phi, tarval *other, tarval *(*eval)(), ir_mode *mode, int left) {
        tarval   *tv;
        void     **res;
        ir_node  *pred;
@@ -1821,7 +1839,7 @@ static ir_node *apply_binop_on_phi(ir_node *phi, tarval *other, tarval *(*eval)(
                for (i = 0; i < n; ++i) {
                        pred = get_irn_n(phi, i);
                        tv   = get_Const_tarval(pred);
-                       tv   = eval(other, tv);
+                       tv   = do_eval(eval, other, tv, mode);
 
                        if (tv == tarval_bad) {
                                /* folding failed, bad */
@@ -1833,7 +1851,7 @@ static ir_node *apply_binop_on_phi(ir_node *phi, tarval *other, tarval *(*eval)(
                for (i = 0; i < n; ++i) {
                        pred = get_irn_n(phi, i);
                        tv   = get_Const_tarval(pred);
-                       tv   = eval(tv, other);
+                       tv   = do_eval(eval, tv, other, mode);
 
                        if (tv == tarval_bad) {
                                /* folding failed, bad */
@@ -1861,7 +1879,7 @@ static ir_node *apply_binop_on_phi(ir_node *phi, tarval *other, tarval *(*eval)(
  *
  * @return a new Phi node if the conversion was successful, NULL else
  */
-static ir_node *apply_binop_on_2_phis(ir_node *a, ir_node *b, tarval *(*eval)(tarval *, tarval *), ir_mode *mode) {
+static ir_node *apply_binop_on_2_phis(ir_node *a, ir_node *b, tarval *(*eval)(), ir_mode *mode) {
        tarval   *tv_l, *tv_r, *tv;
        void     **res;
        ir_node  *pred;
@@ -1879,7 +1897,7 @@ static ir_node *apply_binop_on_2_phis(ir_node *a, ir_node *b, tarval *(*eval)(ta
                tv_l = get_Const_tarval(pred);
                pred = get_irn_n(b, i);
                tv_r = get_Const_tarval(pred);
-               tv   = eval(tv_l, tv_r);
+               tv   = do_eval(eval, tv_l, tv_r, mode);
 
                if (tv == tarval_bad) {
                        /* folding failed, bad */
@@ -4047,7 +4065,7 @@ static ir_node *transform_node_Proj_Cmp(ir_node *proj) {
                                /* c > 0 : a < c  ==>  a <= (c-1)    a >= c  ==>  a > (c-1) */
                                if ((proj_nr == pn_Cmp_Lt || proj_nr == pn_Cmp_Ge) &&
                                        tarval_cmp(tv, get_mode_null(mode)) == pn_Cmp_Gt) {
-                                       tv = tarval_sub(tv, get_mode_one(mode));
+                                       tv = tarval_sub(tv, get_mode_one(mode), NULL);
 
                                        if (tv != tarval_bad) {
                                                proj_nr ^= pn_Cmp_Eq;
@@ -4115,7 +4133,7 @@ static ir_node *transform_node_Proj_Cmp(ir_node *proj) {
                                                        }
 
                                                        if (tv2 != tarval_bad) {
-                                                               tv2 = tarval_sub(tv, tv2);
+                                                               tv2 = tarval_sub(tv, tv2, NULL);
 
                                                                if (tv2 != tarval_bad) {
                                                                        left    = a;
@@ -4127,7 +4145,7 @@ static ir_node *transform_node_Proj_Cmp(ir_node *proj) {
                                                }
                                                /* -a == c ==> a == -c, -a != c ==> a != -c */
                                                else if (is_Minus(left)) {
-                                                       tarval *tv2 = tarval_sub(get_mode_null(mode), tv);
+                                                       tarval *tv2 = tarval_sub(get_mode_null(mode), tv, NULL);
 
                                                        if (tv2 != tarval_bad) {
                                                                left    = get_Minus_op(left);
@@ -4284,7 +4302,7 @@ static ir_node *transform_node_Proj_Cmp(ir_node *proj) {
                                                tarval  *cond   = new_tarval_from_long(get_mode_size_bits(mode), get_tarval_mode(tv1));
                                                ir_node *sl, *blk;
 
-                                               cond = tarval_sub(cond, tv1);
+                                               cond = tarval_sub(cond, tv1, NULL);
                                                cond = tarval_shrs(tv, cond);
 
                                                if (!tarval_is_all_one(cond) && !tarval_is_null(cond)) {
@@ -4326,7 +4344,7 @@ static ir_node *transform_node_Proj_Cmp(ir_node *proj) {
                                        ir_node *blk  = get_irn_n(op, -1);
                                        ir_mode *mode = get_irn_mode(v);
 
-                                       tv = tarval_sub(tv, get_mode_one(mode));
+                                       tv = tarval_sub(tv, get_mode_one(mode), NULL);
                                        left = new_rd_And(get_irn_dbg_info(op), current_ir_graph, blk, v, new_Const(mode, tv), mode);
                                        changed |= 1;
                                        DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_MOD_TO_AND);
@@ -4915,7 +4933,7 @@ static ir_node *transform_node_shl_shr(ir_node *n) {
 
        pnc = tarval_cmp(tv_shl, tv_shr);
        if (pnc == pn_Cmp_Lt || pnc == pn_Cmp_Eq) {
-               tv_shift  = tarval_sub(tv_shr, tv_shl);
+               tv_shift  = tarval_sub(tv_shr, tv_shl, NULL);
                new_const = new_Const(get_tarval_mode(tv_shift), tv_shift);
                if (need_shrs) {
                        new_shift = new_rd_Shrs(dbgi, irg, block, x, new_const, mode);
@@ -4924,7 +4942,7 @@ static ir_node *transform_node_shl_shr(ir_node *n) {
                }
        } else {
                assert(pnc == pn_Cmp_Gt);
-               tv_shift  = tarval_sub(tv_shl, tv_shr);
+               tv_shift  = tarval_sub(tv_shl, tv_shr, NULL);
                new_const = new_Const(get_tarval_mode(tv_shift), tv_shift);
                new_shift = new_rd_Shl(dbgi, irg, block, x, new_const, mode);
        }
@@ -5160,10 +5178,10 @@ static ir_node *transform_node_Mux(ir_node *n) {
                tarval *diff, *min;
 
                if (tarval_cmp(a, b) & pn_Cmp_Gt) {
-                       diff = tarval_sub(a, b);
+                       diff = tarval_sub(a, b, NULL);
                        min  = b;
                } else {
-                       diff = tarval_sub(b, a);
+                       diff = tarval_sub(b, a, NULL);
                        min  = a;
                }
 
@@ -5174,8 +5192,8 @@ static ir_node *transform_node_Mux(ir_node *n) {
 
 
                        conds[0] = sel;
-                       vals[0] = new_Const(mode, tarval_sub(a, min));
-                       vals[1] = new_Const(mode, tarval_sub(b, min));
+                       vals[0] = new_Const(mode, tarval_sub(a, min, NULL));
+                       vals[1] = new_Const(mode, tarval_sub(b, min, NULL));
                        n = new_rd_Psi(dbg, irg, block, 1, conds, vals, mode);
                        n = new_rd_Add(dbg, irg, block, n, new_Const(mode, min), mode);
                        return n;
index ca97dc4..6ff2a34 100644 (file)
@@ -100,7 +100,7 @@ static ir_node *bool_and(cond_pair* const cpair)
                                                 (pnc_hi == pn_Cmp_Eq || pnc_hi == pn_Cmp_Ge || pnc_hi == pn_Cmp_Gt)) {
                /* x >=|>|!= lo || x ==|>=|> hi -> x ==|>=|> hi */
                return proj_hi;
-       } else if (tarval_is_one(tarval_sub(tv_hi, tv_lo))) { /* lo + 1 == hi */
+       } else if (tarval_is_one(tarval_sub(tv_hi, tv_lo, NULL))) { /* lo + 1 == hi */
                if (pnc_lo == pn_Cmp_Ge && pnc_hi == pn_Cmp_Lt) {
                        /* x >= c || x < c + 1 -> x == c */
                        ir_graph *const irg   = current_ir_graph;
@@ -161,7 +161,7 @@ static ir_node *bool_or(cond_pair *const cpair)
                                                 (pnc_hi == pn_Cmp_Eq || pnc_hi == pn_Cmp_Ge || pnc_hi == pn_Cmp_Gt)) {
                /* x >=|>|!= lo || x ==|>=|> hi -> x >=|>|!= lo */
                return proj_lo;
-       } else if (tarval_is_one(tarval_sub(tv_hi, tv_lo))) { /* lo + 1 == hi */
+       } else if (tarval_is_one(tarval_sub(tv_hi, tv_lo, NULL))) { /* lo + 1 == hi */
                if (pnc_lo == pn_Cmp_Lt && pnc_hi == pn_Cmp_Ge) {
                        /* x < c || x >= c + 1 -> x != c */
                        ir_graph *const irg   = current_ir_graph;
index 0c2b981..96746bc 100644 (file)
@@ -1202,7 +1202,7 @@ static void compute_Sub(node_t *node) {
                node->type.tv = tarval_top;
        } else if (is_con(a) && is_con(b)) {
                if (is_tarval(a.tv) && is_tarval(b.tv)) {
-                       node->type.tv = tarval_sub(a.tv, b.tv);
+                       node->type.tv = tarval_sub(a.tv, b.tv, get_irn_mode(sub));
                } else if (is_tarval(a.tv) && tarval_is_null(a.tv)) {
                        node->type = b;
                } else if (is_tarval(b.tv) && tarval_is_null(b.tv)) {
index 7144e18..b709a6f 100644 (file)
@@ -1112,8 +1112,8 @@ static ir_node *applyOneEdge(ir_node *iv, ir_node *rc, LFTR_edge *e, iv_env *env
                        DB((dbg, LEVEL_4, " + %+F", tv_r));
                        break;
                case iro_Sub:
-                       tv      = tarval_sub(tv_l, tv_r);
-                       tv_init = tarval_sub(tv_init, tv_r);
+                       tv      = tarval_sub(tv_l, tv_r, NULL);
+                       tv_init = tarval_sub(tv_init, tv_r, NULL);
                        DB((dbg, LEVEL_4, " - %+F", tv_r));
                        break;
                default:
@@ -1125,7 +1125,7 @@ static ir_node *applyOneEdge(ir_node *iv, ir_node *rc, LFTR_edge *e, iv_env *env
                        tv = tarval_add(tv, tv_incr);
                } else {
                        assert(pscc->code == iro_Sub);
-                       tv = tarval_sub(tv, tv_incr);
+                       tv = tarval_sub(tv, tv_incr, NULL);
                }
 
                tarval_set_integer_overflow_mode(ovmode);
index 083e630..96fcc6e 100644 (file)
@@ -1101,7 +1101,7 @@ tarval *tarval_neg(tarval *a) {
 tarval *tarval_add(tarval *a, tarval *b) {
        tarval  *res;
        char    *buffer;
-       ir_mode *dst_mode, *src_mode = NULL;
+       ir_mode *imm_mode, *dst_mode = NULL;
 
        assert(a);
        assert(b);
@@ -1112,24 +1112,24 @@ tarval *tarval_add(tarval *a, tarval *b) {
        }
 
        if (mode_is_reference(a->mode)) {
-               src_mode = a->mode;
-               dst_mode = find_unsigned_mode(a->mode);
+               dst_mode = a->mode;
+               imm_mode = find_unsigned_mode(a->mode);
 
-               if (dst_mode == NULL)
+               if (imm_mode == NULL)
                        return tarval_bad;
 
-               a = tarval_convert_to(a, dst_mode);
-               b = tarval_convert_to(b, dst_mode);
+               a = tarval_convert_to(a, imm_mode);
+               b = tarval_convert_to(b, imm_mode);
        }
        if (mode_is_reference(b->mode)) {
-               src_mode = b->mode;
-               dst_mode = find_unsigned_mode(b->mode);
+               dst_mode = b->mode;
+               imm_mode = find_unsigned_mode(b->mode);
 
-               if (dst_mode == 0)
+               if (imm_mode == 0)
                        return tarval_bad;
 
-               a = tarval_convert_to(a, dst_mode);
-               b = tarval_convert_to(b, dst_mode);
+               a = tarval_convert_to(a, imm_mode);
+               b = tarval_convert_to(b, imm_mode);
        }
 
        assert(a->mode == b->mode);
@@ -1153,18 +1153,17 @@ tarval *tarval_add(tarval *a, tarval *b) {
        default:
                return tarval_bad;
        }
-       if (src_mode != NULL)
-               return tarval_convert_to(res, src_mode);
+       if (dst_mode != NULL)
+               return tarval_convert_to(res, dst_mode);
        return res;
 }
 
 /*
  * subtraction
  */
-tarval *tarval_sub(tarval *a, tarval *b) {
+tarval *tarval_sub(tarval *a, tarval *b, ir_mode *dst_mode) {
        tarval  *res;
        char    *buffer;
-       ir_mode *dst_mode, *src_mode = NULL;
 
        assert(a);
        assert(b);
@@ -1173,27 +1172,16 @@ tarval *tarval_sub(tarval *a, tarval *b) {
                /* vector arithmetic not implemented yet */
                return tarval_bad;
        }
-       if (mode_is_reference(a->mode)) {
-               src_mode = a->mode;
-               dst_mode = find_unsigned_mode(a->mode);
-
-               if (dst_mode == NULL)
-                       return tarval_bad;
 
-               a = tarval_convert_to(a, dst_mode);
-               b = tarval_convert_to(b, dst_mode);
-       }
-       if (mode_is_reference(b->mode)) {
-               src_mode = b->mode;
-               dst_mode = find_unsigned_mode(b->mode);
-
-               if (dst_mode == 0)
-                       return tarval_bad;
-
-               a = tarval_convert_to(a, dst_mode);
-               b = tarval_convert_to(b, dst_mode);
+       if (dst_mode != NULL) {
+               if (mode_is_reference(a->mode)) {
+                       a = tarval_convert_to(a, dst_mode);
+               }
+               if (mode_is_reference(b->mode)) {
+                       b = tarval_convert_to(b, dst_mode);
+               }
+               assert(a->mode == dst_mode);
        }
-
        assert(a->mode == b->mode);
 
        switch (get_mode_sort(a->mode)) {
@@ -1201,23 +1189,18 @@ tarval *tarval_sub(tarval *a, tarval *b) {
                /* modes of a,b are equal, so result has mode of a as this might be the character */
                buffer = alloca(sc_get_buffer_length());
                sc_sub(a->value, b->value, buffer);
-               res = get_tarval_overflow(buffer, a->length, a->mode);
-               break;
+               return get_tarval_overflow(buffer, a->length, a->mode);
 
        case irms_float_number:
                if (no_float)
                        return tarval_bad;
 
                fc_sub(a->value, b->value, NULL);
-               res = get_tarval_overflow(fc_get_buffer(), fc_get_buffer_length(), a->mode);
-               break;
+               return get_tarval_overflow(fc_get_buffer(), fc_get_buffer_length(), a->mode);
 
        default:
                return tarval_bad;
        }
-       if (src_mode != NULL)
-               return tarval_convert_to(res, src_mode);
-       return res;
 }
 
 /*