aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog22
-rw-r--r--gcc/builtins.c2
-rw-r--r--gcc/c-common.c22
-rw-r--r--gcc/flags.h4
-rw-r--r--gcc/fold-const.c184
-rw-r--r--gcc/libgcc-std.ver12
-rw-r--r--gcc/libgcc2.c203
-rw-r--r--gcc/libgcc2.h13
-rw-r--r--gcc/mklibgcc.in3
-rw-r--r--gcc/toplev.c4
-rw-r--r--gcc/tree-complex.c53
-rw-r--r--gcc/tree.c42
-rw-r--r--gcc/tree.h16
13 files changed, 568 insertions, 12 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index f8348f3..71e5b15 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,25 @@
+2005-02-11 Richard Henderson <rth@redhat.com>
+
+ * tree-complex.c (expand_complex_libcall): New.
+ (expand_complex_multiplication): Use it for c99 compliance.
+ (expand_complex_division): Likewise.
+ * fold-const.c (fold_complex_add, fold_complex_mult): New.
+ (fold): Call them.
+ * builtins.c (built_in_names): Remove const.
+ * tree.c (build_common_builtin_nodes): Build complex arithmetic
+ builtins.
+ * tree.h (BUILT_IN_COMPLEX_MUL_MIN, BUILT_IN_COMPLEX_MUL_MAX): New.
+ (BUILT_IN_COMPLEX_DIV_MIN, BUILT_IN_COMPLEX_DIV_MAX): New.
+ (built_in_names): Remove const.
+ * c-common.c (c_common_type_for_mode): Handle complex modes.
+ * flags.h, toplev.c (flag_complex_method): Rename from
+ flag_complex_divide_method.
+ * libgcc2.c (__divsc3, __divdc3, __divxc3, __divtc3,
+ __mulsc3, __muldc3, __mulxc3, __multc3): New.
+ * libgcc2.h: Declare them.
+ * libgcc-std.ver: Export them.
+ * mklibgcc.in (lib2funcs): Build them.
+
2005-02-11 Steven Bosscher <stevenb@suse.de>
PR tree-optimization/19876
diff --git a/gcc/builtins.c b/gcc/builtins.c
index a206240..420e5dee 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -60,7 +60,7 @@ const char *const built_in_class_names[4]
= {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"};
#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X,
-const char *const built_in_names[(int) END_BUILTINS] =
+const char * built_in_names[(int) END_BUILTINS] =
{
#include "builtins.def"
};
diff --git a/gcc/c-common.c b/gcc/c-common.c
index 15f3648..b414915 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -1611,7 +1611,27 @@ c_common_type_for_mode (enum machine_mode mode, int unsignedp)
if (mode == TYPE_MODE (build_pointer_type (integer_type_node)))
return unsignedp ? make_unsigned_type (mode) : make_signed_type (mode);
- if (VECTOR_MODE_P (mode))
+ if (COMPLEX_MODE_P (mode))
+ {
+ enum machine_mode inner_mode;
+ tree inner_type;
+
+ if (mode == TYPE_MODE (complex_float_type_node))
+ return complex_float_type_node;
+ if (mode == TYPE_MODE (complex_double_type_node))
+ return complex_double_type_node;
+ if (mode == TYPE_MODE (complex_long_double_type_node))
+ return complex_long_double_type_node;
+
+ if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp)
+ return complex_integer_type_node;
+
+ inner_mode = GET_MODE_INNER (mode);
+ inner_type = c_common_type_for_mode (inner_mode, unsignedp);
+ if (inner_type != NULL_TREE)
+ return build_complex_type (inner_type);
+ }
+ else if (VECTOR_MODE_P (mode))
{
enum machine_mode inner_mode = GET_MODE_INNER (mode);
tree inner_type = c_common_type_for_mode (inner_mode, unsignedp);
diff --git a/gcc/flags.h b/gcc/flags.h
index 91e719a..cb8c100 100644
--- a/gcc/flags.h
+++ b/gcc/flags.h
@@ -145,9 +145,9 @@ extern int flag_pcc_struct_return;
/* 0 means straightforward implementation of complex divide acceptable.
1 means wide ranges of inputs must work for complex divide.
- 2 means C99-like requirements for complex divide (not yet implemented). */
+ 2 means C99-like requirements for complex multiply and divide. */
-extern int flag_complex_divide_method;
+extern int flag_complex_method;
/* Nonzero means that we don't want inlining by virtue of -fno-inline,
not just because the tree inliner turned us off. */
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 0d73273..5d6e5c50 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -6306,6 +6306,168 @@ fold_to_nonsharp_ineq_using_bound (tree ineq, tree bound)
return fold (build2 (GE_EXPR, type, a, y));
}
+/* Fold complex addition when both components are accessible by parts.
+ Return non-null if successful. CODE should be PLUS_EXPR for addition,
+ or MINUS_EXPR for subtraction. */
+
+static tree
+fold_complex_add (tree type, tree ac, tree bc, enum tree_code code)
+{
+ tree ar, ai, br, bi, rr, ri, inner_type;
+
+ if (TREE_CODE (ac) == COMPLEX_EXPR)
+ ar = TREE_OPERAND (ac, 0), ai = TREE_OPERAND (ac, 1);
+ else if (TREE_CODE (ac) == COMPLEX_CST)
+ ar = TREE_REALPART (ac), ai = TREE_IMAGPART (ac);
+ else
+ return NULL;
+
+ if (TREE_CODE (bc) == COMPLEX_EXPR)
+ br = TREE_OPERAND (bc, 0), bi = TREE_OPERAND (bc, 1);
+ else if (TREE_CODE (bc) == COMPLEX_CST)
+ br = TREE_REALPART (bc), bi = TREE_IMAGPART (bc);
+ else
+ return NULL;
+
+ inner_type = TREE_TYPE (type);
+
+ rr = fold (build2 (code, inner_type, ar, br));
+ ri = fold (build2 (code, inner_type, ai, bi));
+
+ return fold (build2 (COMPLEX_EXPR, type, rr, ri));
+}
+
+/* Perform some simplifications of complex multiplication when one or more
+ of the components are constants or zeros. Return non-null if successful. */
+
+static tree
+fold_complex_mult (tree type, tree ac, tree bc)
+{
+ tree ar, ai, br, bi, rr, ri, inner_type, zero;
+ bool ar0, ai0, br0, bi0, bi1;
+
+ if (TREE_CODE (ac) == COMPLEX_EXPR)
+ ar = TREE_OPERAND (ac, 0), ai = TREE_OPERAND (ac, 1);
+ else if (TREE_CODE (ac) == COMPLEX_CST)
+ ar = TREE_REALPART (ac), ai = TREE_IMAGPART (ac);
+ else
+ return NULL;
+
+ if (TREE_CODE (bc) == COMPLEX_EXPR)
+ br = TREE_OPERAND (bc, 0), bi = TREE_OPERAND (bc, 1);
+ else if (TREE_CODE (bc) == COMPLEX_CST)
+ br = TREE_REALPART (bc), bi = TREE_IMAGPART (bc);
+ else
+ return NULL;
+
+ inner_type = TREE_TYPE (type);
+ zero = NULL;
+
+ if (SCALAR_FLOAT_TYPE_P (inner_type))
+ {
+ ar0 = ai0 = br0 = bi0 = bi1 = false;
+
+ /* We're only interested in +0.0 here, thus we don't use real_zerop. */
+
+ if (TREE_CODE (ar) == REAL_CST
+ && REAL_VALUES_IDENTICAL (TREE_REAL_CST (ar), dconst0))
+ ar0 = true, zero = ar;
+
+ if (TREE_CODE (ai) == REAL_CST
+ && REAL_VALUES_IDENTICAL (TREE_REAL_CST (ai), dconst0))
+ ai0 = true, zero = ai;
+
+ if (TREE_CODE (br) == REAL_CST
+ && REAL_VALUES_IDENTICAL (TREE_REAL_CST (br), dconst0))
+ br0 = true, zero = br;
+
+ if (TREE_CODE (bi) == REAL_CST)
+ {
+ if (REAL_VALUES_IDENTICAL (TREE_REAL_CST (bi), dconst0))
+ bi0 = true, zero = bi;
+ else if (REAL_VALUES_IDENTICAL (TREE_REAL_CST (bi), dconst1))
+ bi1 = true;
+ }
+ }
+ else
+ {
+ ar0 = integer_zerop (ar);
+ if (ar0)
+ zero = ar;
+ ai0 = integer_zerop (ai);
+ if (ai0)
+ zero = ai;
+ br0 = integer_zerop (br);
+ if (br0)
+ zero = br;
+ bi0 = integer_zerop (bi);
+ if (bi0)
+ {
+ zero = bi;
+ bi1 = false;
+ }
+ else
+ bi1 = integer_onep (bi);
+ }
+
+ /* We won't optimize anything below unless something is zero. */
+ if (zero == NULL)
+ return NULL;
+
+ if (ai0 && br0 && bi1)
+ {
+ rr = zero;
+ ri = ar;
+ }
+ else if (ai0 && bi0)
+ {
+ rr = fold (build2 (MULT_EXPR, inner_type, ar, br));
+ ri = zero;
+ }
+ else if (ai0 && br0)
+ {
+ rr = zero;
+ ri = fold (build2 (MULT_EXPR, inner_type, ar, bi));
+ }
+ else if (ar0 && bi0)
+ {
+ rr = zero;
+ ri = fold (build2 (MULT_EXPR, inner_type, ai, br));
+ }
+ else if (ar0 && br0)
+ {
+ rr = fold (build2 (MULT_EXPR, inner_type, ai, br));
+ rr = fold (build1 (NEGATE_EXPR, inner_type, rr));
+ ri = zero;
+ }
+ else if (bi0)
+ {
+ rr = fold (build2 (MULT_EXPR, inner_type, ar, br));
+ ri = fold (build2 (MULT_EXPR, inner_type, ai, br));
+ }
+ else if (ai0)
+ {
+ rr = fold (build2 (MULT_EXPR, inner_type, ar, br));
+ ri = fold (build2 (MULT_EXPR, inner_type, ar, bi));
+ }
+ else if (br0)
+ {
+ rr = fold (build2 (MULT_EXPR, inner_type, ai, bi));
+ rr = fold (build1 (NEGATE_EXPR, inner_type, rr));
+ ri = fold (build2 (MULT_EXPR, inner_type, ar, bi));
+ }
+ else if (ar0)
+ {
+ rr = fold (build2 (MULT_EXPR, inner_type, ai, bi));
+ rr = fold (build1 (NEGATE_EXPR, inner_type, rr));
+ ri = fold (build2 (MULT_EXPR, inner_type, ai, br));
+ }
+ else
+ return NULL;
+
+ return fold (build2 (COMPLEX_EXPR, type, rr, ri));
+}
+
/* Perform constant folding and related simplification of EXPR.
The related simplifications include x*1 => x, x*0 => 0, etc.,
and application of the associative law.
@@ -6833,6 +6995,14 @@ fold (tree expr)
if (TREE_CODE (arg0) == NEGATE_EXPR
&& reorder_operands_p (TREE_OPERAND (arg0, 0), arg1))
return fold (build2 (MINUS_EXPR, type, arg1, TREE_OPERAND (arg0, 0)));
+
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ {
+ tem = fold_complex_add (type, arg0, arg1, PLUS_EXPR);
+ if (tem)
+ return tem;
+ }
+
if (! FLOAT_TYPE_P (type))
{
if (integer_zerop (arg1))
@@ -7264,6 +7434,13 @@ fold (tree expr)
return fold (build2 (MINUS_EXPR, type, negate_expr (arg1),
TREE_OPERAND (arg0, 0)));
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ {
+ tem = fold_complex_add (type, arg0, arg1, MINUS_EXPR);
+ if (tem)
+ return tem;
+ }
+
if (! FLOAT_TYPE_P (type))
{
if (! wins && integer_zerop (arg0))
@@ -7392,6 +7569,13 @@ fold (tree expr)
negate_expr (arg0),
TREE_OPERAND (arg1, 0)));
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ {
+ tem = fold_complex_mult (type, arg0, arg1);
+ if (tem)
+ return tem;
+ }
+
if (! FLOAT_TYPE_P (type))
{
if (integer_zerop (arg1))
diff --git a/gcc/libgcc-std.ver b/gcc/libgcc-std.ver
index b701fcf..341cf7a 100644
--- a/gcc/libgcc-std.ver
+++ b/gcc/libgcc-std.ver
@@ -241,4 +241,14 @@ GCC_4.0.0 {
__powidf2
__powixf2
__powitf2
-} \ No newline at end of file
+
+ # c99 compliant complex arithmetic
+ __divsc3
+ __divdc3
+ __divxc3
+ __divtc3
+ __mulsc3
+ __muldc3
+ __mulxc3
+ __multc3
+}
diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c
index 9d12d32..df2ecb7 100644
--- a/gcc/libgcc2.c
+++ b/gcc/libgcc2.c
@@ -1501,6 +1501,208 @@ NAME (TYPE x, Wtype m)
#endif
+#if defined(L_mulsc3) || defined(L_divsc3) \
+ || defined(L_muldc3) || defined(L_divdc3) \
+ || (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 80 \
+ && (defined(L_mulxc3) || defined(L_divxc3))) \
+ || (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128 \
+ && (defined(L_multc3) || defined(L_divtc3)))
+
+#undef float
+#undef double
+#undef long
+
+#if defined(L_mulsc3) || defined(L_divsc3)
+# define MTYPE SFtype
+# define CTYPE SCtype
+# define MODE sc
+# define CEXT f
+# define NOTRUNC __FLT_EVAL_METHOD__ == 0
+#elif defined(L_muldc3) || defined(L_divdc3)
+# define MTYPE DFtype
+# define CTYPE DCtype
+# define MODE dc
+# if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 64
+# define CEXT l
+# define NOTRUNC 1
+# else
+# define CEXT
+# define NOTRUNC __FLT_EVAL_METHOD__ == 0 || __FLT_EVAL_METHOD__ == 1
+# endif
+#elif defined(L_mulxc3) || defined(L_divxc3)
+# define MTYPE XFtype
+# define CTYPE XCtype
+# define MODE xc
+# define CEXT l
+# define NOTRUNC 1
+#elif defined(L_multc3) || defined(L_divtc3)
+# define MTYPE TFtype
+# define CTYPE TCtype
+# define MODE tc
+# define CEXT l
+# define NOTRUNC 1
+#else
+# error
+#endif
+
+#define CONCAT3(A,B,C) _CONCAT3(A,B,C)
+#define _CONCAT3(A,B,C) A##B##C
+
+#define CONCAT2(A,B) _CONCAT2(A,B)
+#define _CONCAT2(A,B) A##B
+
+/* All of these would be present in a full C99 implementation of <math.h>
+ and <complex.h>. Our problem is that only a few systems have such full
+ implementations. Further, libgcc_s.so isn't currenly linked against
+ libm.so, and even for systems that do provide full C99, the extra overhead
+ of all programs using libgcc having to link against libm. So avoid it. */
+
+#define isnan(x) __builtin_expect ((x) != (x), 0)
+#define isfinite(x) __builtin_expect (!isnan((x) - (x)), 1)
+#define isinf(x) __builtin_expect (!isnan(x) & !isfinite(x), 0)
+
+#define INFINITY CONCAT2(__builtin_inf, CEXT) ()
+#define I 1i
+
+/* Helpers to make the following code slightly less gross. */
+#define COPYSIGN CONCAT2(__builtin_copysign, CEXT)
+#define FABS CONCAT2(__builtin_fabs, CEXT)
+
+/* Verify that MTYPE matches up with CEXT. */
+extern void *compile_type_assert[sizeof(INFINITY) == sizeof(MTYPE) ? 1 : -1];
+
+/* Ensure that we've lost any extra precision. */
+#if NOTRUNC
+# define TRUNC(x)
+#else
+# define TRUNC(x) __asm__ ("" : "=m"(x) : "m"(x))
+#endif
+
+#if defined(L_mulsc3) || defined(L_muldc3) \
+ || defined(L_mulxc3) || defined(L_multc3)
+
+CTYPE
+CONCAT3(__mul,MODE,3) (MTYPE a, MTYPE b, MTYPE c, MTYPE d)
+{
+ MTYPE ac, bd, ad, bc, x, y;
+
+ ac = a * c;
+ bd = b * d;
+ ad = a * d;
+ bc = b * c;
+
+ TRUNC (ac);
+ TRUNC (bd);
+ TRUNC (ad);
+ TRUNC (bc);
+
+ x = ac - bd;
+ y = ad + bc;
+
+ if (isnan (x) && isnan (y))
+ {
+ /* Recover infinities that computed as NaN + iNaN. */
+ _Bool recalc = 0;
+ if (isinf (a) || isinf (b))
+ {
+ /* z is infinite. "Box" the infinity and change NaNs in
+ the other factor to 0. */
+ a = COPYSIGN (isinf (a) ? 1 : 0, a);
+ b = COPYSIGN (isinf (b) ? 1 : 0, b);
+ if (isnan (c)) c = COPYSIGN (0, c);
+ if (isnan (d)) d = COPYSIGN (0, d);
+ recalc = 1;
+ }
+ if (isinf (c) || isinf (d))
+ {
+ /* w is infinite. "Box" the infinity and change NaNs in
+ the other factor to 0. */
+ c = COPYSIGN (isinf (c) ? 1 : 0, c);
+ d = COPYSIGN (isinf (d) ? 1 : 0, d);
+ if (isnan (a)) a = COPYSIGN (0, a);
+ if (isnan (b)) b = COPYSIGN (0, b);
+ recalc = 1;
+ }
+ if (!recalc
+ && (isinf (ac) || isinf (bd)
+ || isinf (ad) || isinf (bc)))
+ {
+ /* Recover infinities from overflow by changing NaNs to 0. */
+ if (isnan (a)) a = COPYSIGN (0, a);
+ if (isnan (b)) b = COPYSIGN (0, b);
+ if (isnan (c)) c = COPYSIGN (0, c);
+ if (isnan (d)) d = COPYSIGN (0, d);
+ recalc = 1;
+ }
+ if (recalc)
+ {
+ x = INFINITY * (a * c - b * d);
+ y = INFINITY * (a * d + b * c);
+ }
+ }
+
+ return x + I * y;
+}
+#endif /* complex multiply */
+
+#if defined(L_divsc3) || defined(L_divdc3) \
+ || defined(L_divxc3) || defined(L_divtc3)
+
+CTYPE
+CONCAT3(__div,MODE,3) (MTYPE a, MTYPE b, MTYPE c, MTYPE d)
+{
+ MTYPE denom, ratio, x, y;
+
+ /* ??? We can get better behaviour from logrithmic scaling instead of
+ the division. But that would mean starting to link libgcc against
+ libm. We could implement something akin to ldexp/frexp as gcc builtins
+ fairly easily... */
+ if (FABS (c) < FABS (d))
+ {
+ ratio = c / d;
+ denom = (c * ratio) + d;
+ x = ((a * ratio) + b) / denom;
+ y = ((b * ratio) - a) / denom;
+ }
+ else
+ {
+ ratio = d / c;
+ denom = (d * ratio) + c;
+ x = ((b * ratio) + a) / denom;
+ y = (b - (a * ratio)) / denom;
+ }
+
+ /* Recover infinities and zeros that computed as NaN+iNaN; the only cases
+ are non-zero/zero, infinite/finite, and finite/infinite. */
+ if (isnan (x) && isnan (y))
+ {
+ if (denom == 0.0 && (!isnan (a) || !isnan (b)))
+ {
+ x = COPYSIGN (INFINITY, c) * a;
+ y = COPYSIGN (INFINITY, c) * b;
+ }
+ else if ((isinf (a) || isinf (b)) && isfinite (c) && isfinite (d))
+ {
+ a = COPYSIGN (isinf (a) ? 1 : 0, a);
+ b = COPYSIGN (isinf (b) ? 1 : 0, b);
+ x = INFINITY * (a * c + b * d);
+ y = INFINITY * (b * c - a * d);
+ }
+ else if ((isinf (c) || isinf (d)) && isfinite (a) && isfinite (b))
+ {
+ c = COPYSIGN (isinf (c) ? 1 : 0, c);
+ d = COPYSIGN (isinf (d) ? 1 : 0, d);
+ x = 0.0 * (a * c + b * d);
+ y = 0.0 * (b * c - a * d);
+ }
+ }
+
+ return x + I * y;
+}
+#endif /* complex divide */
+
+#endif /* all complex float routines */
+
/* From here on down, the routines use normal data types. */
#define SItype bogus_type
@@ -1772,4 +1974,3 @@ func_ptr __DTOR_LIST__[2];
#endif
#endif /* no INIT_SECTION_ASM_OP and not CTOR_LISTS_DEFINED_EXTERNALLY */
#endif /* L_ctors */
-
diff --git a/gcc/libgcc2.h b/gcc/libgcc2.h
index fb6d914..1657873 100644
--- a/gcc/libgcc2.h
+++ b/gcc/libgcc2.h
@@ -92,12 +92,16 @@ typedef unsigned int UTItype __attribute__ ((mode (TI)));
typedef float SFtype __attribute__ ((mode (SF)));
typedef float DFtype __attribute__ ((mode (DF)));
+typedef _Complex float SCtype __attribute__ ((mode (SC)));
+typedef _Complex float DCtype __attribute__ ((mode (DC)));
#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 80
typedef float XFtype __attribute__ ((mode (XF)));
+typedef _Complex float XCtype __attribute__ ((mode (XC)));
#endif
#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128
typedef float TFtype __attribute__ ((mode (TF)));
+typedef _Complex float TCtype __attribute__ ((mode (TC)));
#endif
#else /* BITS_PER_UNIT != 8 */
@@ -308,12 +312,19 @@ extern DWtype __fixunssfDI (SFtype);
extern SFtype __powisf2 (SFtype, Wtype);
extern DFtype __powidf2 (DFtype, Wtype);
+extern SCtype __divsc3 (SFtype, SFtype, SFtype, SFtype);
+extern SCtype __mulsc3 (SFtype, SFtype, SFtype, SFtype);
+extern DCtype __divdc3 (DFtype, DFtype, DFtype, DFtype);
+extern DCtype __muldc3 (DFtype, DFtype, DFtype, DFtype);
+
#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 80
extern DWtype __fixxfdi (XFtype);
extern DWtype __fixunsxfDI (XFtype);
extern XFtype __floatdixf (DWtype);
extern UWtype __fixunsxfSI (XFtype);
extern XFtype __powixf2 (XFtype, Wtype);
+extern XCtype __divxc3 (XFtype, XFtype, XFtype, XFtype);
+extern XCtype __mulxc3 (XFtype, XFtype, XFtype, XFtype);
#endif
#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128
@@ -321,6 +332,8 @@ extern DWtype __fixunstfDI (TFtype);
extern DWtype __fixtfdi (TFtype);
extern TFtype __floatditf (DWtype);
extern TFtype __powitf2 (TFtype, Wtype);
+extern TCtype __divtc3 (TFtype, TFtype, TFtype, TFtype);
+extern TCtype __multc3 (TFtype, TFtype, TFtype, TFtype);
#endif
#endif /* BITS_PER_UNIT == 8 */
diff --git a/gcc/mklibgcc.in b/gcc/mklibgcc.in
index 7ac1dbf..c88fca9 100644
--- a/gcc/mklibgcc.in
+++ b/gcc/mklibgcc.in
@@ -62,7 +62,8 @@ lib2funcs='_muldi3 _negdi2 _lshrdi3 _ashldi3 _ashrdi3
_addvdi3 _subvsi3 _subvdi3 _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 _ctors
_ffssi2 _ffsdi2 _clz _clzsi2 _clzdi2 _ctzsi2 _ctzdi2 _popcount_tab
_popcountsi2 _popcountdi2 _paritysi2 _paritydi2 _powisf2 _powidf2
- _powixf2 _powitf2'
+ _powixf2 _powitf2 _mulsc3 _muldc3 _mulxc3 _multc3 _divsc3 _divdc3
+ _divxc3 _divtc3'
# Disable SHLIB_LINK if shared libgcc not enabled.
if [ "@enable_shared@" = "no" ]; then
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 2659997..99704a4 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -267,9 +267,9 @@ int flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN;
/* 0 means straightforward implementation of complex divide acceptable.
1 means wide ranges of inputs must work for complex divide.
- 2 means C99-like requirements for complex divide (not yet implemented). */
+ 2 means C99-like requirements for complex multiply and divide. */
-int flag_complex_divide_method = 0;
+int flag_complex_method = 0;
/* Nonzero means that we don't want inlining by virtue of -fno-inline,
not just because the tree inliner turned us off. */
diff --git a/gcc/tree-complex.c b/gcc/tree-complex.c
index 3373326..a5708f3 100644
--- a/gcc/tree-complex.c
+++ b/gcc/tree-complex.c
@@ -105,6 +105,40 @@ expand_complex_addition (block_stmt_iterator *bsi, tree inner_type,
update_complex_assignment (bsi, rr, ri);
}
+/* Expand a complex multiplication or division to a libcall to the c99
+ compliant routines. */
+
+static void
+expand_complex_libcall (block_stmt_iterator *bsi, tree ar, tree ai,
+ tree br, tree bi, enum tree_code code)
+{
+ enum machine_mode mode;
+ enum built_in_function bcode;
+ tree args, fn, stmt, type;
+
+ args = tree_cons (NULL, bi, NULL);
+ args = tree_cons (NULL, br, args);
+ args = tree_cons (NULL, ai, args);
+ args = tree_cons (NULL, ar, args);
+
+ stmt = bsi_stmt (*bsi);
+ type = TREE_TYPE (TREE_OPERAND (stmt, 1));
+
+ mode = TYPE_MODE (type);
+ gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
+ if (code == MULT_EXPR)
+ bcode = BUILT_IN_COMPLEX_MUL_MIN + mode - MIN_MODE_COMPLEX_FLOAT;
+ else if (code == RDIV_EXPR)
+ bcode = BUILT_IN_COMPLEX_DIV_MIN + mode - MIN_MODE_COMPLEX_FLOAT;
+ else
+ gcc_unreachable ();
+ fn = built_in_decls[bcode];
+
+ TREE_OPERAND (stmt, 1)
+ = build3 (CALL_EXPR, type, build_fold_addr_expr (fn), args, NULL);
+ modify_stmt (stmt);
+}
+
/* Expand complex multiplication to scalars:
a * b = (ar*br - ai*bi) + i(ar*bi + br*ai)
*/
@@ -115,6 +149,12 @@ expand_complex_multiplication (block_stmt_iterator *bsi, tree inner_type,
{
tree t1, t2, t3, t4, rr, ri;
+ if (flag_complex_method == 2 && SCALAR_FLOAT_TYPE_P (inner_type))
+ {
+ expand_complex_libcall (bsi, ar, ai, br, bi, MULT_EXPR);
+ return;
+ }
+
t1 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, br);
t2 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ai, bi);
t3 = gimplify_build2 (bsi, MULT_EXPR, inner_type, ar, bi);
@@ -311,18 +351,27 @@ expand_complex_division (block_stmt_iterator *bsi, tree inner_type,
tree ar, tree ai, tree br, tree bi,
enum tree_code code)
{
- switch (flag_complex_divide_method)
+ switch (flag_complex_method)
{
case 0:
/* straightforward implementation of complex divide acceptable. */
expand_complex_div_straight (bsi, inner_type, ar, ai, br, bi, code);
break;
+
+ case 2:
+ if (SCALAR_FLOAT_TYPE_P (inner_type))
+ {
+ expand_complex_libcall (bsi, ar, ai, br, bi, code);
+ return;
+ }
+ /* FALLTHRU */
+
case 1:
/* wide ranges of inputs must work for complex divide. */
expand_complex_div_wide (bsi, inner_type, ar, ai, br, bi, code);
break;
+
default:
- /* C99-like requirements for complex divide (not yet implemented). */
gcc_unreachable ();
}
}
diff --git a/gcc/tree.c b/gcc/tree.c
index 3d41e11..3a0054c 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5903,6 +5903,48 @@ build_common_builtin_nodes (void)
BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter", 0);
local_define_builtin ("__builtin_profile_func_exit", ftype,
BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit", 0);
+
+ /* Complex multiplication and division. These are handled as builtins
+ rather than optabs because emit_library_call_value doesn't support
+ complex. Further, we can do slightly better with folding these
+ beasties if the real and complex parts of the arguments are separate. */
+ {
+ enum machine_mode mode;
+
+ for (mode = MIN_MODE_COMPLEX_FLOAT; mode <= MAX_MODE_COMPLEX_FLOAT; ++mode)
+ {
+ char mode_name_buf[4], *q;
+ const char *p;
+ enum built_in_function mcode, dcode;
+ tree type, inner_type;
+
+ type = lang_hooks.types.type_for_mode (mode, 0);
+ if (type == NULL)
+ continue;
+ inner_type = TREE_TYPE (type);
+
+ tmp = tree_cons (NULL_TREE, inner_type, void_list_node);
+ tmp = tree_cons (NULL_TREE, inner_type, tmp);
+ tmp = tree_cons (NULL_TREE, inner_type, tmp);
+ tmp = tree_cons (NULL_TREE, inner_type, tmp);
+ ftype = build_function_type (type, tmp);
+
+ mcode = BUILT_IN_COMPLEX_MUL_MIN + mode - MIN_MODE_COMPLEX_FLOAT;
+ dcode = BUILT_IN_COMPLEX_DIV_MIN + mode - MIN_MODE_COMPLEX_FLOAT;
+
+ for (p = GET_MODE_NAME (mode), q = mode_name_buf; *p; p++, q++)
+ *q = TOLOWER (*p);
+ *q = '\0';
+
+ built_in_names[mcode] = concat ("__mul", mode_name_buf, "3", NULL);
+ local_define_builtin (built_in_names[mcode], ftype, mcode,
+ built_in_names[mcode], ECF_CONST | ECF_NOTHROW);
+
+ built_in_names[dcode] = concat ("__div", mode_name_buf, "3", NULL);
+ local_define_builtin (built_in_names[dcode], ftype, dcode,
+ built_in_names[dcode], ECF_CONST | ECF_NOTHROW);
+ }
+ }
}
/* HACK. GROSS. This is absolutely disgusting. I wish there was a
diff --git a/gcc/tree.h b/gcc/tree.h
index 4b14d3f..dea3136 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -187,13 +187,27 @@ enum built_in_function
{
#include "builtins.def"
+ /* Complex division routines in libgcc. These are done via builtins
+ because emit_library_call_value can't handle complex values. */
+ BUILT_IN_COMPLEX_MUL_MIN,
+ BUILT_IN_COMPLEX_MUL_MAX
+ = BUILT_IN_COMPLEX_MUL_MIN
+ + MAX_MODE_COMPLEX_FLOAT
+ - MIN_MODE_COMPLEX_FLOAT,
+
+ BUILT_IN_COMPLEX_DIV_MIN,
+ BUILT_IN_COMPLEX_DIV_MAX
+ = BUILT_IN_COMPLEX_DIV_MIN
+ + MAX_MODE_COMPLEX_FLOAT
+ - MIN_MODE_COMPLEX_FLOAT,
+
/* Upper bound on non-language-specific builtins. */
END_BUILTINS
};
#undef DEF_BUILTIN
/* Names for the above. */
-extern const char *const built_in_names[(int) END_BUILTINS];
+extern const char * built_in_names[(int) END_BUILTINS];
/* Helper macros for math builtins. */