diff options
author | burley <burley@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-05-18 01:05:14 +0000 |
---|---|---|
committer | burley <burley@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-05-18 01:05:14 +0000 |
commit | 27bc6aeec64466281eb2cb1437b80a8182027d8b (patch) | |
tree | bf672e6d747e77a35f1c2998b3ff0cc8a94b55b6 /gcc/optabs.c | |
parent | 4e99a7e40710ef7e2f79d9ad2a3124a04e1f8cf6 (diff) | |
download | gcc-27bc6aeec64466281eb2cb1437b80a8182027d8b.tar.gz |
improve open-coding of complex divide, use new method in g77
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@26993 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r-- | gcc/optabs.c | 489 |
1 files changed, 385 insertions, 104 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c index 75f224c4809..9b4d4f1059c 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -246,6 +246,14 @@ enum insn_code movcc_gen_code[NUM_MACHINE_MODES]; static int add_equal_note PROTO((rtx, rtx, enum rtx_code, rtx, rtx)); static rtx widen_operand PROTO((rtx, enum machine_mode, enum machine_mode, int, int)); +static int expand_cmplxdiv_straight PROTO((rtx, rtx, rtx, rtx, + rtx, rtx, enum machine_mode, + int, enum optab_methods, + enum mode_class, optab)); +static int expand_cmplxdiv_wide PROTO((rtx, rtx, rtx, rtx, + rtx, rtx, enum machine_mode, + int, enum optab_methods, + enum mode_class, optab)); static enum insn_code can_fix_p PROTO((enum machine_mode, enum machine_mode, int, int *)); static enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode, @@ -348,6 +356,365 @@ widen_operand (op, mode, oldmode, unsignedp, no_extend) return result; } +/* Generate code to perform a straightforward complex divide. */ + +static int +expand_cmplxdiv_straight (rtx real0, rtx real1, rtx imag0, rtx imag1, + rtx realr, rtx imagr, enum machine_mode submode, + int unsignedp, enum optab_methods methods, + enum mode_class class, optab binoptab) +{ + rtx divisor; + rtx real_t, imag_t; + rtx temp1, temp2; + rtx res; + + /* Don't fetch these from memory more than once. */ + real0 = force_reg (submode, real0); + real1 = force_reg (submode, real1); + + if (imag0 != 0) + imag0 = force_reg (submode, imag0); + + imag1 = force_reg (submode, imag1); + + /* Divisor: c*c + d*d. */ + temp1 = expand_binop (submode, smul_optab, real1, real1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, smul_optab, imag1, imag1, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + return 0; + + divisor = expand_binop (submode, add_optab, temp1, temp2, + NULL_RTX, unsignedp, methods); + if (divisor == 0) + return 0; + + if (imag0 == 0) + { + /* Mathematically, ((a)(c-id))/divisor. */ + /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)). */ + + /* Calculate the dividend. */ + real_t = expand_binop (submode, smul_optab, real0, real1, + NULL_RTX, unsignedp, methods); + + imag_t = expand_binop (submode, smul_optab, real0, imag1, + NULL_RTX, unsignedp, methods); + + if (real_t == 0 || imag_t == 0) + return 0; + + imag_t = expand_unop (submode, neg_optab, imag_t, + NULL_RTX, unsignedp); + } + else + { + /* Mathematically, ((a+ib)(c-id))/divider. */ + /* Calculate the dividend. */ + temp1 = expand_binop (submode, smul_optab, real0, real1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, smul_optab, imag0, imag1, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + return 0; + + real_t = expand_binop (submode, add_optab, temp1, temp2, + NULL_RTX, unsignedp, methods); + + temp1 = expand_binop (submode, smul_optab, imag0, real1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, smul_optab, real0, imag1, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + return 0; + + imag_t = expand_binop (submode, sub_optab, temp1, temp2, + NULL_RTX, unsignedp, methods); + + if (real_t == 0 || imag_t == 0) + return 0; + } + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, real_t, divisor, + realr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + real_t, divisor, realr, unsignedp); + + if (res == 0) + return 0; + + if (res != realr) + emit_move_insn (realr, res); + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, imag_t, divisor, + imagr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + imag_t, divisor, imagr, unsignedp); + + if (res == 0) + return 0; + + if (res != imagr) + emit_move_insn (imagr, res); + + return 1; +} + +/* Generate code to perform a wide-input-range-acceptable complex divide. */ + +static int +expand_cmplxdiv_wide (rtx real0, rtx real1, rtx imag0, rtx imag1, + rtx realr, rtx imagr, enum machine_mode submode, + int unsignedp, enum optab_methods methods, + enum mode_class class, optab binoptab) +{ + rtx ratio, divisor; + rtx real_t, imag_t; + rtx temp1, temp2, lab1, lab2; + enum machine_mode mode; + int align; + rtx res; + + /* Don't fetch these from memory more than once. */ + real0 = force_reg (submode, real0); + real1 = force_reg (submode, real1); + + if (imag0 != 0) + imag0 = force_reg (submode, imag0); + + imag1 = force_reg (submode, imag1); + + temp1 = expand_unop (submode, abs_optab, real1, NULL_RTX, + unsignedp); + + temp2 = expand_unop (submode, abs_optab, imag1, NULL_RTX, + unsignedp); + + if (temp1 == 0 || temp2 == 0) + return 0; + + mode = GET_MODE (temp1); + align = GET_MODE_ALIGNMENT (mode); + lab1 = gen_label_rtx (); + emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX, + mode, unsignedp, align, lab1); + + /* |c| >= |d|; use ratio d/c to scale dividend and divisor. */ + + if (class == MODE_COMPLEX_FLOAT) + ratio = expand_binop (submode, binoptab, imag1, real1, + NULL_RTX, unsignedp, methods); + else + ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode, + imag1, real1, NULL_RTX, unsignedp); + + if (ratio == 0) + return 0; + + /* Calculate divisor. */ + + temp1 = expand_binop (submode, smul_optab, imag1, ratio, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0) + return 0; + + divisor = expand_binop (submode, add_optab, temp1, real1, + NULL_RTX, unsignedp, methods); + + if (divisor == 0) + return 0; + + /* Calculate dividend. */ + + if (imag0 == 0) + { + real_t = real0; + + /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)). */ + + imag_t = expand_binop (submode, smul_optab, real0, ratio, + NULL_RTX, unsignedp, methods); + + if (imag_t == 0) + return 0; + + imag_t = expand_unop (submode, neg_optab, imag_t, + NULL_RTX, unsignedp); + + if (real_t == 0 || imag_t == 0) + return 0; + } + else + { + /* Compute (a+ib)/(c+id) as + (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)). */ + + temp1 = expand_binop (submode, smul_optab, imag0, ratio, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0) + return 0; + + real_t = expand_binop (submode, add_optab, temp1, real0, + NULL_RTX, unsignedp, methods); + + temp1 = expand_binop (submode, smul_optab, real0, ratio, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0) + return 0; + + imag_t = expand_binop (submode, sub_optab, imag0, temp1, + NULL_RTX, unsignedp, methods); + + if (real_t == 0 || imag_t == 0) + return 0; + } + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, real_t, divisor, + realr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + real_t, divisor, realr, unsignedp); + + if (res == 0) + return 0; + + if (res != realr) + emit_move_insn (realr, res); + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, imag_t, divisor, + imagr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + imag_t, divisor, imagr, unsignedp); + + if (res == 0) + return 0; + + if (res != imagr) + emit_move_insn (imagr, res); + + lab2 = gen_label_rtx (); + emit_jump_insn (gen_jump (lab2)); + emit_barrier (); + + emit_label (lab1); + + /* |d| > |c|; use ratio c/d to scale dividend and divisor. */ + + if (class == MODE_COMPLEX_FLOAT) + ratio = expand_binop (submode, binoptab, real1, imag1, + NULL_RTX, unsignedp, methods); + else + ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode, + real1, imag1, NULL_RTX, unsignedp); + + if (ratio == 0) + return 0; + + /* Calculate divisor. */ + + temp1 = expand_binop (submode, smul_optab, real1, ratio, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0) + return 0; + + divisor = expand_binop (submode, add_optab, temp1, imag1, + NULL_RTX, unsignedp, methods); + + if (divisor == 0) + return 0; + + /* Calculate dividend. */ + + if (imag0 == 0) + { + /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d). */ + + real_t = expand_binop (submode, smul_optab, real0, ratio, + NULL_RTX, unsignedp, methods); + + imag_t = expand_unop (submode, neg_optab, real0, + NULL_RTX, unsignedp); + + if (real_t == 0 || imag_t == 0) + return 0; + } + else + { + /* Compute (a+ib)/(c+id) as + (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d). */ + + temp1 = expand_binop (submode, smul_optab, real0, ratio, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0) + return 0; + + real_t = expand_binop (submode, add_optab, temp1, imag0, + NULL_RTX, unsignedp, methods); + + temp1 = expand_binop (submode, smul_optab, imag0, ratio, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0) + return 0; + + imag_t = expand_binop (submode, sub_optab, temp1, real0, + NULL_RTX, unsignedp, methods); + + if (real_t == 0 || imag_t == 0) + return 0; + } + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, real_t, divisor, + realr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + real_t, divisor, realr, unsignedp); + + if (res == 0) + return 0; + + if (res != realr) + emit_move_insn (realr, res); + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, imag_t, divisor, + imagr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + imag_t, divisor, imagr, unsignedp); + + if (res == 0) + return 0; + + if (res != imagr) + emit_move_insn (imagr, res); + + emit_label (lab2); + + return 1; +} + /* Generate code to perform an operation specified by BINOPTAB on operands OP0 and OP1, with result having machine-mode MODE. @@ -1219,12 +1586,12 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) start_sequence (); - realr = gen_realpart (submode, target); + realr = gen_realpart (submode, target); imagr = gen_imagpart (submode, target); if (GET_MODE (op0) == mode) { - real0 = gen_realpart (submode, op0); + real0 = gen_realpart (submode, op0); imag0 = gen_imagpart (submode, op0); } else @@ -1232,7 +1599,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) if (GET_MODE (op1) == mode) { - real1 = gen_realpart (submode, op1); + real1 = gen_realpart (submode, op1); imag1 = gen_imagpart (submode, op1); } else @@ -1390,111 +1757,25 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) } else { - /* Divisor is of complex type: - X/(a+ib) */ - rtx divisor; - rtx real_t, imag_t; - rtx temp1, temp2; - - /* Don't fetch these from memory more than once. */ - real0 = force_reg (submode, real0); - real1 = force_reg (submode, real1); - - if (imag0 != 0) - imag0 = force_reg (submode, imag0); - - imag1 = force_reg (submode, imag1); - - /* Divisor: c*c + d*d */ - temp1 = expand_binop (submode, smul_optab, real1, real1, - NULL_RTX, unsignedp, methods); - - temp2 = expand_binop (submode, smul_optab, imag1, imag1, - NULL_RTX, unsignedp, methods); - - if (temp1 == 0 || temp2 == 0) - break; - - divisor = expand_binop (submode, add_optab, temp1, temp2, - NULL_RTX, unsignedp, methods); - if (divisor == 0) - break; - - if (imag0 == 0) + switch (flag_complex_divide_method) { - /* ((a)(c-id))/divisor */ - /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */ - - /* Calculate the dividend */ - real_t = expand_binop (submode, smul_optab, real0, real1, - NULL_RTX, unsignedp, methods); - - imag_t = expand_binop (submode, smul_optab, real0, imag1, - NULL_RTX, unsignedp, methods); - - if (real_t == 0 || imag_t == 0) - break; - - imag_t = expand_unop (submode, neg_optab, imag_t, - NULL_RTX, unsignedp); - } - else - { - /* ((a+ib)(c-id))/divider */ - /* Calculate the dividend */ - temp1 = expand_binop (submode, smul_optab, real0, real1, - NULL_RTX, unsignedp, methods); - - temp2 = expand_binop (submode, smul_optab, imag0, imag1, - NULL_RTX, unsignedp, methods); - - if (temp1 == 0 || temp2 == 0) - break; - - real_t = expand_binop (submode, add_optab, temp1, temp2, - NULL_RTX, unsignedp, methods); - - temp1 = expand_binop (submode, smul_optab, imag0, real1, - NULL_RTX, unsignedp, methods); - - temp2 = expand_binop (submode, smul_optab, real0, imag1, - NULL_RTX, unsignedp, methods); - - if (temp1 == 0 || temp2 == 0) - break; + case 0: + ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1, + realr, imagr, submode, + unsignedp, methods, + class, binoptab); + break; - imag_t = expand_binop (submode, sub_optab, temp1, temp2, - NULL_RTX, unsignedp, methods); + case 1: + ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1, + realr, imagr, submode, + unsignedp, methods, + class, binoptab); + break; - if (real_t == 0 || imag_t == 0) - break; + default: + abort (); } - - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, real_t, divisor, - realr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - real_t, divisor, realr, unsignedp); - - if (res == 0) - break; - else if (res != realr) - emit_move_insn (realr, res); - - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, imag_t, divisor, - imagr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - imag_t, divisor, imagr, unsignedp); - - if (res == 0) - break; - else if (res != imagr) - emit_move_insn (imagr, res); - - ok = 1; } break; |