+/* Generate code to perform a straightforward complex divide. */
+
+static int
+expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
+ unsignedp, methods, class, binoptab)
+ rtx real0, real1, imag0, imag1, realr, 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;
+ optab this_add_optab = add_optab;
+ optab this_sub_optab = sub_optab;
+ optab this_neg_optab = neg_optab;
+ optab this_mul_optab = smul_optab;
+
+ if (binoptab == sdivv_optab)
+ {
+ this_add_optab = addv_optab;
+ this_sub_optab = subv_optab;
+ this_neg_optab = negv_optab;
+ this_mul_optab = smulv_optab;
+ }
+
+ /* 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, this_mul_optab, real1, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, this_mul_optab, imag1, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ divisor = expand_binop (submode, this_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, this_mul_optab, real0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ imag_t = expand_binop (submode, this_mul_optab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+
+ imag_t = expand_unop (submode, this_neg_optab, imag_t,
+ NULL_RTX, unsignedp);
+ }
+ else
+ {
+ /* Mathematically, ((a+ib)(c-id))/divider. */
+ /* Calculate the dividend. */
+ temp1 = expand_binop (submode, this_mul_optab, real0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, this_mul_optab, imag0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ real_t = expand_binop (submode, this_add_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, this_mul_optab, imag0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, this_mul_optab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ imag_t = expand_binop (submode, this_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;
+}
+\f
+/* Generate code to perform a wide-input-range-acceptable complex divide. */
+
+static int
+expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
+ unsignedp, methods, class, binoptab)
+ rtx real0, real1, imag0, imag1, realr, 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;
+ optab this_add_optab = add_optab;
+ optab this_sub_optab = sub_optab;
+ optab this_neg_optab = neg_optab;
+ optab this_mul_optab = smul_optab;
+
+ if (binoptab == sdivv_optab)
+ {
+ this_add_optab = addv_optab;
+ this_sub_optab = subv_optab;
+ this_neg_optab = negv_optab;
+ this_mul_optab = smulv_optab;
+ }
+
+ /* 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);
+
+ /* XXX What's an "unsigned" complex number? */
+ if (unsignedp)
+ {
+ temp1 = real1;
+ temp2 = imag1;
+ }
+ else
+ {
+ temp1 = expand_abs (submode, real1, NULL_RTX, unsignedp, 1);
+ temp2 = expand_abs (submode, imag1, NULL_RTX, unsignedp, 1);
+ }
+
+ 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, this_mul_optab, imag1, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ divisor = expand_binop (submode, this_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, this_mul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (imag_t == 0)
+ return 0;
+
+ imag_t = expand_unop (submode, this_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, this_mul_optab, imag0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ real_t = expand_binop (submode, this_add_optab, temp1, real0,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ imag_t = expand_binop (submode, this_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, this_mul_optab, real1, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ divisor = expand_binop (submode, this_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, this_mul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ imag_t = expand_unop (submode, this_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, this_mul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ real_t = expand_binop (submode, this_add_optab, temp1, imag0,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ imag_t = expand_binop (submode, this_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;
+}
+\f