X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fbuiltins.c;h=f3c1623785f4db3b37986a74d9144827cec644ce;hb=a1da787df346866e3a1f32b089a2ad5b68be53c8;hp=5375c5c77aba2205f5de02247eee4e408945d0cc;hpb=c7d32ff6193ad93643fda556bc0db619771241f1;p=gcc.git diff --git a/gcc/builtins.c b/gcc/builtins.c index 5375c5c77ab..f3c1623785f 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -47,6 +47,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "langhooks.h" #include "basic-block.h" #include "tree-mudflap.h" +#include "tree-flow.h" #ifndef PAD_VARARGS_DOWN #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN @@ -151,6 +152,7 @@ static tree fold_builtin_cbrt (tree, tree); static tree fold_builtin_pow (tree, tree, tree); static tree fold_builtin_powi (tree, tree, tree); static tree fold_builtin_cos (tree, tree, tree); +static tree fold_builtin_cosh (tree, tree, tree); static tree fold_builtin_tan (tree, tree); static tree fold_builtin_trunc (tree, tree); static tree fold_builtin_floor (tree, tree); @@ -207,6 +209,9 @@ static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t), const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, bool); static tree do_mpfr_arg2 (tree, tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)); +static tree do_mpfr_arg3 (tree, tree, tree, tree, + int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)); +static tree do_mpfr_sincos (tree, tree, tree); /* Return true if NODE should be considered for inline expansion regardless of the optimization level. This means whenever a function is invoked with @@ -2089,7 +2094,7 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) /* Check if sincos insn is available, otherwise fallback to sin or cos insn. */ - if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) { + if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) switch (DECL_FUNCTION_CODE (fndecl)) { CASE_FLT_FN (BUILT_IN_SIN): @@ -2099,7 +2104,6 @@ expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget) default: gcc_unreachable (); } - } /* Before working hard, check whether the instruction is available. */ if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) @@ -2598,8 +2602,13 @@ expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n) static rtx expand_builtin_pow (tree exp, rtx target, rtx subtarget) { + tree arg0, arg1, fn, narg0, narglist; tree arglist = TREE_OPERAND (exp, 1); - tree arg0, arg1; + tree type = TREE_TYPE (exp); + REAL_VALUE_TYPE cint, c, c2; + HOST_WIDE_INT n; + rtx op, op2; + enum machine_mode mode = TYPE_MODE (type); if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return 0; @@ -2607,36 +2616,108 @@ expand_builtin_pow (tree exp, rtx target, rtx subtarget) arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - if (TREE_CODE (arg1) == REAL_CST - && ! TREE_CONSTANT_OVERFLOW (arg1)) + if (TREE_CODE (arg1) != REAL_CST + || TREE_CONSTANT_OVERFLOW (arg1)) + return expand_builtin_mathfn_2 (exp, target, subtarget); + + /* Handle constant exponents. */ + + /* For integer valued exponents we can expand to an optimal multiplication + sequence using expand_powi. */ + c = TREE_REAL_CST (arg1); + n = real_to_integer (&c); + real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + if (real_identical (&c, &cint) + && ((n >= -1 && n <= 2) + || (flag_unsafe_math_optimizations + && !optimize_size + && powi_cost (n) <= POWI_MAX_MULTS))) { - REAL_VALUE_TYPE cint; - REAL_VALUE_TYPE c; - HOST_WIDE_INT n; + op = expand_expr (arg0, subtarget, VOIDmode, 0); + if (n != 1) + { + op = force_reg (mode, op); + op = expand_powi (op, mode, n); + } + return op; + } - c = TREE_REAL_CST (arg1); - n = real_to_integer (&c); + narg0 = builtin_save_expr (arg0); + narglist = build_tree_list (NULL_TREE, narg0); + + /* If the exponent is not integer valued, check if it is half of an integer. + In this case we can expand to sqrt (x) * x**(n/2). */ + fn = mathfn_built_in (type, BUILT_IN_SQRT); + if (fn != NULL_TREE) + { + real_arithmetic (&c2, MULT_EXPR, &c, &dconst2); + n = real_to_integer (&c2); real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); - if (real_identical (&c, &cint)) - { - /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. - Otherwise, check the number of multiplications required. - Note that pow never sets errno for an integer exponent. */ - if ((n >= -1 && n <= 2) - || (flag_unsafe_math_optimizations - && ! optimize_size - && powi_cost (n) <= POWI_MAX_MULTS)) + if (real_identical (&c2, &cint) + && ((flag_unsafe_math_optimizations + && !optimize_size + && powi_cost (n/2) <= POWI_MAX_MULTS) + || n == 1)) + { + tree call_expr = build_function_call_expr (fn, narglist); + op = expand_builtin (call_expr, NULL_RTX, subtarget, mode, 0); + if (n != 1) { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); - rtx op = expand_expr (arg0, subtarget, VOIDmode, 0); - op = force_reg (mode, op); - return expand_powi (op, mode, n); + op2 = expand_expr (narg0, subtarget, VOIDmode, 0); + op2 = force_reg (mode, op2); + op2 = expand_powi (op2, mode, abs (n / 2)); + op = expand_simple_binop (mode, MULT, op, op2, NULL_RTX, + 0, OPTAB_LIB_WIDEN); + /* If the original exponent was negative, reciprocate the + result. */ + if (n < 0) + op = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), + op, NULL_RTX, 0, OPTAB_LIB_WIDEN); } + return op; } } - if (! flag_unsafe_math_optimizations) - return NULL_RTX; + /* Try if the exponent is a third of an integer. In this case + we can expand to x**(n/3) * cbrt(x)**(n%3). */ + fn = mathfn_built_in (type, BUILT_IN_CBRT); + if (fn != NULL_TREE) + { + real_arithmetic (&c2, MULT_EXPR, &c, &dconst3); + real_round (&c2, mode, &c2); + n = real_to_integer (&c2); + real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + real_arithmetic (&c2, RDIV_EXPR, &cint, &dconst3); + real_convert (&c2, mode, &c2); + if (real_identical (&c2, &c) + && ((!optimize_size + && flag_unsafe_math_optimizations + && powi_cost (n/3) <= POWI_MAX_MULTS) + || n == 1)) + { + tree call_expr = build_function_call_expr (fn, narglist); + op = expand_builtin (call_expr, NULL_RTX, subtarget, mode, 0); + if (abs (n) % 3 == 2) + op = expand_simple_binop (mode, MULT, op, op, op, + 0, OPTAB_LIB_WIDEN); + if (n != 1) + { + op2 = expand_expr (narg0, subtarget, VOIDmode, 0); + op2 = force_reg (mode, op2); + op2 = expand_powi (op2, mode, abs (n / 3)); + op = expand_simple_binop (mode, MULT, op, op2, NULL_RTX, + 0, OPTAB_LIB_WIDEN); + /* If the original exponent was negative, reciprocate the + result. */ + if (n < 0) + op = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), + op, NULL_RTX, 0, OPTAB_LIB_WIDEN); + } + return op; + } + } + + /* Fall back to optab expansion. */ return expand_builtin_mathfn_2 (exp, target, subtarget); } @@ -4626,6 +4707,30 @@ expand_builtin_alloca (tree arglist, rtx target) return result; } +/* Expand a call to a bswap builtin. The arguments are in ARGLIST. MODE + is the mode to expand with. */ + +static rtx +expand_builtin_bswap (tree arglist, rtx target, rtx subtarget) +{ + enum machine_mode mode; + tree arg; + rtx op0; + + if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) + return 0; + + arg = TREE_VALUE (arglist); + mode = TYPE_MODE (TREE_TYPE (arg)); + op0 = expand_expr (arg, subtarget, VOIDmode, 0); + + target = expand_unop (mode, bswap_optab, op0, target, 1); + + gcc_assert (target); + + return convert_to_mode (mode, target, 0); +} + /* Expand a call to a unary builtin. The arguments are in ARGLIST. Return 0 if a normal call should be emitted rather than expanding the function in-line. If convenient, the result should be placed in TARGET. @@ -4667,15 +4772,14 @@ expand_builtin_fputs (tree arglist, rtx target, bool unlocked) return 0; } -/* Expand a call to __builtin_expect. We return our argument and emit a - NOTE_INSN_EXPECTED_VALUE note. This is the expansion of __builtin_expect in - a non-jump context. */ +/* Expand a call to __builtin_expect. We just return our argument + as the builtin_expect semantic should've been already executed by + tree branch prediction pass. */ static rtx expand_builtin_expect (tree arglist, rtx target) { tree exp, c; - rtx note, rtx_c; if (arglist == NULL_TREE || TREE_CHAIN (arglist) == NULL_TREE) @@ -4683,149 +4787,12 @@ expand_builtin_expect (tree arglist, rtx target) exp = TREE_VALUE (arglist); c = TREE_VALUE (TREE_CHAIN (arglist)); - if (TREE_CODE (c) != INTEGER_CST) - { - error ("second argument to %<__builtin_expect%> must be a constant"); - c = integer_zero_node; - } - target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL); - - /* Don't bother with expected value notes for integral constants. */ - if (flag_guess_branch_prob && GET_CODE (target) != CONST_INT) - { - /* We do need to force this into a register so that we can be - moderately sure to be able to correctly interpret the branch - condition later. */ - target = force_reg (GET_MODE (target), target); - - rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL); - - note = emit_note (NOTE_INSN_EXPECTED_VALUE); - NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c); - } - + /* When guessing was done, the hints should be already stripped away. */ + gcc_assert (!flag_guess_branch_prob); return target; } -/* Like expand_builtin_expect, except do this in a jump context. This is - called from do_jump if the conditional is a __builtin_expect. Return either - a list of insns to emit the jump or NULL if we cannot optimize - __builtin_expect. We need to optimize this at jump time so that machines - like the PowerPC don't turn the test into a SCC operation, and then jump - based on the test being 0/1. */ - -rtx -expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label) -{ - tree arglist = TREE_OPERAND (exp, 1); - tree arg0 = TREE_VALUE (arglist); - tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - rtx ret = NULL_RTX; - - /* Only handle __builtin_expect (test, 0) and - __builtin_expect (test, 1). */ - if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE - && (integer_zerop (arg1) || integer_onep (arg1))) - { - rtx insn, drop_through_label, temp; - - /* Expand the jump insns. */ - start_sequence (); - do_jump (arg0, if_false_label, if_true_label); - ret = get_insns (); - - drop_through_label = get_last_insn (); - if (drop_through_label && NOTE_P (drop_through_label)) - drop_through_label = prev_nonnote_insn (drop_through_label); - if (drop_through_label && !LABEL_P (drop_through_label)) - drop_through_label = NULL_RTX; - end_sequence (); - - if (! if_true_label) - if_true_label = drop_through_label; - if (! if_false_label) - if_false_label = drop_through_label; - - /* Go through and add the expect's to each of the conditional jumps. */ - insn = ret; - while (insn != NULL_RTX) - { - rtx next = NEXT_INSN (insn); - - if (JUMP_P (insn) && any_condjump_p (insn)) - { - rtx ifelse = SET_SRC (pc_set (insn)); - rtx then_dest = XEXP (ifelse, 1); - rtx else_dest = XEXP (ifelse, 2); - int taken = -1; - - /* First check if we recognize any of the labels. */ - if (GET_CODE (then_dest) == LABEL_REF - && XEXP (then_dest, 0) == if_true_label) - taken = 1; - else if (GET_CODE (then_dest) == LABEL_REF - && XEXP (then_dest, 0) == if_false_label) - taken = 0; - else if (GET_CODE (else_dest) == LABEL_REF - && XEXP (else_dest, 0) == if_false_label) - taken = 1; - else if (GET_CODE (else_dest) == LABEL_REF - && XEXP (else_dest, 0) == if_true_label) - taken = 0; - /* Otherwise check where we drop through. */ - else if (else_dest == pc_rtx) - { - if (next && NOTE_P (next)) - next = next_nonnote_insn (next); - - if (next && JUMP_P (next) - && any_uncondjump_p (next)) - temp = XEXP (SET_SRC (pc_set (next)), 0); - else - temp = next; - - /* TEMP is either a CODE_LABEL, NULL_RTX or something - else that can't possibly match either target label. */ - if (temp == if_false_label) - taken = 1; - else if (temp == if_true_label) - taken = 0; - } - else if (then_dest == pc_rtx) - { - if (next && NOTE_P (next)) - next = next_nonnote_insn (next); - - if (next && JUMP_P (next) - && any_uncondjump_p (next)) - temp = XEXP (SET_SRC (pc_set (next)), 0); - else - temp = next; - - if (temp == if_false_label) - taken = 0; - else if (temp == if_true_label) - taken = 1; - } - - if (taken != -1) - { - /* If the test is expected to fail, reverse the - probabilities. */ - if (integer_zerop (arg1)) - taken = 1 - taken; - predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken); - } - } - - insn = next; - } - } - - return ret; -} - void expand_builtin_trap (void) { @@ -5904,6 +5871,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_stack_restore (TREE_VALUE (arglist)); return const0_rtx; + case BUILT_IN_BSWAP32: + case BUILT_IN_BSWAP64: + target = expand_builtin_bswap (arglist, target, subtarget); + + if (target) + return target; + break; + CASE_INT_FN (BUILT_IN_FFS): case BUILT_IN_FFSIMAX: target = expand_builtin_unop (target_mode, arglist, target, @@ -6780,15 +6755,7 @@ integer_valued_real_p (tree t) && integer_valued_real_p (TREE_OPERAND (t, 2)); case REAL_CST: - if (! TREE_CONSTANT_OVERFLOW (t)) - { - REAL_VALUE_TYPE c, cint; - - c = TREE_REAL_CST (t); - real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c); - return real_identical (&c, &cint); - } - break; + return real_isinteger (TREE_REAL_CST_PTR (t), TYPE_MODE (TREE_TYPE (t))); case NOP_EXPR: { @@ -6811,6 +6778,11 @@ integer_valued_real_p (tree t) CASE_FLT_FN (BUILT_IN_TRUNC): return true; + CASE_FLT_FN (BUILT_IN_FMIN): + CASE_FLT_FN (BUILT_IN_FMAX): + return integer_valued_real_p (TREE_VALUE (TREE_OPERAND (t, 1))) + && integer_valued_real_p (TREE_VALUE (TREE_CHAIN (TREE_OPERAND (t, 1)))); + default: break; } @@ -7208,7 +7180,7 @@ static tree fold_builtin_cos (tree arglist, tree type, tree fndecl) { tree arg = TREE_VALUE (arglist); - tree res; + tree res, narg; if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return NULL_TREE; @@ -7218,13 +7190,33 @@ fold_builtin_cos (tree arglist, tree type, tree fndecl) return res; /* Optimize cos(-x) into cos (x). */ - if (TREE_CODE (arg) == NEGATE_EXPR) + if ((narg = fold_strip_sign_ops (arg))) + return build_function_call_expr (fndecl, + build_tree_list (NULL_TREE, narg)); + + return NULL_TREE; +} + +/* Fold function call to builtin cosh, coshf, or coshl. Return + NULL_TREE if no simplification can be made. */ +static tree +fold_builtin_cosh (tree arglist, tree type, tree fndecl) +{ + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) { - tree args = build_tree_list (NULL_TREE, - TREE_OPERAND (arg, 0)); - return build_function_call_expr (fndecl, args); - } + tree arg = TREE_VALUE (arglist); + tree res, narg; + /* Calculate the result when the argument is a constant. */ + if ((res = do_mpfr_arg1 (arg, type, mpfr_cosh, NULL, NULL, 0))) + return res; + + /* Optimize cosh(-x) into cosh (x). */ + if ((narg = fold_strip_sign_ops (arg))) + return build_function_call_expr (fndecl, + build_tree_list (NULL_TREE, narg)); + } + return NULL_TREE; } @@ -7311,9 +7303,11 @@ fold_builtin_floor (tree fndecl, tree arglist) /* Fold floor (x) where x is nonnegative to trunc (x). */ if (tree_expr_nonnegative_p (arg)) - return build_function_call_expr (mathfn_built_in (TREE_TYPE (arg), - BUILT_IN_TRUNC), - arglist); + { + tree truncfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_TRUNC); + if (truncfn) + return build_function_call_expr (truncfn, arglist); + } return fold_trunc_transparent_mathfn (fndecl, arglist); } @@ -7542,6 +7536,67 @@ fold_builtin_bitop (tree fndecl, tree arglist) return NULL_TREE; } +/* Fold function call to builtin_bswap and the long and long long + variants. Return NULL_TREE if no simplification can be made. */ +static tree +fold_builtin_bswap (tree fndecl, tree arglist) +{ + tree arg; + + if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE)) + return 0; + + /* Optimize constant value. */ + arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg)) + { + HOST_WIDE_INT hi, width, r_hi = 0; + unsigned HOST_WIDE_INT lo, r_lo = 0; + tree type; + + type = TREE_TYPE (arg); + width = TYPE_PRECISION (type); + lo = TREE_INT_CST_LOW (arg); + hi = TREE_INT_CST_HIGH (arg); + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_BSWAP32: + case BUILT_IN_BSWAP64: + { + int s; + + for (s = 0; s < width; s += 8) + { + int d = width - s - 8; + unsigned HOST_WIDE_INT byte; + + if (s < HOST_BITS_PER_WIDE_INT) + byte = (lo >> s) & 0xff; + else + byte = (hi >> (s - HOST_BITS_PER_WIDE_INT)) & 0xff; + + if (d < HOST_BITS_PER_WIDE_INT) + r_lo |= byte << d; + else + r_hi |= byte << (d - HOST_BITS_PER_WIDE_INT); + } + } + + break; + + default: + gcc_unreachable (); + } + + if (width < HOST_BITS_PER_WIDE_INT) + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), r_lo); + else + return build_int_cst_wide (TREE_TYPE (TREE_TYPE (fndecl)), r_lo, r_hi); + } + + return NULL_TREE; +} /* Return true if EXPR is the real constant contained in VALUE. */ static bool @@ -7668,7 +7723,7 @@ fold_builtin_hypot (tree fndecl, tree arglist, tree type) { tree arg0 = TREE_VALUE (arglist); tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - tree res; + tree res, narg0, narg1; if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return NULL_TREE; @@ -7677,35 +7732,36 @@ fold_builtin_hypot (tree fndecl, tree arglist, tree type) if ((res = do_mpfr_arg2 (arg0, arg1, type, mpfr_hypot))) return res; + /* If either argument to hypot has a negate or abs, strip that off. + E.g. hypot(-x,fabs(y)) -> hypot(x,y). */ + narg0 = fold_strip_sign_ops (arg0); + narg1 = fold_strip_sign_ops (arg1); + if (narg0 || narg1) + { + tree narglist = tree_cons (NULL_TREE, narg0 ? narg0 : arg0, + build_tree_list (NULL_TREE, + narg1 ? narg1 : arg1)); + return build_function_call_expr (fndecl, narglist); + } + /* If either argument is zero, hypot is fabs of the other. */ if (real_zerop (arg0)) return fold_build1 (ABS_EXPR, type, arg1); else if (real_zerop (arg1)) return fold_build1 (ABS_EXPR, type, arg0); - /* hypot(x,x) -> x*sqrt(2). */ - if (operand_equal_p (arg0, arg1, OEP_PURE_SAME)) + /* hypot(x,x) -> fabs(x)*sqrt(2). */ + if (flag_unsafe_math_optimizations + && operand_equal_p (arg0, arg1, OEP_PURE_SAME)) { REAL_VALUE_TYPE sqrt2; real_sqrt (&sqrt2, TYPE_MODE (type), &dconst2); - return fold_build2 (MULT_EXPR, type, arg0, + return fold_build2 (MULT_EXPR, type, + fold_build1 (ABS_EXPR, type, arg0), build_real (type, sqrt2)); } - /* Transform hypot(-x,y) or hypot(x,-y) or hypot(-x,-y) into - hypot(x,y). */ - if (TREE_CODE (arg0) == NEGATE_EXPR || TREE_CODE (arg1) == NEGATE_EXPR) - { - tree narg0 = (TREE_CODE (arg0) == NEGATE_EXPR) - ? TREE_OPERAND (arg0, 0) : arg0; - tree narg1 = (TREE_CODE (arg1) == NEGATE_EXPR) - ? TREE_OPERAND (arg1, 0) : arg1; - tree narglist = tree_cons (NULL_TREE, narg0, - build_tree_list (NULL_TREE, narg1)); - return build_function_call_expr (fndecl, narglist); - } - return NULL_TREE; } @@ -8087,7 +8143,6 @@ static tree fold_builtin_memory_op (tree arglist, tree type, bool ignore, int endp) { tree dest, src, len, destvar, srcvar, expr; - unsigned HOST_WIDE_INT length; if (! validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) @@ -8107,12 +8162,12 @@ fold_builtin_memory_op (tree arglist, tree type, bool ignore, int endp) expr = len; else { + tree srctype, desttype; if (endp == 3) { - unsigned int src_align - = get_pointer_alignment (src, BIGGEST_ALIGNMENT); - unsigned int dest_align - = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + /* Both DEST and SRC must be pointer types. ??? This is what old code did. Is the testing for pointer types really mandatory? @@ -8120,64 +8175,72 @@ fold_builtin_memory_op (tree arglist, tree type, bool ignore, int endp) If either SRC is readonly or length is 1, we can use memcpy. */ if (dest_align && src_align && (readonly_data_expr (src) - || integer_onep (len))) + || (host_integerp (len, 1) + && (MIN (src_align, dest_align) / BITS_PER_UNIT <= + tree_low_cst (len, 1))))) { tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return 0; return build_function_call_expr (fn, arglist); } + return 0; } - if (! host_integerp (len, 1)) - return 0; - if (TREE_SIDE_EFFECTS (dest) || TREE_SIDE_EFFECTS (src)) + if (!host_integerp (len, 0)) return 0; - - destvar = dest; - STRIP_NOPS (destvar); - if (TREE_CODE (destvar) != ADDR_EXPR) + /* FIXME: + This logic lose for arguments like (type *)malloc (sizeof (type)), + since we strip the casts of up to VOID return value from malloc. + Perhaps we ought to inherit type from non-VOID argument here? */ + STRIP_NOPS (src); + STRIP_NOPS (dest); + srctype = TREE_TYPE (TREE_TYPE (src)); + desttype = TREE_TYPE (TREE_TYPE (dest)); + if (!srctype || !desttype + || !TYPE_SIZE_UNIT (srctype) + || !TYPE_SIZE_UNIT (desttype) + || TREE_CODE (TYPE_SIZE_UNIT (srctype)) != INTEGER_CST + || TREE_CODE (TYPE_SIZE_UNIT (desttype)) != INTEGER_CST + || !operand_equal_p (TYPE_SIZE_UNIT (srctype), len, 0) + || !operand_equal_p (TYPE_SIZE_UNIT (desttype), len, 0)) return 0; - destvar = TREE_OPERAND (destvar, 0); - if (TREE_THIS_VOLATILE (destvar)) + if (get_pointer_alignment (dest, BIGGEST_ALIGNMENT) + < (int) TYPE_ALIGN (desttype) + || (get_pointer_alignment (src, BIGGEST_ALIGNMENT) + < (int) TYPE_ALIGN (srctype))) return 0; - if (!INTEGRAL_TYPE_P (TREE_TYPE (destvar)) - && !POINTER_TYPE_P (TREE_TYPE (destvar)) - && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (destvar))) - return 0; + if (!ignore) + dest = builtin_save_expr (dest); - if (! var_decl_component_p (destvar)) - return 0; - - srcvar = src; - STRIP_NOPS (srcvar); - if (TREE_CODE (srcvar) != ADDR_EXPR) - return 0; - - srcvar = TREE_OPERAND (srcvar, 0); + srcvar = build_fold_indirect_ref (src); if (TREE_THIS_VOLATILE (srcvar)) return 0; - - if (!INTEGRAL_TYPE_P (TREE_TYPE (srcvar)) - && !POINTER_TYPE_P (TREE_TYPE (srcvar)) - && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (srcvar))) + /* With memcpy, it is possible to bypass aliasing rules, so without + this check i. e. execute/20060930-2.c would be misoptimized, because + it use conflicting alias set to hold argument for the memcpy call. + This check is probably unnecesary with -fno-strict-aliasing. + Similarly for destvar. See also PR29286. */ + if (!var_decl_component_p (srcvar) + /* Accept: memcpy (*char_var, "test", 1); that simplify + to char_var='t'; */ + || is_gimple_min_invariant (srcvar) + || readonly_data_expr (src)) return 0; - if (! var_decl_component_p (srcvar)) + destvar = build_fold_indirect_ref (dest); + if (TREE_THIS_VOLATILE (destvar)) return 0; - - length = tree_low_cst (len, 1); - if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (destvar))) != length - || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT - < (int) length - || GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (srcvar))) != length - || get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT - < (int) length) + if (!var_decl_component_p (destvar)) return 0; - if ((INTEGRAL_TYPE_P (TREE_TYPE (srcvar)) + if (srctype == desttype + || (in_ssa_p + && tree_ssa_useless_type_conversion_1 (desttype, srctype))) + expr = srcvar; + else if ((INTEGRAL_TYPE_P (TREE_TYPE (srcvar)) || POINTER_TYPE_P (TREE_TYPE (srcvar))) && (INTEGRAL_TYPE_P (TREE_TYPE (destvar)) || POINTER_TYPE_P (TREE_TYPE (destvar)))) @@ -8658,13 +8721,8 @@ fold_builtin_isascii (tree arglist) arg = build2 (BIT_AND_EXPR, integer_type_node, arg, build_int_cst (NULL_TREE, ~ (unsigned HOST_WIDE_INT) 0x7f)); - arg = fold_build2 (EQ_EXPR, integer_type_node, - arg, integer_zero_node); - - if (in_gimple_form && !TREE_CONSTANT (arg)) - return NULL_TREE; - else - return arg; + return fold_build2 (EQ_EXPR, integer_type_node, + arg, integer_zero_node); } } @@ -8707,12 +8765,8 @@ fold_builtin_isdigit (tree arglist) arg = fold_convert (unsigned_type_node, TREE_VALUE (arglist)); arg = build2 (MINUS_EXPR, unsigned_type_node, arg, build_int_cst (unsigned_type_node, target_digit0)); - arg = fold_build2 (LE_EXPR, integer_type_node, arg, - build_int_cst (unsigned_type_node, 9)); - if (in_gimple_form && !TREE_CONSTANT (arg)) - return NULL_TREE; - else - return arg; + return fold_build2 (LE_EXPR, integer_type_node, arg, + build_int_cst (unsigned_type_node, 9)); } } @@ -8750,6 +8804,52 @@ fold_builtin_abs (tree arglist, tree type) return fold_build1 (ABS_EXPR, type, arg); } +/* Fold a call to builtin fmin or fmax. */ + +static tree +fold_builtin_fmin_fmax (tree arglist, tree type, bool max) +{ + if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + { + tree arg0 = TREE_VALUE (arglist); + tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); + /* Calculate the result when the argument is a constant. */ + tree res = do_mpfr_arg2 (arg0, arg1, type, (max ? mpfr_max : mpfr_min)); + + if (res) + return res; + + /* If either argument is NaN, return the other one. Avoid the + transformation if we get (and honor) a signalling NaN. Using + omit_one_operand() ensures we create a non-lvalue. */ + if (TREE_CODE (arg0) == REAL_CST + && real_isnan (&TREE_REAL_CST (arg0)) + && (! HONOR_SNANS (TYPE_MODE (TREE_TYPE (arg0))) + || ! TREE_REAL_CST (arg0).signalling)) + return omit_one_operand (type, arg1, arg0); + if (TREE_CODE (arg1) == REAL_CST + && real_isnan (&TREE_REAL_CST (arg1)) + && (! HONOR_SNANS (TYPE_MODE (TREE_TYPE (arg1))) + || ! TREE_REAL_CST (arg1).signalling)) + return omit_one_operand (type, arg0, arg1); + + /* Transform fmin/fmax(x,x) -> x. */ + if (operand_equal_p (arg0, arg1, OEP_PURE_SAME)) + return omit_one_operand (type, arg0, arg1); + + /* Convert fmin/fmax to MIN_EXPR/MAX_EXPR. C99 requires these + functions to return the numeric arg if the other one is NaN. + These tree codes don't honor that, so only transform if + -ffinite-math-only is set. C99 doesn't require -0.0 to be + handled, so we don't have to worry about it either. */ + if (flag_finite_math_only) + return fold_build2 ((max ? MAX_EXPR : MIN_EXPR), type, + fold_convert (type, arg0), + fold_convert (type, arg1)); + } + return NULL_TREE; +} + /* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite. EXP is the CALL_EXPR for the call. */ @@ -9085,6 +9185,12 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) CASE_FLT_FN (BUILT_IN_TAN): return fold_builtin_tan (arglist, type); + CASE_FLT_FN (BUILT_IN_SINCOS): + if (validate_arglist (arglist, REAL_TYPE, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + return do_mpfr_sincos (TREE_VALUE (arglist), TREE_VALUE (TREE_CHAIN (arglist)), + TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))); + break; + CASE_FLT_FN (BUILT_IN_SINH): if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) return do_mpfr_arg1 (TREE_VALUE (arglist), type, mpfr_sinh, @@ -9092,10 +9198,7 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) break; CASE_FLT_FN (BUILT_IN_COSH): - if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) - return do_mpfr_arg1 (TREE_VALUE (arglist), type, mpfr_cosh, - NULL, NULL, 0); - break; + return fold_builtin_cosh (arglist, type, fndecl); CASE_FLT_FN (BUILT_IN_TANH): if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) @@ -9115,6 +9218,12 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) NULL, NULL, 0); break; + CASE_FLT_FN (BUILT_IN_TGAMMA): + if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE)) + return do_mpfr_arg1 (TREE_VALUE (arglist), type, mpfr_gamma, + NULL, NULL, 0); + break; + CASE_FLT_FN (BUILT_IN_EXP): return fold_builtin_exponent (fndecl, arglist, mpfr_exp); @@ -9153,6 +9262,20 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) type, mpfr_atan2); break; + CASE_FLT_FN (BUILT_IN_FMA): + if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return do_mpfr_arg3 (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))), + type, mpfr_fma); + break; + + CASE_FLT_FN (BUILT_IN_FMIN): + return fold_builtin_fmin_fmax (arglist, type, /*max=*/false); + + CASE_FLT_FN (BUILT_IN_FMAX): + return fold_builtin_fmin_fmax (arglist, type, /*max=*/true); + CASE_FLT_FN (BUILT_IN_HYPOT): return fold_builtin_hypot (fndecl, arglist, type); @@ -9208,6 +9331,10 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) CASE_FLT_FN (BUILT_IN_LLRINT): return fold_fixed_mathfn (fndecl, arglist); + case BUILT_IN_BSWAP32: + case BUILT_IN_BSWAP64: + return fold_builtin_bswap (fndecl, arglist); + CASE_INT_FN (BUILT_IN_FFS): CASE_INT_FN (BUILT_IN_CLZ): CASE_INT_FN (BUILT_IN_CTZ): @@ -11485,3 +11612,104 @@ do_mpfr_arg2 (tree arg1, tree arg2, tree type, return result; } + +/* If argument ARG is a REAL_CST, call the three-argument mpfr function + FUNC on it and return the resulting value as a tree with type TYPE. + The mpfr precision is set to the precision of TYPE. We assume that + function FUNC returns zero if the result could be calculated + exactly within the requested precision. */ + +static tree +do_mpfr_arg3 (tree arg1, tree arg2, tree arg3, tree type, + int (*func)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)) +{ + tree result = NULL_TREE; + + STRIP_NOPS (arg1); + STRIP_NOPS (arg2); + STRIP_NOPS (arg3); + + if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1) + && TREE_CODE (arg2) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg2) + && TREE_CODE (arg3) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg3)) + { + const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1); + const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2); + const REAL_VALUE_TYPE *const ra3 = &TREE_REAL_CST (arg3); + + if (!real_isnan (ra1) && !real_isinf (ra1) + && !real_isnan (ra2) && !real_isinf (ra2) + && !real_isnan (ra3) && !real_isinf (ra3)) + { + const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p; + int inexact; + mpfr_t m1, m2, m3; + + mpfr_inits2 (prec, m1, m2, m3, NULL); + mpfr_from_real (m1, ra1); + mpfr_from_real (m2, ra2); + mpfr_from_real (m3, ra3); + mpfr_clear_flags(); + inexact = func (m1, m1, m2, m3, GMP_RNDN); + result = do_mpfr_ckconv (m1, type, inexact); + mpfr_clears (m1, m2, m3, NULL); + } + } + + return result; +} + +/* If argument ARG is a REAL_CST, call mpfr_sin_cos() on it and set + the pointers *(ARG_SINP) and *(ARG_COSP) to the resulting values. + The type is taken from the type of ARG and is used for setting the + precision of the calculation and results. */ + +static tree +do_mpfr_sincos (tree arg, tree arg_sinp, tree arg_cosp) +{ + tree result = NULL_TREE; + + STRIP_NOPS (arg); + + if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg)) + { + const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg); + + if (!real_isnan (ra) && !real_isinf (ra)) + { + tree const type = TREE_TYPE (arg); + const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p; + tree result_s, result_c; + int inexact; + mpfr_t m, ms, mc; + + mpfr_inits2 (prec, m, ms, mc, NULL); + mpfr_from_real (m, ra); + mpfr_clear_flags(); + inexact = mpfr_sin_cos (ms, mc, m, GMP_RNDN); + result_s = do_mpfr_ckconv (ms, type, inexact); + result_c = do_mpfr_ckconv (mc, type, inexact); + mpfr_clears (m, ms, mc, NULL); + if (result_s && result_c) + { + /* Dereference the sin/cos pointer arguments. */ + arg_sinp = build_fold_indirect_ref (arg_sinp); + arg_cosp = build_fold_indirect_ref (arg_cosp); + /* Proceed if valid pointer type were passed in. */ + if (TYPE_MAIN_VARIANT (TREE_TYPE (arg_sinp)) == TYPE_MAIN_VARIANT (type) + && TYPE_MAIN_VARIANT (TREE_TYPE (arg_cosp)) == TYPE_MAIN_VARIANT (type)) + { + /* Set the values. */ + result_s = fold_build2 (MODIFY_EXPR, type, arg_sinp, result_s); + TREE_SIDE_EFFECTS (result_s) = 1; + result_c = fold_build2 (MODIFY_EXPR, type, arg_cosp, result_c); + TREE_SIDE_EFFECTS (result_c) = 1; + /* Combine the assignments into a compound expr. */ + result = non_lvalue (fold_build2 (COMPOUND_EXPR, type, + result_s, result_c)); + } + } + } + } + return result; +}