diff options
-rw-r--r-- | gcc/ChangeLog | 22 | ||||
-rw-r--r-- | gcc/builtins.c | 2 | ||||
-rw-r--r-- | gcc/c-common.c | 22 | ||||
-rw-r--r-- | gcc/flags.h | 4 | ||||
-rw-r--r-- | gcc/fold-const.c | 184 | ||||
-rw-r--r-- | gcc/libgcc-std.ver | 12 | ||||
-rw-r--r-- | gcc/libgcc2.c | 203 | ||||
-rw-r--r-- | gcc/libgcc2.h | 13 | ||||
-rw-r--r-- | gcc/mklibgcc.in | 3 | ||||
-rw-r--r-- | gcc/toplev.c | 4 | ||||
-rw-r--r-- | gcc/tree-complex.c | 53 | ||||
-rw-r--r-- | gcc/tree.c | 42 | ||||
-rw-r--r-- | gcc/tree.h | 16 |
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 (); } } @@ -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 @@ -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. */ |