X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fbuiltins.c;h=e8fef179f881b749aac4d9725b76e967b820c207;hp=611b777ec9ce14e82d568d382512379bbbed44a0;hb=201ab45ce222afeba464e526be44421e0081fcfd;hpb=4bac51c921d817806b80efb4d906a77e83ffb8db diff --git a/gcc/builtins.c b/gcc/builtins.c index 611b777ec9c..e8fef179f88 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -1,7 +1,7 @@ /* Expand builtin functions. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - Free Software Foundation, Inc. + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, + 2012 Free Software Foundation, Inc. This file is part of GCC. @@ -40,7 +40,6 @@ along with GCC; see the file COPYING3. If not see #include "recog.h" #include "output.h" #include "typeclass.h" -#include "toplev.h" #include "predict.h" #include "tm_p.h" #include "target.h" @@ -52,9 +51,6 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "builtins.h" -#ifndef SLOW_UNALIGNED_ACCESS -#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT -#endif #ifndef PAD_VARARGS_DOWN #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN @@ -79,11 +75,7 @@ const char * built_in_names[(int) END_BUILTINS] = /* Setup an array of _DECL trees, make sure each element is initialized to NULL_TREE. */ -tree built_in_decls[(int) END_BUILTINS]; -/* Declarations used when constructing the builtin implicitly in the compiler. - It may be NULL_TREE when this is invalid (for instance runtime is not - required to implement the function call in all cases). */ -tree implicit_built_in_decls[(int) END_BUILTINS]; +builtin_info_type builtin_info; static const char *c_getstr (tree); static rtx c_readstr (const char *, enum machine_mode); @@ -195,6 +187,7 @@ static tree fold_builtin_strncat (location_t, tree, tree, tree); static tree fold_builtin_strspn (location_t, tree, tree); static tree fold_builtin_strcspn (location_t, tree, tree); static tree fold_builtin_sprintf (location_t, tree, tree, tree, int); +static tree fold_builtin_snprintf (location_t, tree, tree, tree, tree, int); static rtx expand_builtin_object_size (tree); static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, @@ -230,16 +223,19 @@ static tree do_mpfr_bessel_n (tree, tree, tree, const REAL_VALUE_TYPE *, bool); static tree do_mpfr_remquo (tree, tree, tree); static tree do_mpfr_lgamma_r (tree, tree, tree); +static void expand_builtin_sync_synchronize (void); /* Return true if NAME starts with __builtin_ or __sync_. */ -bool +static bool is_builtin_name (const char *name) { if (strncmp (name, "__builtin_", 10) == 0) return true; if (strncmp (name, "__sync_", 7) == 0) return true; + if (strncmp (name, "__atomic_", 9) == 0) + return true; return false; } @@ -267,11 +263,18 @@ called_as_built_in (tree node) return is_builtin_name (name); } -/* Return the alignment in bits of EXP, an object. - Don't return more than MAX_ALIGN no matter what. */ +/* Compute values M and N such that M divides (address of EXP - N) and + such that N < M. Store N in *BITPOSP and return M. + + Note that the address (and thus the alignment) computed here is based + on the address to which a symbol resolves, whereas DECL_ALIGN is based + on the address at which an object is actually located. These two + addresses are not always the same. For example, on ARM targets, + the address &foo of a Thumb function foo() has the lowest bit set, + whereas foo() itself starts on an even address. */ unsigned int -get_object_alignment (tree exp, unsigned int max_align) +get_object_alignment_1 (tree exp, unsigned HOST_WIDE_INT *bitposp) { HOST_WIDE_INT bitsize, bitpos; tree offset; @@ -290,7 +293,21 @@ get_object_alignment (tree exp, unsigned int max_align) exp = DECL_INITIAL (exp); if (DECL_P (exp) && TREE_CODE (exp) != LABEL_DECL) - align = DECL_ALIGN (exp); + { + if (TREE_CODE (exp) == FUNCTION_DECL) + { + /* Function addresses can encode extra information besides their + alignment. However, if TARGET_PTRMEMFUNC_VBIT_LOCATION + allows the low bit to be used as a virtual bit, we know + that the address itself must be 2-byte aligned. */ + if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn) + align = 2 * BITS_PER_UNIT; + else + align = BITS_PER_UNIT; + } + else + align = DECL_ALIGN (exp); + } else if (CONSTANT_CLASS_P (exp)) { align = TYPE_ALIGN (TREE_TYPE (exp)); @@ -323,8 +340,7 @@ get_object_alignment (tree exp, unsigned int max_align) align = MAX (pi->align * BITS_PER_UNIT, align); } else if (TREE_CODE (addr) == ADDR_EXPR) - align = MAX (align, get_object_alignment (TREE_OPERAND (addr, 0), - max_align)); + align = MAX (align, get_object_alignment (TREE_OPERAND (addr, 0))); bitpos += mem_ref_offset (exp).low * BITS_PER_UNIT; } else if (TREE_CODE (exp) == TARGET_MEM_REF) @@ -348,8 +364,7 @@ get_object_alignment (tree exp, unsigned int max_align) align = MAX (pi->align * BITS_PER_UNIT, align); } else if (TREE_CODE (addr) == ADDR_EXPR) - align = MAX (align, get_object_alignment (TREE_OPERAND (addr, 0), - max_align)); + align = MAX (align, get_object_alignment (TREE_OPERAND (addr, 0))); if (TMR_OFFSET (exp)) bitpos += TREE_INT_CST_LOW (TMR_OFFSET (exp)) * BITS_PER_UNIT; if (TMR_INDEX (exp) && TMR_STEP (exp)) @@ -367,7 +382,7 @@ get_object_alignment (tree exp, unsigned int max_align) /* If there is a non-constant offset part extract the maximum alignment that can prevail. */ - inner = max_align; + inner = ~0U; while (offset) { tree next_offset; @@ -414,57 +429,107 @@ get_object_alignment (tree exp, unsigned int max_align) align = MIN (align, inner); bitpos = bitpos & (align - 1); + *bitposp = bitpos; + return align; +} + +/* Return the alignment in bits of EXP, an object. */ + +unsigned int +get_object_alignment (tree exp) +{ + unsigned HOST_WIDE_INT bitpos = 0; + unsigned int align; + + align = get_object_alignment_1 (exp, &bitpos); + /* align and bitpos now specify known low bits of the pointer. ptr & (align - 1) == bitpos. */ if (bitpos != 0) align = (bitpos & -bitpos); - return MIN (align, max_align); + return align; } -/* Returns true iff we can trust that alignment information has been - calculated properly. */ +/* Return the alignment of object EXP, also considering its type when we do + not know of explicit misalignment. Only handle MEM_REF and TARGET_MEM_REF. -bool -can_trust_pointer_alignment (void) + ??? Note that, in the general case, the type of an expression is not kept + consistent with misalignment information by the front-end, for example when + taking the address of a member of a packed structure. However, in most of + the cases, expressions have the alignment of their type so we optimistically + fall back to this alignment when we cannot compute a misalignment. */ + +unsigned int +get_object_or_type_alignment (tree exp) { - /* We rely on TER to compute accurate alignment information. */ - return (optimize && flag_tree_ter); + unsigned HOST_WIDE_INT misalign; + unsigned int align = get_object_alignment_1 (exp, &misalign); + + gcc_assert (TREE_CODE (exp) == MEM_REF || TREE_CODE (exp) == TARGET_MEM_REF); + + if (misalign != 0) + align = (misalign & -misalign); + else + align = MAX (TYPE_ALIGN (TREE_TYPE (exp)), align); + + return align; } -/* Return the alignment in bits of EXP, a pointer valued expression. - But don't return more than MAX_ALIGN no matter what. - The alignment returned is, by default, the alignment of the thing that - EXP points to. If it is not a POINTER_TYPE, 0 is returned. +/* For a pointer valued expression EXP compute values M and N such that + M divides (EXP - N) and such that N < M. Store N in *BITPOSP and return M. - Otherwise, look at the expression to see if we can do better, i.e., if the - expression is actually pointing at an object whose alignment is tighter. */ + If EXP is not a pointer, 0 is returned. */ unsigned int -get_pointer_alignment (tree exp, unsigned int max_align) +get_pointer_alignment_1 (tree exp, unsigned HOST_WIDE_INT *bitposp) { STRIP_NOPS (exp); if (TREE_CODE (exp) == ADDR_EXPR) - return get_object_alignment (TREE_OPERAND (exp, 0), max_align); + return get_object_alignment_1 (TREE_OPERAND (exp, 0), bitposp); else if (TREE_CODE (exp) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (exp))) { struct ptr_info_def *pi = SSA_NAME_PTR_INFO (exp); - unsigned align; if (!pi) - return BITS_PER_UNIT; - if (pi->misalign != 0) - align = (pi->misalign & -pi->misalign); - else - align = pi->align; - return MIN (max_align, align * BITS_PER_UNIT); + { + *bitposp = 0; + return BITS_PER_UNIT; + } + *bitposp = pi->misalign * BITS_PER_UNIT; + return pi->align * BITS_PER_UNIT; } + *bitposp = 0; return POINTER_TYPE_P (TREE_TYPE (exp)) ? BITS_PER_UNIT : 0; } +/* Return the alignment in bits of EXP, a pointer valued expression. + The alignment returned is, by default, the alignment of the thing that + EXP points to. If it is not a POINTER_TYPE, 0 is returned. + + Otherwise, look at the expression to see if we can do better, i.e., if the + expression is actually pointing at an object whose alignment is tighter. */ + +unsigned int +get_pointer_alignment (tree exp) +{ + unsigned HOST_WIDE_INT bitpos = 0; + unsigned int align; + + align = get_pointer_alignment_1 (exp, &bitpos); + + /* align and bitpos now specify known low bits of the pointer. + ptr & (align - 1) == bitpos. */ + + if (bitpos != 0) + align = (bitpos & -bitpos); + + return align; +} + /* Compute the length of a C string. TREE_STRING_LENGTH is not the right way, because it could contain a zero byte in the middle. TREE_STRING_LENGTH is the size of the character array, not the string. @@ -506,10 +571,7 @@ c_strlen (tree src, int only_value) && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) return c_strlen (TREE_OPERAND (src, 1), only_value); - if (EXPR_HAS_LOCATION (src)) - loc = EXPR_LOCATION (src); - else - loc = input_location; + loc = EXPR_LOC_OR_HERE (src); src = string_constant (src, &offset_node); if (src == 0) @@ -612,7 +674,7 @@ c_readstr (const char *str, enum machine_mode mode) if (WORDS_BIG_ENDIAN) j = GET_MODE_SIZE (mode) - i - 1; if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN - && GET_MODE_SIZE (mode) > UNITS_PER_WORD) + && GET_MODE_SIZE (mode) >= UNITS_PER_WORD) j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1; j *= BITS_PER_UNIT; gcc_assert (j < 2 * HOST_BITS_PER_WIDE_INT); @@ -633,11 +695,11 @@ target_char_cast (tree cst, char *p) { unsigned HOST_WIDE_INT val, hostval; - if (!host_integerp (cst, 1) + if (TREE_CODE (cst) != INTEGER_CST || CHAR_TYPE_SIZE > HOST_BITS_PER_WIDE_INT) return 1; - val = tree_low_cst (cst, 1); + val = TREE_INT_CST_LOW (cst); if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT) val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1; @@ -659,9 +721,10 @@ target_char_cast (tree cst, char *p) static tree builtin_save_expr (tree exp) { - if (TREE_ADDRESSABLE (exp) == 0 - && (TREE_CODE (exp) == PARM_DECL - || (TREE_CODE (exp) == VAR_DECL && !TREE_STATIC (exp)))) + if (TREE_CODE (exp) == SSA_NAME + || (TREE_ADDRESSABLE (exp) == 0 + && (TREE_CODE (exp) == PARM_DECL + || (TREE_CODE (exp) == VAR_DECL && !TREE_STATIC (exp))))) return exp; return save_expr (exp); @@ -790,7 +853,7 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode))); set_mem_alias_set (stack_save, setjmp_alias_set); - emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX); + emit_stack_save (SAVE_NONLOCAL, &stack_save); /* If there is further processing to do, do it. */ #ifdef HAVE_builtin_setjmp_setup @@ -798,10 +861,6 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) emit_insn (gen_builtin_setjmp_setup (buf_addr)); #endif - /* Tell optimize_save_area_alloca that extra work is going to - need to go on during alloca. */ - cfun->calls_setjmp = 1; - /* We have a nonlocal label. */ cfun->has_nonlocal_label = 1; } @@ -938,7 +997,7 @@ expand_builtin_longjmp (rtx buf_addr, rtx value) emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx)); emit_move_insn (hard_frame_pointer_rtx, fp); - emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX); + emit_stack_restore (SAVE_NONLOCAL, stack); emit_use (hard_frame_pointer_rtx); emit_use (stack_pointer_rtx); @@ -984,8 +1043,8 @@ expand_builtin_nonlocal_goto (tree exp) r_label = convert_memory_address (Pmode, r_label); r_save_area = expand_normal (t_save_area); r_save_area = convert_memory_address (Pmode, r_save_area); - /* Copy the address of the save location to a register just in case it was based - on the frame pointer. */ + /* Copy the address of the save location to a register just in case it was + based on the frame pointer. */ r_save_area = copy_to_reg (r_save_area); r_fp = gen_rtx_MEM (Pmode, r_save_area); r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL), @@ -1005,13 +1064,9 @@ expand_builtin_nonlocal_goto (tree exp) emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))); emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx)); - /* Restore frame pointer for containing function. - This sets the actual hard register used for the frame pointer - to the location of the function's incoming static chain info. - The non-local goto handler will then adjust it to contain the - proper value and reload the argument pointer, if needed. */ + /* Restore frame pointer for containing function. */ emit_move_insn (hard_frame_pointer_rtx, r_fp); - emit_stack_restore (SAVE_NONLOCAL, r_sp, NULL_RTX); + emit_stack_restore (SAVE_NONLOCAL, r_sp); /* USE of hard_frame_pointer_rtx added for consistency; not clear if really needed. */ @@ -1058,30 +1113,14 @@ expand_builtin_nonlocal_goto (tree exp) static void expand_builtin_update_setjmp_buf (rtx buf_addr) { - enum machine_mode sa_mode = Pmode; - rtx stack_save; - - -#ifdef HAVE_save_stack_nonlocal - if (HAVE_save_stack_nonlocal) - sa_mode = insn_data[(int) CODE_FOR_save_stack_nonlocal].operand[0].mode; -#endif -#ifdef STACK_SAVEAREA_MODE - sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); -#endif - - stack_save + enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); + rtx stack_save = gen_rtx_MEM (sa_mode, memory_address (sa_mode, plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode)))); -#ifdef HAVE_setjmp - if (HAVE_setjmp) - emit_insn (gen_setjmp ()); -#endif - - emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX); + emit_stack_save (SAVE_NONLOCAL, &stack_save); } /* Expand a call to __builtin_prefetch. For a target that does not support @@ -1148,15 +1187,13 @@ expand_builtin_prefetch (tree exp) #ifdef HAVE_prefetch if (HAVE_prefetch) { - if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate) - (op0, - insn_data[(int) CODE_FOR_prefetch].operand[0].mode)) - || (GET_MODE (op0) != Pmode)) - { - op0 = convert_memory_address (Pmode, op0); - op0 = force_reg (Pmode, op0); - } - emit_insn (gen_prefetch (op0, op1, op2)); + struct expand_operand ops[3]; + + create_address_operand (&ops[0], op0); + create_integer_operand (&ops[1], INTVAL (op1)); + create_integer_operand (&ops[2], INTVAL (op2)); + if (maybe_expand_insn (CODE_FOR_prefetch, 3, ops)) + return; } #endif @@ -1235,9 +1272,8 @@ get_memory_rtx (tree exp, tree len) gcc_assert (TREE_CODE (inner) == COMPONENT_REF); - if (MEM_OFFSET (mem) - && CONST_INT_P (MEM_OFFSET (mem))) - offset = INTVAL (MEM_OFFSET (mem)); + if (MEM_OFFSET_KNOWN_P (mem)) + offset = MEM_OFFSET (mem); if (offset >= 0 && len && host_integerp (len, 0)) length = tree_low_cst (len, 0); @@ -1292,11 +1328,14 @@ get_memory_rtx (tree exp, tree len) if (mem_expr != MEM_EXPR (mem)) { set_mem_expr (mem, mem_expr); - set_mem_offset (mem, offset >= 0 ? GEN_INT (offset) : NULL_RTX); + if (offset >= 0) + set_mem_offset (mem, offset); + else + clear_mem_offset (mem); } } set_mem_alias_set (mem, 0); - set_mem_size (mem, NULL_RTX); + clear_mem_size (mem); } return mem; @@ -1564,10 +1603,10 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize) /* Save the stack with nonlocal if available. */ #ifdef HAVE_save_stack_nonlocal if (HAVE_save_stack_nonlocal) - emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX); + emit_stack_save (SAVE_NONLOCAL, &old_stack_level); else #endif - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + emit_stack_save (SAVE_BLOCK, &old_stack_level); /* Allocate a block of memory onto the stack and copy the memory arguments to the outgoing arguments address. We can pass TRUE @@ -1683,10 +1722,11 @@ expand_builtin_apply (rtx function, rtx arguments, rtx argsize) /* Restore the stack. */ #ifdef HAVE_save_stack_nonlocal if (HAVE_save_stack_nonlocal) - emit_stack_restore (SAVE_NONLOCAL, old_stack_level, NULL_RTX); + emit_stack_restore (SAVE_NONLOCAL, old_stack_level); else #endif - emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); + emit_stack_restore (SAVE_BLOCK, old_stack_level); + fixup_args_size_notes (call_insn, get_last_insn(), 0); OK_DEFER_POP; @@ -1797,17 +1837,15 @@ expand_builtin_classify_type (tree exp) fcode = BUILT_IN_MATHFN##_R; fcodef = BUILT_IN_MATHFN##F_R ; \ fcodel = BUILT_IN_MATHFN##L_R ; break; -/* Return mathematic function equivalent to FN but operating directly - on TYPE, if available. If IMPLICIT is true find the function in - implicit_built_in_decls[], otherwise use built_in_decls[]. If we - can't do the conversion, return zero. */ +/* Return mathematic function equivalent to FN but operating directly on TYPE, + if available. If IMPLICIT is true use the implicit builtin declaration, + otherwise use the explicit declaration. If we can't do the conversion, + return zero. */ static tree -mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit) +mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p) { - tree const *const fn_arr - = implicit ? implicit_built_in_decls : built_in_decls; - enum built_in_function fcode, fcodef, fcodel; + enum built_in_function fcode, fcodef, fcodel, fcode2; switch (fn) { @@ -1844,7 +1882,11 @@ mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit) CASE_MATHFN (BUILT_IN_HUGE_VAL) CASE_MATHFN (BUILT_IN_HYPOT) CASE_MATHFN (BUILT_IN_ILOGB) + CASE_MATHFN (BUILT_IN_ICEIL) + CASE_MATHFN (BUILT_IN_IFLOOR) CASE_MATHFN (BUILT_IN_INF) + CASE_MATHFN (BUILT_IN_IRINT) + CASE_MATHFN (BUILT_IN_IROUND) CASE_MATHFN (BUILT_IN_ISINF) CASE_MATHFN (BUILT_IN_J0) CASE_MATHFN (BUILT_IN_J1) @@ -1900,13 +1942,18 @@ mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit) } if (TYPE_MAIN_VARIANT (type) == double_type_node) - return fn_arr[fcode]; + fcode2 = fcode; else if (TYPE_MAIN_VARIANT (type) == float_type_node) - return fn_arr[fcodef]; + fcode2 = fcodef; else if (TYPE_MAIN_VARIANT (type) == long_double_type_node) - return fn_arr[fcodel]; + fcode2 = fcodel; else return NULL_TREE; + + if (implicit_p && !builtin_decl_implicit_p (fcode2)) + return NULL_TREE; + + return builtin_decl_explicit (fcode2); } /* Like mathfn_built_in_1(), but always use the implicit array. */ @@ -2042,7 +2089,8 @@ expand_builtin_mathfn (tree exp, rtx target, rtx subtarget) errno_set = false; /* Before working hard, check whether the instruction is available. */ - if (optab_handler (builtin_optab, mode) != CODE_FOR_nothing) + if (optab_handler (builtin_optab, mode) != CODE_FOR_nothing + && (!errno_set || !optimize_insn_for_size_p ())) { target = gen_reg_rtx (mode); @@ -2152,6 +2200,9 @@ expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget) if (! flag_errno_math || ! HONOR_NANS (mode)) errno_set = false; + if (errno_set && optimize_insn_for_size_p ()) + return 0; + /* Always stabilize the argument list. */ CALL_EXPR_ARG (exp, 0) = arg0 = builtin_save_expr (arg0); CALL_EXPR_ARG (exp, 1) = arg1 = builtin_save_expr (arg1); @@ -2432,16 +2483,9 @@ expand_builtin_interclass_mathfn (tree exp, rtx target) if (icode != CODE_FOR_nothing) { + struct expand_operand ops[1]; rtx last = get_last_insn (); tree orig_arg = arg; - /* Make a suitable register to place result in. */ - if (!target - || GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)) - || !insn_data[icode].operand[0].predicate (target, GET_MODE (target))) - target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); - - gcc_assert (insn_data[icode].operand[0].predicate - (target, GET_MODE (target))); /* Wrap the computation of the argument in a SAVE_EXPR, as we may need to expand the argument again. This way, we will not perform @@ -2453,10 +2497,11 @@ expand_builtin_interclass_mathfn (tree exp, rtx target) if (mode != GET_MODE (op0)) op0 = convert_to_mode (mode, op0, 0); - /* Compute into TARGET. - Set TARGET to wherever the result comes back. */ - if (maybe_emit_unop_insn (icode, target, op0, UNKNOWN)) - return target; + create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (exp))); + if (maybe_legitimize_operands (icode, 0, 1, ops) + && maybe_emit_unop_insn (icode, ops[0].value, op0, UNKNOWN)) + return ops[0].value; + delete_insns_since (last); CALL_EXPR_ARG (exp, 0) = orig_arg; } @@ -2477,6 +2522,7 @@ expand_builtin_sincos (tree exp) tree arg, sinp, cosp; int result; location_t loc = EXPR_LOCATION (exp); + tree alias_type, alias_off; if (!validate_arglist (exp, REAL_TYPE, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) @@ -2497,8 +2543,12 @@ expand_builtin_sincos (tree exp) target2 = gen_reg_rtx (mode); op0 = expand_normal (arg); - op1 = expand_normal (build_fold_indirect_ref_loc (loc, sinp)); - op2 = expand_normal (build_fold_indirect_ref_loc (loc, cosp)); + alias_type = build_pointer_type_for_mode (TREE_TYPE (arg), ptr_mode, true); + alias_off = build_int_cst (alias_type, 0); + op1 = expand_normal (fold_build2_loc (loc, MEM_REF, TREE_TYPE (arg), + sinp, alias_off)); + op2 = expand_normal (fold_build2_loc (loc, MEM_REF, TREE_TYPE (arg), + cosp, alias_off)); /* Compute into target1 and target2. Set TARGET to wherever the result comes back. */ @@ -2553,11 +2603,11 @@ expand_builtin_cexpi (tree exp, rtx target) rtx op1a, op2a; if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIF) - fn = built_in_decls[BUILT_IN_SINCOSF]; + fn = builtin_decl_explicit (BUILT_IN_SINCOSF); else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPI) - fn = built_in_decls[BUILT_IN_SINCOS]; + fn = builtin_decl_explicit (BUILT_IN_SINCOS); else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIL) - fn = built_in_decls[BUILT_IN_SINCOSL]; + fn = builtin_decl_explicit (BUILT_IN_SINCOSL); else gcc_unreachable (); @@ -2579,11 +2629,11 @@ expand_builtin_cexpi (tree exp, rtx target) tree ctype = build_complex_type (type); if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIF) - fn = built_in_decls[BUILT_IN_CEXPF]; + fn = builtin_decl_explicit (BUILT_IN_CEXPF); else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPI) - fn = built_in_decls[BUILT_IN_CEXP]; + fn = builtin_decl_explicit (BUILT_IN_CEXP); else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CEXPIL) - fn = built_in_decls[BUILT_IN_CEXPL]; + fn = builtin_decl_explicit (BUILT_IN_CEXPL); else gcc_unreachable (); @@ -2666,12 +2716,14 @@ expand_builtin_int_roundingfn (tree exp, rtx target) switch (DECL_FUNCTION_CODE (fndecl)) { + CASE_FLT_FN (BUILT_IN_ICEIL): CASE_FLT_FN (BUILT_IN_LCEIL): CASE_FLT_FN (BUILT_IN_LLCEIL): builtin_optab = lceil_optab; fallback_fn = BUILT_IN_CEIL; break; + CASE_FLT_FN (BUILT_IN_IFLOOR): CASE_FLT_FN (BUILT_IN_LFLOOR): CASE_FLT_FN (BUILT_IN_LLFLOOR): builtin_optab = lfloor_optab; @@ -2724,26 +2776,32 @@ expand_builtin_int_roundingfn (tree exp, rtx target) switch (DECL_FUNCTION_CODE (fndecl)) { + case BUILT_IN_ICEIL: case BUILT_IN_LCEIL: case BUILT_IN_LLCEIL: name = "ceil"; break; + case BUILT_IN_ICEILF: case BUILT_IN_LCEILF: case BUILT_IN_LLCEILF: name = "ceilf"; break; + case BUILT_IN_ICEILL: case BUILT_IN_LCEILL: case BUILT_IN_LLCEILL: name = "ceill"; break; + case BUILT_IN_IFLOOR: case BUILT_IN_LFLOOR: case BUILT_IN_LLFLOOR: name = "floor"; break; + case BUILT_IN_IFLOORF: case BUILT_IN_LFLOORF: case BUILT_IN_LLFLOORF: name = "floorf"; break; + case BUILT_IN_IFLOORL: case BUILT_IN_LFLOORL: case BUILT_IN_LLFLOORL: name = "floorl"; @@ -2783,10 +2841,7 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target) tree fndecl = get_callee_fndecl (exp); tree arg; enum machine_mode mode; - - /* There's no easy way to detect the case we need to set EDOM. */ - if (flag_errno_math) - return NULL_RTX; + enum built_in_function fallback_fn = BUILT_IN_NONE; if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) gcc_unreachable (); @@ -2795,644 +2850,211 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target) switch (DECL_FUNCTION_CODE (fndecl)) { + CASE_FLT_FN (BUILT_IN_IRINT): + fallback_fn = BUILT_IN_LRINT; + /* FALLTHRU */ CASE_FLT_FN (BUILT_IN_LRINT): CASE_FLT_FN (BUILT_IN_LLRINT): - builtin_optab = lrint_optab; break; + builtin_optab = lrint_optab; + break; + + CASE_FLT_FN (BUILT_IN_IROUND): + fallback_fn = BUILT_IN_LROUND; + /* FALLTHRU */ CASE_FLT_FN (BUILT_IN_LROUND): CASE_FLT_FN (BUILT_IN_LLROUND): - builtin_optab = lround_optab; break; + builtin_optab = lround_optab; + break; + default: gcc_unreachable (); } + /* There's no easy way to detect the case we need to set EDOM. */ + if (flag_errno_math && fallback_fn == BUILT_IN_NONE) + return NULL_RTX; + /* Make a suitable register to place result in. */ mode = TYPE_MODE (TREE_TYPE (exp)); - target = gen_reg_rtx (mode); + /* There's no easy way to detect the case we need to set EDOM. */ + if (!flag_errno_math) + { + target = gen_reg_rtx (mode); - /* Wrap the computation of the argument in a SAVE_EXPR, as we may - need to expand the argument again. This way, we will not perform - side-effects more the once. */ - CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg); + /* Wrap the computation of the argument in a SAVE_EXPR, as we may + need to expand the argument again. This way, we will not perform + side-effects more the once. */ + CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg); - op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL); + op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL); - start_sequence (); + start_sequence (); - if (expand_sfix_optab (target, op0, builtin_optab)) - { - /* Output the entire sequence. */ - insns = get_insns (); + if (expand_sfix_optab (target, op0, builtin_optab)) + { + /* Output the entire sequence. */ + insns = get_insns (); + end_sequence (); + emit_insn (insns); + return target; + } + + /* If we were unable to expand via the builtin, stop the sequence + (without outputting the insns) and call to the library function + with the stabilized argument list. */ end_sequence (); - emit_insn (insns); - return target; } - /* If we were unable to expand via the builtin, stop the sequence - (without outputting the insns) and call to the library function - with the stabilized argument list. */ - end_sequence (); + if (fallback_fn != BUILT_IN_NONE) + { + /* Fall back to rounding to long int. Use implicit_p 0 - for non-C99 + targets, (int) round (x) should never be transformed into + BUILT_IN_IROUND and if __builtin_iround is called directly, emit + a call to lround in the hope that the target provides at least some + C99 functions. This should result in the best user experience for + not full C99 targets. */ + tree fallback_fndecl = mathfn_built_in_1 (TREE_TYPE (arg), + fallback_fn, 0); + + exp = build_call_nofold_loc (EXPR_LOCATION (exp), + fallback_fndecl, 1, arg); + + target = expand_call (exp, NULL_RTX, target == const0_rtx); + return convert_to_mode (mode, target, 0); + } target = expand_call (exp, target, target == const0_rtx); return target; } -/* To evaluate powi(x,n), the floating point value x raised to the - constant integer exponent n, we use a hybrid algorithm that - combines the "window method" with look-up tables. For an - introduction to exponentiation algorithms and "addition chains", - see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth, - "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming", - 3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation - Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998. */ - -/* Provide a default value for POWI_MAX_MULTS, the maximum number of - multiplications to inline before calling the system library's pow - function. powi(x,n) requires at worst 2*bits(n)-2 multiplications, - so this default never requires calling pow, powf or powl. */ - -#ifndef POWI_MAX_MULTS -#define POWI_MAX_MULTS (2*HOST_BITS_PER_WIDE_INT-2) -#endif - -/* The size of the "optimal power tree" lookup table. All - exponents less than this value are simply looked up in the - powi_table below. This threshold is also used to size the - cache of pseudo registers that hold intermediate results. */ -#define POWI_TABLE_SIZE 256 - -/* The size, in bits of the window, used in the "window method" - exponentiation algorithm. This is equivalent to a radix of - (1<= POWI_TABLE_SIZE) - { - if (val & 1) - { - digit = val & ((1 << POWI_WINDOW_SIZE) - 1); - result += powi_lookup_cost (digit, cache) - + POWI_WINDOW_SIZE + 1; - val >>= POWI_WINDOW_SIZE; - } - else - { - val >>= 1; - result++; - } - } + target = emit_library_call_value (optab_libfunc (powi_optab, mode), + target, LCT_CONST, mode, 2, + op0, mode, op1, mode2); - return result + powi_lookup_cost (val, cache); + return target; } -/* Recursive subroutine of expand_powi. This function takes the array, - CACHE, of already calculated exponents and an exponent N and returns - an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE. */ +/* Expand expression EXP which is a call to the strlen builtin. Return + NULL_RTX if we failed the caller should emit a normal call, otherwise + try to get the result in TARGET, if convenient. */ static rtx -expand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache) +expand_builtin_strlen (tree exp, rtx target, + enum machine_mode target_mode) { - unsigned HOST_WIDE_INT digit; - rtx target, result; - rtx op0, op1; - - if (n < POWI_TABLE_SIZE) - { - if (cache[n]) - return cache[n]; - - target = gen_reg_rtx (mode); - cache[n] = target; - - op0 = expand_powi_1 (mode, n - powi_table[n], cache); - op1 = expand_powi_1 (mode, powi_table[n], cache); - } - else if (n & 1) - { - target = gen_reg_rtx (mode); - digit = n & ((1 << POWI_WINDOW_SIZE) - 1); - op0 = expand_powi_1 (mode, n - digit, cache); - op1 = expand_powi_1 (mode, digit, cache); - } + if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; else { - target = gen_reg_rtx (mode); - op0 = expand_powi_1 (mode, n >> 1, cache); - op1 = op0; - } - - result = expand_mult (mode, op0, op1, target, 0); - if (result != target) - emit_move_insn (target, result); - return target; -} + struct expand_operand ops[4]; + rtx pat; + tree len; + tree src = CALL_EXPR_ARG (exp, 0); + rtx src_reg, before_strlen; + enum machine_mode insn_mode = target_mode; + enum insn_code icode = CODE_FOR_nothing; + unsigned int align; -/* Expand the RTL to evaluate powi(x,n) in mode MODE. X is the - floating point operand in mode MODE, and N is the exponent. This - function needs to be kept in sync with powi_cost above. */ + /* If the length can be computed at compile-time, return it. */ + len = c_strlen (src, 0); + if (len) + return expand_expr (len, target, target_mode, EXPAND_NORMAL); -static rtx -expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n) -{ - rtx cache[POWI_TABLE_SIZE]; - rtx result; + /* If the length can be computed at compile-time and is constant + integer, but there are side-effects in src, evaluate + src for side-effects, then return len. + E.g. x = strlen (i++ ? "xfoo" + 1 : "bar"); + can be optimized into: i++; x = 3; */ + len = c_strlen (src, 1); + if (len && TREE_CODE (len) == INTEGER_CST) + { + expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); + return expand_expr (len, target, target_mode, EXPAND_NORMAL); + } - if (n == 0) - return CONST1_RTX (mode); + align = get_pointer_alignment (src) / BITS_PER_UNIT; - memset (cache, 0, sizeof (cache)); - cache[1] = x; + /* If SRC is not a pointer type, don't do this operation inline. */ + if (align == 0) + return NULL_RTX; - result = expand_powi_1 (mode, (n < 0) ? -n : n, cache); + /* Bail out if we can't compute strlen in the right mode. */ + while (insn_mode != VOIDmode) + { + icode = optab_handler (strlen_optab, insn_mode); + if (icode != CODE_FOR_nothing) + break; - /* If the original exponent was negative, reciprocate the result. */ - if (n < 0) - result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), - result, NULL_RTX, 0, OPTAB_LIB_WIDEN); + insn_mode = GET_MODE_WIDER_MODE (insn_mode); + } + if (insn_mode == VOIDmode) + return NULL_RTX; - return result; -} + /* Make a place to hold the source address. We will not expand + the actual source until we are sure that the expansion will + not fail -- there are trees that cannot be expanded twice. */ + src_reg = gen_reg_rtx (Pmode); -/* Fold a builtin function call to pow, powf, or powl into a series of sqrts or - cbrts. Return NULL_RTX if no simplification can be made or expand the tree - if we can simplify it. */ -static rtx -expand_builtin_pow_root (location_t loc, tree arg0, tree arg1, tree type, - rtx subtarget) -{ - if (TREE_CODE (arg1) == REAL_CST - && !TREE_OVERFLOW (arg1) - && flag_unsafe_math_optimizations) - { - enum machine_mode mode = TYPE_MODE (type); - tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT); - tree cbrtfn = mathfn_built_in (type, BUILT_IN_CBRT); - REAL_VALUE_TYPE c = TREE_REAL_CST (arg1); - tree op = NULL_TREE; + /* Mark the beginning of the strlen sequence so we can emit the + source operand later. */ + before_strlen = get_last_insn (); - if (sqrtfn) - { - /* Optimize pow (x, 0.5) into sqrt. */ - if (REAL_VALUES_EQUAL (c, dconsthalf)) - op = build_call_nofold_loc (loc, sqrtfn, 1, arg0); - - else - { - REAL_VALUE_TYPE dconst1_4 = dconst1; - REAL_VALUE_TYPE dconst3_4; - SET_REAL_EXP (&dconst1_4, REAL_EXP (&dconst1_4) - 2); - - real_from_integer (&dconst3_4, VOIDmode, 3, 0, 0); - SET_REAL_EXP (&dconst3_4, REAL_EXP (&dconst3_4) - 2); - - /* Optimize pow (x, 0.25) into sqrt (sqrt (x)). Assume on most - machines that a builtin sqrt instruction is smaller than a - call to pow with 0.25, so do this optimization even if - -Os. */ - if (REAL_VALUES_EQUAL (c, dconst1_4)) - { - op = build_call_nofold_loc (loc, sqrtfn, 1, arg0); - op = build_call_nofold_loc (loc, sqrtfn, 1, op); - } - - /* Optimize pow (x, 0.75) = sqrt (x) * sqrt (sqrt (x)) unless we - are optimizing for space. */ - else if (optimize_insn_for_speed_p () - && !TREE_SIDE_EFFECTS (arg0) - && REAL_VALUES_EQUAL (c, dconst3_4)) - { - tree sqrt1 = build_call_expr_loc (loc, sqrtfn, 1, arg0); - tree sqrt2 = builtin_save_expr (sqrt1); - tree sqrt3 = build_call_expr_loc (loc, sqrtfn, 1, sqrt1); - op = fold_build2_loc (loc, MULT_EXPR, type, sqrt2, sqrt3); - } - } - } - - /* Check whether we can do cbrt insstead of pow (x, 1./3.) and - cbrt/sqrts instead of pow (x, 1./6.). */ - if (cbrtfn && ! op - && (tree_expr_nonnegative_p (arg0) || !HONOR_NANS (mode))) - { - /* First try 1/3. */ - REAL_VALUE_TYPE dconst1_3 - = real_value_truncate (mode, dconst_third ()); - - if (REAL_VALUES_EQUAL (c, dconst1_3)) - op = build_call_nofold_loc (loc, cbrtfn, 1, arg0); - - /* Now try 1/6. */ - else if (optimize_insn_for_speed_p ()) - { - REAL_VALUE_TYPE dconst1_6 = dconst1_3; - SET_REAL_EXP (&dconst1_6, REAL_EXP (&dconst1_6) - 1); - - if (REAL_VALUES_EQUAL (c, dconst1_6)) - { - op = build_call_nofold_loc (loc, sqrtfn, 1, arg0); - op = build_call_nofold_loc (loc, cbrtfn, 1, op); - } - } - } - - if (op) - return expand_expr (op, subtarget, mode, EXPAND_NORMAL); - } - - return NULL_RTX; -} - -/* Expand a call to the pow built-in mathematical function. Return NULL_RTX if - a normal call should be emitted rather than expanding the function - in-line. EXP is the expression that is a call to the builtin - function; if convenient, the result should be placed in TARGET. */ - -static rtx -expand_builtin_pow (tree exp, rtx target, rtx subtarget) -{ - tree arg0, arg1; - tree fn, narg0; - 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 (exp, REAL_TYPE, REAL_TYPE, VOID_TYPE)) - return NULL_RTX; - - arg0 = CALL_EXPR_ARG (exp, 0); - arg1 = CALL_EXPR_ARG (exp, 1); - - if (TREE_CODE (arg1) != REAL_CST - || TREE_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_insn_for_speed_p () - && powi_cost (n) <= POWI_MAX_MULTS))) - { - op = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL); - if (n != 1) - { - op = force_reg (mode, op); - op = expand_powi (op, mode, n); - } - return op; - } - - narg0 = builtin_save_expr (arg0); - - /* 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 (&c2, &cint) - && ((flag_unsafe_math_optimizations - && optimize_insn_for_speed_p () - && powi_cost (n/2) <= POWI_MAX_MULTS) - /* Even the c == 0.5 case cannot be done unconditionally - when we need to preserve signed zeros, as - pow (-0, 0.5) is +0, while sqrt(-0) is -0. */ - || (!HONOR_SIGNED_ZEROS (mode) && n == 1) - /* For c == 1.5 we can assume that x * sqrt (x) is always - smaller than pow (x, 1.5) if sqrt will not be expanded - as a call. */ - || (n == 3 - && optab_handler (sqrt_optab, mode) != CODE_FOR_nothing))) - { - tree call_expr = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 1, - narg0); - /* Use expand_expr in case the newly built call expression - was folded to a non-call. */ - op = expand_expr (call_expr, subtarget, mode, EXPAND_NORMAL); - if (n != 1) - { - op2 = expand_expr (narg0, subtarget, VOIDmode, EXPAND_NORMAL); - 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; - } - } - - /* Check whether we can do a series of sqrt or cbrt's instead of the pow - call. */ - op = expand_builtin_pow_root (EXPR_LOCATION (exp), arg0, arg1, type, - subtarget); - if (op) - return op; - - /* Try if the exponent is a third of an integer. In this case - we can expand to x**(n/3) * cbrt(x)**(n%3). As cbrt (x) is - different from pow (x, 1./3.) due to rounding and behavior - with negative x we need to constrain this transformation to - unsafe math and positive x or finite math. */ - fn = mathfn_built_in (type, BUILT_IN_CBRT); - if (fn != NULL_TREE - && flag_unsafe_math_optimizations - && (tree_expr_nonnegative_p (arg0) - || !HONOR_NANS (mode))) - { - REAL_VALUE_TYPE dconst3; - real_from_integer (&dconst3, VOIDmode, 3, 0, 0); - 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_insn_for_speed_p () - && powi_cost (n/3) <= POWI_MAX_MULTS) - || n == 1)) - { - tree call_expr = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 1, - narg0); - 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, EXPAND_NORMAL); - 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); -} - -/* Expand a call to the powi built-in mathematical function. Return NULL_RTX if - a normal call should be emitted rather than expanding the function - in-line. EXP is the expression that is a call to the builtin - function; if convenient, the result should be placed in TARGET. */ - -static rtx -expand_builtin_powi (tree exp, rtx target) -{ - tree arg0, arg1; - rtx op0, op1; - enum machine_mode mode; - enum machine_mode mode2; - - if (! validate_arglist (exp, REAL_TYPE, INTEGER_TYPE, VOID_TYPE)) - return NULL_RTX; - - arg0 = CALL_EXPR_ARG (exp, 0); - arg1 = CALL_EXPR_ARG (exp, 1); - mode = TYPE_MODE (TREE_TYPE (exp)); - - /* Handle constant power. */ - - if (TREE_CODE (arg1) == INTEGER_CST - && !TREE_OVERFLOW (arg1)) - { - HOST_WIDE_INT n = TREE_INT_CST_LOW (arg1); - - /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. - Otherwise, check the number of multiplications required. */ - if ((TREE_INT_CST_HIGH (arg1) == 0 - || TREE_INT_CST_HIGH (arg1) == -1) - && ((n >= -1 && n <= 2) - || (optimize_insn_for_speed_p () - && powi_cost (n) <= POWI_MAX_MULTS))) - { - op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); - op0 = force_reg (mode, op0); - return expand_powi (op0, mode, n); - } - } - - /* Emit a libcall to libgcc. */ - - /* Mode of the 2nd argument must match that of an int. */ - mode2 = mode_for_size (INT_TYPE_SIZE, MODE_INT, 0); - - if (target == NULL_RTX) - target = gen_reg_rtx (mode); - - op0 = expand_expr (arg0, NULL_RTX, mode, EXPAND_NORMAL); - if (GET_MODE (op0) != mode) - op0 = convert_to_mode (mode, op0, 0); - op1 = expand_expr (arg1, NULL_RTX, mode2, EXPAND_NORMAL); - if (GET_MODE (op1) != mode2) - op1 = convert_to_mode (mode2, op1, 0); - - target = emit_library_call_value (optab_libfunc (powi_optab, mode), - target, LCT_CONST, mode, 2, - op0, mode, op1, mode2); - - return target; -} - -/* Expand expression EXP which is a call to the strlen builtin. Return - NULL_RTX if we failed the caller should emit a normal call, otherwise - try to get the result in TARGET, if convenient. */ - -static rtx -expand_builtin_strlen (tree exp, rtx target, - enum machine_mode target_mode) -{ - if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) - return NULL_RTX; - else - { - rtx pat; - tree len; - tree src = CALL_EXPR_ARG (exp, 0); - rtx result, src_reg, char_rtx, before_strlen; - enum machine_mode insn_mode = target_mode, char_mode; - enum insn_code icode = CODE_FOR_nothing; - unsigned int align; - - /* If the length can be computed at compile-time, return it. */ - len = c_strlen (src, 0); - if (len) - return expand_expr (len, target, target_mode, EXPAND_NORMAL); - - /* If the length can be computed at compile-time and is constant - integer, but there are side-effects in src, evaluate - src for side-effects, then return len. - E.g. x = strlen (i++ ? "xfoo" + 1 : "bar"); - can be optimized into: i++; x = 3; */ - len = c_strlen (src, 1); - if (len && TREE_CODE (len) == INTEGER_CST) - { - expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL); - return expand_expr (len, target, target_mode, EXPAND_NORMAL); - } - - align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - - /* If SRC is not a pointer type, don't do this operation inline. */ - if (align == 0) - return NULL_RTX; - - /* Bail out if we can't compute strlen in the right mode. */ - while (insn_mode != VOIDmode) - { - icode = optab_handler (strlen_optab, insn_mode); - if (icode != CODE_FOR_nothing) - break; - - insn_mode = GET_MODE_WIDER_MODE (insn_mode); - } - if (insn_mode == VOIDmode) - return NULL_RTX; - - /* Make a place to write the result of the instruction. */ - result = target; - if (! (result != 0 - && REG_P (result) - && GET_MODE (result) == insn_mode - && REGNO (result) >= FIRST_PSEUDO_REGISTER)) - result = gen_reg_rtx (insn_mode); - - /* Make a place to hold the source address. We will not expand - the actual source until we are sure that the expansion will - not fail -- there are trees that cannot be expanded twice. */ - src_reg = gen_reg_rtx (Pmode); - - /* Mark the beginning of the strlen sequence so we can emit the - source operand later. */ - before_strlen = get_last_insn (); - - char_rtx = const0_rtx; - char_mode = insn_data[(int) icode].operand[2].mode; - if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx, - char_mode)) - char_rtx = copy_to_mode_reg (char_mode, char_rtx); - - pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg), - char_rtx, GEN_INT (align)); - if (! pat) - return NULL_RTX; - emit_insn (pat); + create_output_operand (&ops[0], target, insn_mode); + create_fixed_operand (&ops[1], gen_rtx_MEM (BLKmode, src_reg)); + create_integer_operand (&ops[2], 0); + create_integer_operand (&ops[3], align); + if (!maybe_expand_insn (icode, 4, ops)) + return NULL_RTX; /* Now that we are assured of success, expand the source. */ start_sequence (); - pat = expand_expr (src, src_reg, ptr_mode, EXPAND_NORMAL); + pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL); if (pat != src_reg) - emit_move_insn (src_reg, pat); + { +#ifdef POINTERS_EXTEND_UNSIGNED + if (GET_MODE (pat) != Pmode) + pat = convert_to_mode (Pmode, pat, + POINTERS_EXTEND_UNSIGNED); +#endif + emit_move_insn (src_reg, pat); + } pat = get_insns (); end_sequence (); @@ -3442,12 +3064,12 @@ expand_builtin_strlen (tree exp, rtx target, emit_insn_before (pat, get_insns ()); /* Return the value in the proper mode for this function. */ - if (GET_MODE (result) == target_mode) - target = result; + if (GET_MODE (ops[0].value) == target_mode) + target = ops[0].value; else if (target != 0) - convert_move (target, result, 0); + convert_move (target, ops[0].value, 0); else - target = convert_to_mode (target_mode, result, 0); + target = convert_to_mode (target_mode, ops[0].value, 0); return target; } @@ -3487,9 +3109,8 @@ expand_builtin_memcpy (tree exp, rtx target) tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); const char *src_str; - unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); - unsigned int dest_align - = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + unsigned int src_align = get_pointer_alignment (src); + unsigned int dest_align = get_pointer_alignment (dest); rtx dest_mem, src_mem, dest_addr, len_rtx; HOST_WIDE_INT expected_size = -1; unsigned int expected_align = 0; @@ -3586,9 +3207,9 @@ expand_builtin_mempcpy_args (tree dest, tree src, tree len, rtx target, enum machine_mode mode, int endp) { /* If return value is ignored, transform mempcpy into memcpy. */ - if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_MEMCPY]) + if (target == const0_rtx && builtin_decl_implicit_p (BUILT_IN_MEMCPY)) { - tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY); tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3, dest, src, len); return expand_expr (result, target, mode, EXPAND_NORMAL); @@ -3596,9 +3217,8 @@ expand_builtin_mempcpy_args (tree dest, tree src, tree len, else { const char *src_str; - unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); - unsigned int dest_align - = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + unsigned int src_align = get_pointer_alignment (src); + unsigned int dest_align = get_pointer_alignment (dest); rtx dest_mem, src_mem, len_rtx; /* If either SRC or DEST is not a pointer type, don't do this @@ -3668,56 +3288,38 @@ expand_builtin_mempcpy_args (tree dest, tree src, tree len, static rtx expand_movstr (tree dest, tree src, rtx target, int endp) { - rtx end; + struct expand_operand ops[3]; rtx dest_mem; rtx src_mem; - rtx insn; - const struct insn_data_d * data; if (!HAVE_movstr) return NULL_RTX; dest_mem = get_memory_rtx (dest, NULL); src_mem = get_memory_rtx (src, NULL); - data = insn_data + CODE_FOR_movstr; if (!endp) { target = force_reg (Pmode, XEXP (dest_mem, 0)); dest_mem = replace_equiv_address (dest_mem, target); - end = gen_reg_rtx (Pmode); - } - else - { - if (target == 0 - || target == const0_rtx - || ! (*data->operand[0].predicate) (target, Pmode)) - { - end = gen_reg_rtx (Pmode); - if (target != const0_rtx) - target = end; - } - else - end = target; } - if (data->operand[0].mode != VOIDmode) - end = gen_lowpart (data->operand[0].mode, end); + create_output_operand (&ops[0], endp ? target : NULL_RTX, Pmode); + create_fixed_operand (&ops[1], dest_mem); + create_fixed_operand (&ops[2], src_mem); + expand_insn (CODE_FOR_movstr, 3, ops); - insn = data->genfun (end, dest_mem, src_mem); - - gcc_assert (insn); - - emit_insn (insn); - - /* movstr is supposed to set end to the address of the NUL - terminator. If the caller requested a mempcpy-like return value, - adjust it. */ - if (endp == 1 && target != const0_rtx) + if (endp && target != const0_rtx) { - rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1); - emit_move_insn (target, force_operand (tem, NULL_RTX)); + target = ops[0].value; + /* movstr is supposed to set end to the address of the NUL + terminator. If the caller requested a mempcpy-like return value, + adjust it. */ + if (endp == 1) + { + rtx tem = plus_constant (gen_lowpart (GET_MODE (target), target), 1); + emit_move_insn (target, force_operand (tem, NULL_RTX)); + } } - return target; } @@ -3768,9 +3370,9 @@ expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode) src = CALL_EXPR_ARG (exp, 1); /* If return value is ignored, transform stpcpy into strcpy. */ - if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_STRCPY]) + if (target == const0_rtx && builtin_decl_implicit (BUILT_IN_STRCPY)) { - tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); tree result = build_call_nofold_loc (loc, fn, 2, dst, src); return expand_expr (result, target, mode, EXPAND_NORMAL); } @@ -3869,8 +3471,7 @@ expand_builtin_strncpy (tree exp, rtx target) use store_by_pieces, if it fails, punt. */ if (tree_int_cst_lt (slen, len)) { - unsigned int dest_align - = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + unsigned int dest_align = get_pointer_alignment (dest); const char *p = c_getstr (src); rtx dest_mem; @@ -3967,13 +3568,14 @@ expand_builtin_memset_args (tree dest, tree val, tree len, { tree fndecl, fn; enum built_in_function fcode; + enum machine_mode val_mode; char c; unsigned int dest_align; rtx dest_mem, dest_addr, len_rtx; HOST_WIDE_INT expected_size = -1; unsigned int expected_align = 0; - dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + dest_align = get_pointer_alignment (dest); /* If DEST is not a pointer type, don't do this operation in-line. */ if (dest_align == 0) @@ -4001,14 +3603,14 @@ expand_builtin_memset_args (tree dest, tree val, tree len, len_rtx = expand_normal (len); dest_mem = get_memory_rtx (dest, len); + val_mode = TYPE_MODE (unsigned_char_type_node); if (TREE_CODE (val) != INTEGER_CST) { rtx val_rtx; val_rtx = expand_normal (val); - val_rtx = convert_to_mode (TYPE_MODE (unsigned_char_type_node), - val_rtx, 0); + val_rtx = convert_to_mode (val_mode, val_rtx, 0); /* Assume that we can memset by pieces if we can store * the coefficients by pieces (in the required modes). @@ -4019,8 +3621,7 @@ expand_builtin_memset_args (tree dest, tree val, tree len, builtin_memset_read_str, &c, dest_align, true)) { - val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node), - val_rtx); + val_rtx = force_reg (val_mode, val_rtx); store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_memset_gen_str, val_rtx, dest_align, true, 0); @@ -4046,7 +3647,8 @@ expand_builtin_memset_args (tree dest, tree val, tree len, true)) store_by_pieces (dest_mem, tree_low_cst (len, 1), builtin_memset_read_str, &c, dest_align, true, 0); - else if (!set_storage_via_setmem (dest_mem, len_rtx, GEN_INT (c), + else if (!set_storage_via_setmem (dest_mem, len_rtx, + gen_int_mode (c, val_mode), dest_align, expected_align, expected_size)) goto do_libcall; @@ -4107,14 +3709,15 @@ expand_builtin_bzero (tree exp) calling bzero instead of memset. */ return expand_builtin_memset_args (dest, integer_zero_node, - fold_convert_loc (loc, sizetype, size), + fold_convert_loc (loc, + size_type_node, size), const0_rtx, VOIDmode, exp); } /* Expand expression EXP, which is a call to the memcmp built-in function. - Return NULL_RTX if we failed and the - caller should emit a normal call, otherwise try to get the result in - TARGET, if convenient (and in mode MODE, if that's convenient). */ + Return NULL_RTX if we failed and the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in mode + MODE, if that's convenient). */ static rtx expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, @@ -4126,7 +3729,10 @@ expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) return NULL_RTX; -#if defined HAVE_cmpmemsi || defined HAVE_cmpstrnsi + /* Note: The cmpstrnsi pattern, if it exists, is not suitable for + implementing memcmp because it will stop if it encounters two + zero bytes. */ +#if defined HAVE_cmpmemsi { rtx arg1_rtx, arg2_rtx, arg3_rtx; rtx result; @@ -4135,22 +3741,13 @@ expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, tree arg2 = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); - unsigned int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - unsigned int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; + unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; enum machine_mode insn_mode; -#ifdef HAVE_cmpmemsi if (HAVE_cmpmemsi) insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode; else -#endif -#ifdef HAVE_cmpstrnsi - if (HAVE_cmpstrnsi) - insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; - else -#endif return NULL_RTX; /* If we don't have POINTER_TYPE, call the function. */ @@ -4171,22 +3768,14 @@ expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, /* Set MEM_SIZE as appropriate. */ if (CONST_INT_P (arg3_rtx)) { - set_mem_size (arg1_rtx, arg3_rtx); - set_mem_size (arg2_rtx, arg3_rtx); + set_mem_size (arg1_rtx, INTVAL (arg3_rtx)); + set_mem_size (arg2_rtx, INTVAL (arg3_rtx)); } -#ifdef HAVE_cmpmemsi if (HAVE_cmpmemsi) insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx, GEN_INT (MIN (arg1_align, arg2_align))); else -#endif -#ifdef HAVE_cmpstrnsi - if (HAVE_cmpstrnsi) - insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx, - GEN_INT (MIN (arg1_align, arg2_align))); - else -#endif gcc_unreachable (); if (insn) @@ -4212,7 +3801,7 @@ expand_builtin_memcmp (tree exp, ATTRIBUTE_UNUSED rtx target, else return convert_to_mode (mode, result, 0); } -#endif +#endif /* HAVE_cmpmemsi. */ return NULL_RTX; } @@ -4237,10 +3826,8 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target) tree arg1 = CALL_EXPR_ARG (exp, 0); tree arg2 = CALL_EXPR_ARG (exp, 1); - unsigned int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - unsigned int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; + unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; /* If we don't have POINTER_TYPE, call the function. */ if (arg1_align == 0 || arg2_align == 0) @@ -4388,10 +3975,8 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target, tree arg2 = CALL_EXPR_ARG (exp, 1); tree arg3 = CALL_EXPR_ARG (exp, 2); - unsigned int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - unsigned int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT; + unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT; enum machine_mode insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode; @@ -4696,7 +4281,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, type = build_pointer_type (type); align = PARM_BOUNDARY / BITS_PER_UNIT; - boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type); + boundary = targetm.calls.function_arg_boundary (TYPE_MODE (type), type); /* When we align parameter on stack for caller, if the parameter alignment is beyond MAX_SUPPORTED_STACK_ALIGNMENT, it will be @@ -4716,16 +4301,13 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, && !integer_zerop (TYPE_SIZE (type))) { t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, - fold_build2 (POINTER_PLUS_EXPR, - TREE_TYPE (valist), - valist_tmp, size_int (boundary - 1))); + fold_build_pointer_plus_hwi (valist_tmp, boundary - 1)); gimplify_and_add (t, pre_p); - t = fold_convert (sizetype, valist_tmp); t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, - fold_convert (TREE_TYPE (valist), - fold_build2 (BIT_AND_EXPR, sizetype, t, - size_int (-boundary)))); + fold_build2 (BIT_AND_EXPR, TREE_TYPE (valist), + valist_tmp, + build_int_cst (TREE_TYPE (valist), -boundary))); gimplify_and_add (t, pre_p); } else @@ -4757,12 +4339,11 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, rounded_size, size_int (align)); t = fold_build3 (COND_EXPR, sizetype, t, size_zero_node, size_binop (MINUS_EXPR, rounded_size, type_size)); - addr = fold_build2 (POINTER_PLUS_EXPR, - TREE_TYPE (addr), addr, t); + addr = fold_build_pointer_plus (addr, t); } /* Compute new value for AP. */ - t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (valist), valist_tmp, rounded_size); + t = fold_build_pointer_plus (valist_tmp, rounded_size); t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t); gimplify_and_add (t, pre_p); @@ -4779,7 +4360,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, tree build_va_arg_indirect_ref (tree addr) { - addr = build_fold_indirect_ref_loc (EXPR_LOCATION (addr), addr); + addr = build_simple_mem_ref_loc (EXPR_LOCATION (addr), addr); if (flag_mudflap) /* Don't instrument va_arg INDIRECT_REF. */ mf_mark (addr); @@ -4850,7 +4431,7 @@ gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) expression to exit or longjmp. */ gimplify_and_add (valist, pre_p); t = build_call_expr_loc (loc, - implicit_built_in_decls[BUILT_IN_TRAP], 0); + builtin_decl_implicit (BUILT_IN_TRAP), 0); gimplify_and_add (t, pre_p); /* This is dead code, but go ahead and finish so that the @@ -5013,20 +4594,33 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate) { rtx op0; rtx result; + bool valid_arglist; + unsigned int align; + bool alloca_with_align = (DECL_FUNCTION_CODE (get_callee_fndecl (exp)) + == BUILT_IN_ALLOCA_WITH_ALIGN); - /* Emit normal call if marked not-inlineable. */ - if (CALL_CANNOT_INLINE_P (exp)) + /* Emit normal call if we use mudflap. */ + if (flag_mudflap) return NULL_RTX; - if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE)) + valid_arglist + = (alloca_with_align + ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE) + : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE)); + + if (!valid_arglist) return NULL_RTX; /* Compute the argument. */ op0 = expand_normal (CALL_EXPR_ARG (exp, 0)); + /* Compute the alignment. */ + align = (alloca_with_align + ? TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1)) + : BIGGEST_ALIGNMENT); + /* Allocate the desired space. */ - result = allocate_dynamic_stack_space (op0, 0, BIGGEST_ALIGNMENT, - cannot_accumulate); + result = allocate_dynamic_stack_space (op0, 0, align, cannot_accumulate); result = convert_memory_address (ptr_mode, result); return result; @@ -5079,7 +4673,7 @@ expand_builtin_unop (enum machine_mode target_mode, tree exp, rtx target, /* Compute op, into TARGET if possible. Set TARGET to wherever the result comes back. */ target = expand_unop (TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 0))), - op_optab, op0, target, 1); + op_optab, op0, target, op_optab != clrsb_optab); gcc_assert (target); return convert_to_mode (target_mode, target, 0); @@ -5105,12 +4699,36 @@ expand_builtin_expect (tree exp, rtx target) return target; } +/* Expand a call to __builtin_assume_aligned. We just return our first + argument as the builtin_assume_aligned semantic should've been already + executed by CCP. */ + +static rtx +expand_builtin_assume_aligned (tree exp, rtx target) +{ + if (call_expr_nargs (exp) < 2) + return const0_rtx; + target = expand_expr (CALL_EXPR_ARG (exp, 0), target, VOIDmode, + EXPAND_NORMAL); + gcc_assert (!TREE_SIDE_EFFECTS (CALL_EXPR_ARG (exp, 1)) + && (call_expr_nargs (exp) < 3 + || !TREE_SIDE_EFFECTS (CALL_EXPR_ARG (exp, 2)))); + return target; +} + void expand_builtin_trap (void) { #ifdef HAVE_trap if (HAVE_trap) - emit_insn (gen_trap ()); + { + rtx insn = emit_insn (gen_trap ()); + /* For trap insns when not accumulating outgoing args force + REG_ARGS_SIZE note to prevent crossjumping of calls with + different args sizes. */ + if (!ACCUMULATE_OUTGOING_ARGS) + add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta)); + } else #endif emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0); @@ -5197,30 +4815,6 @@ build_string_literal (int len, const char *str) return t; } -/* Expand a call to either the entry or exit function profiler. */ - -static rtx -expand_builtin_profile_func (bool exitp) -{ - rtx this_rtx, which; - - this_rtx = DECL_RTL (current_function_decl); - gcc_assert (MEM_P (this_rtx)); - this_rtx = XEXP (this_rtx, 0); - - if (exitp) - which = profile_function_exit_libfunc; - else - which = profile_function_entry_libfunc; - - emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this_rtx, Pmode, - expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, - 0), - Pmode); - - return const0_rtx; -} - /* Expand a call to __builtin___clear_cache. */ static rtx @@ -5241,7 +4835,6 @@ expand_builtin___clear_cache (tree exp ATTRIBUTE_UNUSED) /* We have a "clear_cache" insn, and it will handle everything. */ tree begin, end; rtx begin_rtx, end_rtx; - enum insn_code icode; /* We must not expand to a library call. If we did, any fallback library function in libgcc that might contain a call to @@ -5254,21 +4847,18 @@ expand_builtin___clear_cache (tree exp ATTRIBUTE_UNUSED) if (HAVE_clear_cache) { - icode = CODE_FOR_clear_cache; + struct expand_operand ops[2]; begin = CALL_EXPR_ARG (exp, 0); begin_rtx = expand_expr (begin, NULL_RTX, Pmode, EXPAND_NORMAL); - begin_rtx = convert_memory_address (Pmode, begin_rtx); - if (!insn_data[icode].operand[0].predicate (begin_rtx, Pmode)) - begin_rtx = copy_to_mode_reg (Pmode, begin_rtx); end = CALL_EXPR_ARG (exp, 1); end_rtx = expand_expr (end, NULL_RTX, Pmode, EXPAND_NORMAL); - end_rtx = convert_memory_address (Pmode, end_rtx); - if (!insn_data[icode].operand[1].predicate (end_rtx, Pmode)) - end_rtx = copy_to_mode_reg (Pmode, end_rtx); - emit_insn (gen_clear_cache (begin_rtx, end_rtx)); + create_address_operand (&ops[0], begin_rtx); + create_address_operand (&ops[1], end_rtx); + if (maybe_expand_insn (CODE_FOR_clear_cache, 2, ops)) + return const0_rtx; } return const0_rtx; #endif /* HAVE_clear_cache */ @@ -5300,7 +4890,7 @@ round_trampoline_addr (rtx tramp) } static rtx -expand_builtin_init_trampoline (tree exp) +expand_builtin_init_trampoline (tree exp, bool onstack) { tree t_tramp, t_func, t_chain; rtx m_tramp, r_tramp, r_chain, tmp; @@ -5317,19 +4907,22 @@ expand_builtin_init_trampoline (tree exp) m_tramp = gen_rtx_MEM (BLKmode, r_tramp); MEM_NOTRAP_P (m_tramp) = 1; - /* The TRAMP argument should be the address of a field within the - local function's FRAME decl. Let's see if we can fill in the - to fill in the MEM_ATTRs for this memory. */ + /* If ONSTACK, the TRAMP argument should be the address of a field + within the local function's FRAME decl. Either way, let's see if + we can fill in the MEM_ATTRs for this memory. */ if (TREE_CODE (t_tramp) == ADDR_EXPR) set_mem_attributes_minus_bitpos (m_tramp, TREE_OPERAND (t_tramp, 0), true, 0); + /* Creator of a heap trampoline is responsible for making sure the + address is aligned to at least STACK_BOUNDARY. Normally malloc + will ensure this anyhow. */ tmp = round_trampoline_addr (r_tramp); if (tmp != r_tramp) { m_tramp = change_address (m_tramp, BLKmode, tmp); set_mem_align (m_tramp, TRAMPOLINE_ALIGNMENT); - set_mem_size (m_tramp, GEN_INT (TRAMPOLINE_SIZE)); + set_mem_size (m_tramp, TRAMPOLINE_SIZE); } /* The FUNC argument should be the address of the nested function. @@ -5343,10 +4936,13 @@ expand_builtin_init_trampoline (tree exp) /* Generate insns to initialize the trampoline. */ targetm.calls.trampoline_init (m_tramp, t_func, r_chain); - trampolines_created = 1; + if (onstack) + { + trampolines_created = 1; - warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines, - "trampoline generated for nested function %qD", t_func); + warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines, + "trampoline generated for nested function %qD", t_func); + } return const0_rtx; } @@ -5469,8 +5065,7 @@ expand_builtin_signbit (tree exp, rtx target) /* Perform a logical right shift to place the signbit in the least significant bit, then truncate the result to the desired mode and mask just this bit. */ - temp = expand_shift (RSHIFT_EXPR, imode, temp, - build_int_cst (NULL_TREE, bitpos), NULL_RTX, 1); + temp = expand_shift (RSHIFT_EXPR, imode, temp, bitpos, NULL_RTX, 1); temp = gen_lowpart (rmode, temp); temp = expand_binop (rmode, and_optab, temp, const1_rtx, NULL_RTX, 1, OPTAB_LIB_WIDEN); @@ -5580,28 +5175,48 @@ get_builtin_sync_mem (tree loc, enum machine_mode mode) /* The alignment needs to be at least according to that of the mode. */ set_mem_align (mem, MAX (GET_MODE_ALIGNMENT (mode), - get_pointer_alignment (loc, BIGGEST_ALIGNMENT))); + get_pointer_alignment (loc))); set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); MEM_VOLATILE_P (mem) = 1; return mem; } +/* Make sure an argument is in the right mode. + EXP is the tree argument. + MODE is the mode it should be in. */ + +static rtx +expand_expr_force_mode (tree exp, enum machine_mode mode) +{ + rtx val; + enum machine_mode old_mode; + + val = expand_expr (exp, NULL_RTX, mode, EXPAND_NORMAL); + /* If VAL is promoted to a wider mode, convert it back to MODE. Take care + of CONST_INTs, where we know the old_mode only from the call argument. */ + + old_mode = GET_MODE (val); + if (old_mode == VOIDmode) + old_mode = TYPE_MODE (TREE_TYPE (exp)); + val = convert_modes (mode, old_mode, val, 1); + return val; +} + + /* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics. EXP is the CALL_EXPR. CODE is the rtx code that corresponds to the arithmetic or logical operation from the name; an exception here is that NOT actually means NAND. TARGET is an optional place for us to store the results; AFTER is true if this is the - fetch_and_xxx form. IGNORE is true if we don't actually care about - the result of the operation at all. */ + fetch_and_xxx form. */ static rtx expand_builtin_sync_operation (enum machine_mode mode, tree exp, enum rtx_code code, bool after, - rtx target, bool ignore) + rtx target) { rtx val, mem; - enum machine_mode old_mode; location_t loc = EXPR_LOCATION (exp); if (code == NOT && warn_sync_nand) @@ -5613,30 +5228,28 @@ expand_builtin_sync_operation (enum machine_mode mode, tree exp, switch (fcode) { - case BUILT_IN_FETCH_AND_NAND_1: - case BUILT_IN_FETCH_AND_NAND_2: - case BUILT_IN_FETCH_AND_NAND_4: - case BUILT_IN_FETCH_AND_NAND_8: - case BUILT_IN_FETCH_AND_NAND_16: - + case BUILT_IN_SYNC_FETCH_AND_NAND_1: + case BUILT_IN_SYNC_FETCH_AND_NAND_2: + case BUILT_IN_SYNC_FETCH_AND_NAND_4: + case BUILT_IN_SYNC_FETCH_AND_NAND_8: + case BUILT_IN_SYNC_FETCH_AND_NAND_16: if (warned_f_a_n) break; - fndecl = implicit_built_in_decls[BUILT_IN_FETCH_AND_NAND_N]; + fndecl = builtin_decl_implicit (BUILT_IN_SYNC_FETCH_AND_NAND_N); inform (loc, "%qD changed semantics in GCC 4.4", fndecl); warned_f_a_n = true; break; - case BUILT_IN_NAND_AND_FETCH_1: - case BUILT_IN_NAND_AND_FETCH_2: - case BUILT_IN_NAND_AND_FETCH_4: - case BUILT_IN_NAND_AND_FETCH_8: - case BUILT_IN_NAND_AND_FETCH_16: - + case BUILT_IN_SYNC_NAND_AND_FETCH_1: + case BUILT_IN_SYNC_NAND_AND_FETCH_2: + case BUILT_IN_SYNC_NAND_AND_FETCH_4: + case BUILT_IN_SYNC_NAND_AND_FETCH_8: + case BUILT_IN_SYNC_NAND_AND_FETCH_16: if (warned_n_a_f) break; - fndecl = implicit_built_in_decls[BUILT_IN_NAND_AND_FETCH_N]; + fndecl = builtin_decl_implicit (BUILT_IN_SYNC_NAND_AND_FETCH_N); inform (loc, "%qD changed semantics in GCC 4.4", fndecl); warned_n_a_f = true; break; @@ -5648,19 +5261,10 @@ expand_builtin_sync_operation (enum machine_mode mode, tree exp, /* Expand the operands. */ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode); - val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL_RTX, mode, EXPAND_NORMAL); - /* If VAL is promoted to a wider mode, convert it back to MODE. Take care - of CONST_INTs, where we know the old_mode only from the call argument. */ - old_mode = GET_MODE (val); - if (old_mode == VOIDmode) - old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1))); - val = convert_modes (mode, old_mode, val, 1); - - if (ignore) - return expand_sync_operation (mem, val, code); - else - return expand_sync_fetch_operation (mem, val, code, after, target); + return expand_atomic_fetch_op (target, mem, val, code, MEMMODEL_SEQ_CST, + after); } /* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap @@ -5673,34 +5277,27 @@ expand_builtin_compare_and_swap (enum machine_mode mode, tree exp, bool is_bool, rtx target) { rtx old_val, new_val, mem; - enum machine_mode old_mode; + rtx *pbool, *poval; /* Expand the operands. */ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + old_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode); + new_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode); + pbool = poval = NULL; + if (target != const0_rtx) + { + if (is_bool) + pbool = ⌖ + else + poval = ⌖ + } + if (!expand_atomic_compare_and_swap (pbool, poval, mem, old_val, new_val, + false, MEMMODEL_SEQ_CST, + MEMMODEL_SEQ_CST)) + return NULL_RTX; - old_val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL_RTX, - mode, EXPAND_NORMAL); - /* If VAL is promoted to a wider mode, convert it back to MODE. Take care - of CONST_INTs, where we know the old_mode only from the call argument. */ - old_mode = GET_MODE (old_val); - if (old_mode == VOIDmode) - old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1))); - old_val = convert_modes (mode, old_mode, old_val, 1); - - new_val = expand_expr (CALL_EXPR_ARG (exp, 2), NULL_RTX, - mode, EXPAND_NORMAL); - /* If VAL is promoted to a wider mode, convert it back to MODE. Take care - of CONST_INTs, where we know the old_mode only from the call argument. */ - old_mode = GET_MODE (new_val); - if (old_mode == VOIDmode) - old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 2))); - new_val = convert_modes (mode, old_mode, new_val, 1); - - if (is_bool) - return expand_bool_compare_and_swap (mem, old_val, new_val, target); - else - return expand_val_compare_and_swap (mem, old_val, new_val, target); + return target; } /* Expand the __sync_lock_test_and_set intrinsic. Note that the most @@ -5710,89 +5307,488 @@ expand_builtin_compare_and_swap (enum machine_mode mode, tree exp, the results. */ static rtx -expand_builtin_lock_test_and_set (enum machine_mode mode, tree exp, - rtx target) +expand_builtin_sync_lock_test_and_set (enum machine_mode mode, tree exp, + rtx target) { rtx val, mem; - enum machine_mode old_mode; /* Expand the operands. */ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); - val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL_RTX, mode, EXPAND_NORMAL); - /* If VAL is promoted to a wider mode, convert it back to MODE. Take care - of CONST_INTs, where we know the old_mode only from the call argument. */ - old_mode = GET_MODE (val); - if (old_mode == VOIDmode) - old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1))); - val = convert_modes (mode, old_mode, val, 1); + val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode); - return expand_sync_lock_test_and_set (mem, val, target); + return expand_sync_lock_test_and_set (target, mem, val); } -/* Expand the __sync_synchronize intrinsic. */ +/* Expand the __sync_lock_release intrinsic. EXP is the CALL_EXPR. */ static void -expand_builtin_synchronize (void) +expand_builtin_sync_lock_release (enum machine_mode mode, tree exp) { - gimple x; - VEC (tree, gc) *v_clobbers; + rtx mem; -#ifdef HAVE_memory_barrier - if (HAVE_memory_barrier) + /* Expand the operands. */ + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + + expand_atomic_store (mem, const0_rtx, MEMMODEL_RELEASE, true); +} + +/* Given an integer representing an ``enum memmodel'', verify its + correctness and return the memory model enum. */ + +static enum memmodel +get_memmodel (tree exp) +{ + rtx op; + + /* If the parameter is not a constant, it's a run time value so we'll just + convert it to MEMMODEL_SEQ_CST to avoid annoying runtime checking. */ + if (TREE_CODE (exp) != INTEGER_CST) + return MEMMODEL_SEQ_CST; + + op = expand_normal (exp); + if (INTVAL (op) < 0 || INTVAL (op) >= MEMMODEL_LAST) { - emit_insn (gen_memory_barrier ()); - return; + warning (OPT_Winvalid_memory_model, + "invalid memory model argument to builtin"); + return MEMMODEL_SEQ_CST; } -#endif + return (enum memmodel) INTVAL (op); +} + +/* Expand the __atomic_exchange intrinsic: + TYPE __atomic_exchange (TYPE *object, TYPE desired, enum memmodel) + EXP is the CALL_EXPR. + TARGET is an optional place for us to store the results. */ + +static rtx +expand_builtin_atomic_exchange (enum machine_mode mode, tree exp, rtx target) +{ + rtx val, mem; + enum memmodel model; - if (synchronize_libfunc != NULL_RTX) + model = get_memmodel (CALL_EXPR_ARG (exp, 2)); + if (model == MEMMODEL_CONSUME) { - emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode, 0); - return; + error ("invalid memory model for %<__atomic_exchange%>"); + return NULL_RTX; + } + + if (!flag_inline_atomics) + return NULL_RTX; + + /* Expand the operands. */ + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode); + + return expand_atomic_exchange (target, mem, val, model); +} + +/* Expand the __atomic_compare_exchange intrinsic: + bool __atomic_compare_exchange (TYPE *object, TYPE *expect, + TYPE desired, BOOL weak, + enum memmodel success, + enum memmodel failure) + EXP is the CALL_EXPR. + TARGET is an optional place for us to store the results. */ + +static rtx +expand_builtin_atomic_compare_exchange (enum machine_mode mode, tree exp, + rtx target) +{ + rtx expect, desired, mem, oldval; + enum memmodel success, failure; + tree weak; + bool is_weak; + + success = get_memmodel (CALL_EXPR_ARG (exp, 4)); + failure = get_memmodel (CALL_EXPR_ARG (exp, 5)); + + if (failure == MEMMODEL_RELEASE || failure == MEMMODEL_ACQ_REL) + { + error ("invalid failure memory model for %<__atomic_compare_exchange%>"); + return NULL_RTX; + } + + if (failure > success) + { + error ("failure memory model cannot be stronger than success " + "memory model for %<__atomic_compare_exchange%>"); + return NULL_RTX; } + + if (!flag_inline_atomics) + return NULL_RTX; + + /* Expand the operands. */ + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); - /* If no explicit memory barrier instruction is available, create an - empty asm stmt with a memory clobber. */ - v_clobbers = VEC_alloc (tree, gc, 1); - VEC_quick_push (tree, v_clobbers, - tree_cons (NULL, build_string (6, "memory"), NULL)); - x = gimple_build_asm_vec ("", NULL, NULL, v_clobbers, NULL); - gimple_asm_set_volatile (x, true); - expand_asm_stmt (x); + expect = expand_normal (CALL_EXPR_ARG (exp, 1)); + expect = convert_memory_address (Pmode, expect); + desired = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode); + + weak = CALL_EXPR_ARG (exp, 3); + is_weak = false; + if (host_integerp (weak, 0) && tree_low_cst (weak, 0) != 0) + is_weak = true; + + oldval = copy_to_reg (gen_rtx_MEM (mode, expect)); + + if (!expand_atomic_compare_and_swap ((target == const0_rtx ? NULL : &target), + &oldval, mem, oldval, desired, + is_weak, success, failure)) + return NULL_RTX; + + emit_move_insn (gen_rtx_MEM (mode, expect), oldval); + return target; } -/* Expand the __sync_lock_release intrinsic. EXP is the CALL_EXPR. */ +/* Expand the __atomic_load intrinsic: + TYPE __atomic_load (TYPE *object, enum memmodel) + EXP is the CALL_EXPR. + TARGET is an optional place for us to store the results. */ -static void -expand_builtin_lock_release (enum machine_mode mode, tree exp) +static rtx +expand_builtin_atomic_load (enum machine_mode mode, tree exp, rtx target) { - enum insn_code icode; - rtx mem, insn; - rtx val = const0_rtx; + rtx mem; + enum memmodel model; + + model = get_memmodel (CALL_EXPR_ARG (exp, 1)); + if (model == MEMMODEL_RELEASE + || model == MEMMODEL_ACQ_REL) + { + error ("invalid memory model for %<__atomic_load%>"); + return NULL_RTX; + } + + if (!flag_inline_atomics) + return NULL_RTX; + + /* Expand the operand. */ + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + + return expand_atomic_load (target, mem, model); +} + + +/* Expand the __atomic_store intrinsic: + void __atomic_store (TYPE *object, TYPE desired, enum memmodel) + EXP is the CALL_EXPR. + TARGET is an optional place for us to store the results. */ + +static rtx +expand_builtin_atomic_store (enum machine_mode mode, tree exp) +{ + rtx mem, val; + enum memmodel model; + + model = get_memmodel (CALL_EXPR_ARG (exp, 2)); + if (model != MEMMODEL_RELAXED + && model != MEMMODEL_SEQ_CST + && model != MEMMODEL_RELEASE) + { + error ("invalid memory model for %<__atomic_store%>"); + return NULL_RTX; + } + + if (!flag_inline_atomics) + return NULL_RTX; /* Expand the operands. */ mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode); - /* If there is an explicit operation in the md file, use it. */ - icode = direct_optab_handler (sync_lock_release_optab, mode); - if (icode != CODE_FOR_nothing) + return expand_atomic_store (mem, val, model, false); +} + +/* Expand the __atomic_fetch_XXX intrinsic: + TYPE __atomic_fetch_XXX (TYPE *object, TYPE val, enum memmodel) + EXP is the CALL_EXPR. + TARGET is an optional place for us to store the results. + CODE is the operation, PLUS, MINUS, ADD, XOR, or IOR. + FETCH_AFTER is true if returning the result of the operation. + FETCH_AFTER is false if returning the value before the operation. + IGNORE is true if the result is not used. + EXT_CALL is the correct builtin for an external call if this cannot be + resolved to an instruction sequence. */ + +static rtx +expand_builtin_atomic_fetch_op (enum machine_mode mode, tree exp, rtx target, + enum rtx_code code, bool fetch_after, + bool ignore, enum built_in_function ext_call) +{ + rtx val, mem, ret; + enum memmodel model; + tree fndecl; + tree addr; + + model = get_memmodel (CALL_EXPR_ARG (exp, 2)); + + /* Expand the operands. */ + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode); + + /* Only try generating instructions if inlining is turned on. */ + if (flag_inline_atomics) + { + ret = expand_atomic_fetch_op (target, mem, val, code, model, fetch_after); + if (ret) + return ret; + } + + /* Return if a different routine isn't needed for the library call. */ + if (ext_call == BUILT_IN_NONE) + return NULL_RTX; + + /* Change the call to the specified function. */ + fndecl = get_callee_fndecl (exp); + addr = CALL_EXPR_FN (exp); + STRIP_NOPS (addr); + + gcc_assert (TREE_OPERAND (addr, 0) == fndecl); + TREE_OPERAND (addr, 0) = builtin_decl_explicit(ext_call); + + /* Expand the call here so we can emit trailing code. */ + ret = expand_call (exp, target, ignore); + + /* Replace the original function just in case it matters. */ + TREE_OPERAND (addr, 0) = fndecl; + + /* Then issue the arithmetic correction to return the right result. */ + if (!ignore) + { + if (code == NOT) + { + ret = expand_simple_binop (mode, AND, ret, val, NULL_RTX, true, + OPTAB_LIB_WIDEN); + ret = expand_simple_unop (mode, NOT, ret, target, true); + } + else + ret = expand_simple_binop (mode, code, ret, val, target, true, + OPTAB_LIB_WIDEN); + } + return ret; +} + + +#ifndef HAVE_atomic_clear +# define HAVE_atomic_clear 0 +# define gen_atomic_clear(x,y) (gcc_unreachable (), NULL_RTX) +#endif + +/* Expand an atomic clear operation. + void _atomic_clear (BOOL *obj, enum memmodel) + EXP is the call expression. */ + +static rtx +expand_builtin_atomic_clear (tree exp) +{ + enum machine_mode mode; + rtx mem, ret; + enum memmodel model; + + mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0); + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + model = get_memmodel (CALL_EXPR_ARG (exp, 1)); + + if (model == MEMMODEL_ACQUIRE || model == MEMMODEL_ACQ_REL) + { + error ("invalid memory model for %<__atomic_store%>"); + return const0_rtx; + } + + if (HAVE_atomic_clear) + { + emit_insn (gen_atomic_clear (mem, model)); + return const0_rtx; + } + + /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release. + Failing that, a store is issued by __atomic_store. The only way this can + fail is if the bool type is larger than a word size. Unlikely, but + handle it anyway for completeness. Assume a single threaded model since + there is no atomic support in this case, and no barriers are required. */ + ret = expand_atomic_store (mem, const0_rtx, model, true); + if (!ret) + emit_move_insn (mem, const0_rtx); + return const0_rtx; +} + +/* Expand an atomic test_and_set operation. + bool _atomic_test_and_set (BOOL *obj, enum memmodel) + EXP is the call expression. */ + +static rtx +expand_builtin_atomic_test_and_set (tree exp, rtx target) +{ + rtx mem; + enum memmodel model; + enum machine_mode mode; + + mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0); + mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode); + model = get_memmodel (CALL_EXPR_ARG (exp, 1)); + + return expand_atomic_test_and_set (target, mem, model); +} + + +/* Return true if (optional) argument ARG1 of size ARG0 is always lock free on + this architecture. If ARG1 is NULL, use typical alignment for size ARG0. */ + +static tree +fold_builtin_atomic_always_lock_free (tree arg0, tree arg1) +{ + int size; + enum machine_mode mode; + unsigned int mode_align, type_align; + + if (TREE_CODE (arg0) != INTEGER_CST) + return NULL_TREE; + + size = INTVAL (expand_normal (arg0)) * BITS_PER_UNIT; + mode = mode_for_size (size, MODE_INT, 0); + mode_align = GET_MODE_ALIGNMENT (mode); + + if (TREE_CODE (arg1) == INTEGER_CST && INTVAL (expand_normal (arg1)) == 0) + type_align = mode_align; + else + { + tree ttype = TREE_TYPE (arg1); + + /* This function is usually invoked and folded immediately by the front + end before anything else has a chance to look at it. The pointer + parameter at this point is usually cast to a void *, so check for that + and look past the cast. */ + if (TREE_CODE (arg1) == NOP_EXPR && POINTER_TYPE_P (ttype) + && VOID_TYPE_P (TREE_TYPE (ttype))) + arg1 = TREE_OPERAND (arg1, 0); + + ttype = TREE_TYPE (arg1); + gcc_assert (POINTER_TYPE_P (ttype)); + + /* Get the underlying type of the object. */ + ttype = TREE_TYPE (ttype); + type_align = TYPE_ALIGN (ttype); + } + + /* If the object has smaller alignment, the the lock free routines cannot + be used. */ + if (type_align < mode_align) + return boolean_false_node; + + /* Check if a compare_and_swap pattern exists for the mode which represents + the required size. The pattern is not allowed to fail, so the existence + of the pattern indicates support is present. */ + if (can_compare_and_swap_p (mode, true)) + return boolean_true_node; + else + return boolean_false_node; +} + +/* Return true if the parameters to call EXP represent an object which will + always generate lock free instructions. The first argument represents the + size of the object, and the second parameter is a pointer to the object + itself. If NULL is passed for the object, then the result is based on + typical alignment for an object of the specified size. Otherwise return + false. */ + +static rtx +expand_builtin_atomic_always_lock_free (tree exp) +{ + tree size; + tree arg0 = CALL_EXPR_ARG (exp, 0); + tree arg1 = CALL_EXPR_ARG (exp, 1); + + if (TREE_CODE (arg0) != INTEGER_CST) { - if (!insn_data[icode].operand[1].predicate (val, mode)) - val = force_reg (mode, val); + error ("non-constant argument 1 to __atomic_always_lock_free"); + return const0_rtx; + } + + size = fold_builtin_atomic_always_lock_free (arg0, arg1); + if (size == boolean_true_node) + return const1_rtx; + return const0_rtx; +} + +/* Return a one or zero if it can be determined that object ARG1 of size ARG + is lock free on this architecture. */ + +static tree +fold_builtin_atomic_is_lock_free (tree arg0, tree arg1) +{ + if (!flag_inline_atomics) + return NULL_TREE; + + /* If it isn't always lock free, don't generate a result. */ + if (fold_builtin_atomic_always_lock_free (arg0, arg1) == boolean_true_node) + return boolean_true_node; + + return NULL_TREE; +} + +/* Return true if the parameters to call EXP represent an object which will + always generate lock free instructions. The first argument represents the + size of the object, and the second parameter is a pointer to the object + itself. If NULL is passed for the object, then the result is based on + typical alignment for an object of the specified size. Otherwise return + NULL*/ + +static rtx +expand_builtin_atomic_is_lock_free (tree exp) +{ + tree size; + tree arg0 = CALL_EXPR_ARG (exp, 0); + tree arg1 = CALL_EXPR_ARG (exp, 1); + + if (!INTEGRAL_TYPE_P (TREE_TYPE (arg0))) + { + error ("non-integer argument 1 to __atomic_is_lock_free"); + return NULL_RTX; + } + + if (!flag_inline_atomics) + return NULL_RTX; + + /* If the value is known at compile time, return the RTX for it. */ + size = fold_builtin_atomic_is_lock_free (arg0, arg1); + if (size == boolean_true_node) + return const1_rtx; + + return NULL_RTX; +} + +/* Expand the __atomic_thread_fence intrinsic: + void __atomic_thread_fence (enum memmodel) + EXP is the CALL_EXPR. */ + +static void +expand_builtin_atomic_thread_fence (tree exp) +{ + enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0)); + expand_mem_thread_fence (model); +} + +/* Expand the __atomic_signal_fence intrinsic: + void __atomic_signal_fence (enum memmodel) + EXP is the CALL_EXPR. */ + +static void +expand_builtin_atomic_signal_fence (tree exp) +{ + enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0)); + expand_mem_signal_fence (model); +} - insn = GEN_FCN (icode) (mem, val); - if (insn) - { - emit_insn (insn); - return; - } - } +/* Expand the __sync_synchronize intrinsic. */ - /* Otherwise we can implement this operation by emitting a barrier - followed by a store of zero. */ - expand_builtin_synchronize (); - emit_move_insn (mem, val); +static void +expand_builtin_sync_synchronize (void) +{ + expand_mem_thread_fence (MEMMODEL_SEQ_CST); } + /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient @@ -5816,8 +5812,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, set of builtins. */ if (!optimize && !called_as_built_in (fndecl) - && DECL_ASSEMBLER_NAME_SET_P (fndecl) && fcode != BUILT_IN_ALLOCA + && fcode != BUILT_IN_ALLOCA_WITH_ALIGN && fcode != BUILT_IN_FREE) return expand_call (exp, target, ignore); @@ -5920,17 +5916,21 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; + CASE_FLT_FN (BUILT_IN_ICEIL): CASE_FLT_FN (BUILT_IN_LCEIL): CASE_FLT_FN (BUILT_IN_LLCEIL): CASE_FLT_FN (BUILT_IN_LFLOOR): + CASE_FLT_FN (BUILT_IN_IFLOOR): CASE_FLT_FN (BUILT_IN_LLFLOOR): target = expand_builtin_int_roundingfn (exp, target); if (target) return target; break; + CASE_FLT_FN (BUILT_IN_IRINT): CASE_FLT_FN (BUILT_IN_LRINT): CASE_FLT_FN (BUILT_IN_LLRINT): + CASE_FLT_FN (BUILT_IN_IROUND): CASE_FLT_FN (BUILT_IN_LROUND): CASE_FLT_FN (BUILT_IN_LLROUND): target = expand_builtin_int_roundingfn_2 (exp, target); @@ -5938,12 +5938,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - CASE_FLT_FN (BUILT_IN_POW): - target = expand_builtin_pow (exp, target, subtarget); - if (target) - return target; - break; - CASE_FLT_FN (BUILT_IN_POWI): target = expand_builtin_powi (exp, target); if (target) @@ -5961,6 +5955,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, CASE_FLT_FN (BUILT_IN_FMOD): CASE_FLT_FN (BUILT_IN_REMAINDER): CASE_FLT_FN (BUILT_IN_DREM): + CASE_FLT_FN (BUILT_IN_POW): target = expand_builtin_mathfn_2 (exp, target, subtarget); if (target) return target; @@ -6074,9 +6069,10 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); case BUILT_IN_ALLOCA: + case BUILT_IN_ALLOCA_WITH_ALIGN: /* If the allocation stems from the declaration of a variable-sized object, it cannot accumulate. */ - target = expand_builtin_alloca (exp, ALLOCA_FOR_VAR_P (exp)); + target = expand_builtin_alloca (exp, CALL_ALLOCA_FOR_VAR_P (exp)); if (target) return target; break; @@ -6120,6 +6116,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; + CASE_INT_FN (BUILT_IN_CLRSB): + case BUILT_IN_CLRSBIMAX: + target = expand_builtin_unop (target_mode, exp, target, + subtarget, clrsb_optab); + if (target) + return target; + break; + CASE_INT_FN (BUILT_IN_POPCOUNT): case BUILT_IN_POPCOUNTIMAX: target = expand_builtin_unop (target_mode, exp, target, @@ -6355,17 +6359,16 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return expand_builtin_va_copy (exp); case BUILT_IN_EXPECT: return expand_builtin_expect (exp, target); + case BUILT_IN_ASSUME_ALIGNED: + return expand_builtin_assume_aligned (exp, target); case BUILT_IN_PREFETCH: expand_builtin_prefetch (exp); return const0_rtx; - case BUILT_IN_PROFILE_FUNC_ENTER: - return expand_builtin_profile_func (false); - case BUILT_IN_PROFILE_FUNC_EXIT: - return expand_builtin_profile_func (true); - case BUILT_IN_INIT_TRAMPOLINE: - return expand_builtin_init_trampoline (exp); + return expand_builtin_init_trampoline (exp, true); + case BUILT_IN_INIT_HEAP_TRAMPOLINE: + return expand_builtin_init_trampoline (exp, false); case BUILT_IN_ADJUST_TRAMPOLINE: return expand_builtin_adjust_trampoline (exp); @@ -6381,199 +6384,441 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, return target; break; - case BUILT_IN_FETCH_AND_ADD_1: - case BUILT_IN_FETCH_AND_ADD_2: - case BUILT_IN_FETCH_AND_ADD_4: - case BUILT_IN_FETCH_AND_ADD_8: - case BUILT_IN_FETCH_AND_ADD_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_ADD_1); - target = expand_builtin_sync_operation (mode, exp, PLUS, - false, target, ignore); + case BUILT_IN_SYNC_FETCH_AND_ADD_1: + case BUILT_IN_SYNC_FETCH_AND_ADD_2: + case BUILT_IN_SYNC_FETCH_AND_ADD_4: + case BUILT_IN_SYNC_FETCH_AND_ADD_8: + case BUILT_IN_SYNC_FETCH_AND_ADD_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_ADD_1); + target = expand_builtin_sync_operation (mode, exp, PLUS, false, target); if (target) return target; break; - case BUILT_IN_FETCH_AND_SUB_1: - case BUILT_IN_FETCH_AND_SUB_2: - case BUILT_IN_FETCH_AND_SUB_4: - case BUILT_IN_FETCH_AND_SUB_8: - case BUILT_IN_FETCH_AND_SUB_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_SUB_1); - target = expand_builtin_sync_operation (mode, exp, MINUS, - false, target, ignore); + case BUILT_IN_SYNC_FETCH_AND_SUB_1: + case BUILT_IN_SYNC_FETCH_AND_SUB_2: + case BUILT_IN_SYNC_FETCH_AND_SUB_4: + case BUILT_IN_SYNC_FETCH_AND_SUB_8: + case BUILT_IN_SYNC_FETCH_AND_SUB_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_SUB_1); + target = expand_builtin_sync_operation (mode, exp, MINUS, false, target); if (target) return target; break; - case BUILT_IN_FETCH_AND_OR_1: - case BUILT_IN_FETCH_AND_OR_2: - case BUILT_IN_FETCH_AND_OR_4: - case BUILT_IN_FETCH_AND_OR_8: - case BUILT_IN_FETCH_AND_OR_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_OR_1); - target = expand_builtin_sync_operation (mode, exp, IOR, - false, target, ignore); + case BUILT_IN_SYNC_FETCH_AND_OR_1: + case BUILT_IN_SYNC_FETCH_AND_OR_2: + case BUILT_IN_SYNC_FETCH_AND_OR_4: + case BUILT_IN_SYNC_FETCH_AND_OR_8: + case BUILT_IN_SYNC_FETCH_AND_OR_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_OR_1); + target = expand_builtin_sync_operation (mode, exp, IOR, false, target); if (target) return target; break; - case BUILT_IN_FETCH_AND_AND_1: - case BUILT_IN_FETCH_AND_AND_2: - case BUILT_IN_FETCH_AND_AND_4: - case BUILT_IN_FETCH_AND_AND_8: - case BUILT_IN_FETCH_AND_AND_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_AND_1); - target = expand_builtin_sync_operation (mode, exp, AND, - false, target, ignore); + case BUILT_IN_SYNC_FETCH_AND_AND_1: + case BUILT_IN_SYNC_FETCH_AND_AND_2: + case BUILT_IN_SYNC_FETCH_AND_AND_4: + case BUILT_IN_SYNC_FETCH_AND_AND_8: + case BUILT_IN_SYNC_FETCH_AND_AND_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_AND_1); + target = expand_builtin_sync_operation (mode, exp, AND, false, target); if (target) return target; break; - case BUILT_IN_FETCH_AND_XOR_1: - case BUILT_IN_FETCH_AND_XOR_2: - case BUILT_IN_FETCH_AND_XOR_4: - case BUILT_IN_FETCH_AND_XOR_8: - case BUILT_IN_FETCH_AND_XOR_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_XOR_1); - target = expand_builtin_sync_operation (mode, exp, XOR, - false, target, ignore); + case BUILT_IN_SYNC_FETCH_AND_XOR_1: + case BUILT_IN_SYNC_FETCH_AND_XOR_2: + case BUILT_IN_SYNC_FETCH_AND_XOR_4: + case BUILT_IN_SYNC_FETCH_AND_XOR_8: + case BUILT_IN_SYNC_FETCH_AND_XOR_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_XOR_1); + target = expand_builtin_sync_operation (mode, exp, XOR, false, target); if (target) return target; break; - case BUILT_IN_FETCH_AND_NAND_1: - case BUILT_IN_FETCH_AND_NAND_2: - case BUILT_IN_FETCH_AND_NAND_4: - case BUILT_IN_FETCH_AND_NAND_8: - case BUILT_IN_FETCH_AND_NAND_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_NAND_1); - target = expand_builtin_sync_operation (mode, exp, NOT, - false, target, ignore); + case BUILT_IN_SYNC_FETCH_AND_NAND_1: + case BUILT_IN_SYNC_FETCH_AND_NAND_2: + case BUILT_IN_SYNC_FETCH_AND_NAND_4: + case BUILT_IN_SYNC_FETCH_AND_NAND_8: + case BUILT_IN_SYNC_FETCH_AND_NAND_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_NAND_1); + target = expand_builtin_sync_operation (mode, exp, NOT, false, target); if (target) return target; break; - case BUILT_IN_ADD_AND_FETCH_1: - case BUILT_IN_ADD_AND_FETCH_2: - case BUILT_IN_ADD_AND_FETCH_4: - case BUILT_IN_ADD_AND_FETCH_8: - case BUILT_IN_ADD_AND_FETCH_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_ADD_AND_FETCH_1); - target = expand_builtin_sync_operation (mode, exp, PLUS, - true, target, ignore); + case BUILT_IN_SYNC_ADD_AND_FETCH_1: + case BUILT_IN_SYNC_ADD_AND_FETCH_2: + case BUILT_IN_SYNC_ADD_AND_FETCH_4: + case BUILT_IN_SYNC_ADD_AND_FETCH_8: + case BUILT_IN_SYNC_ADD_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_ADD_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, exp, PLUS, true, target); if (target) return target; break; - case BUILT_IN_SUB_AND_FETCH_1: - case BUILT_IN_SUB_AND_FETCH_2: - case BUILT_IN_SUB_AND_FETCH_4: - case BUILT_IN_SUB_AND_FETCH_8: - case BUILT_IN_SUB_AND_FETCH_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_SUB_AND_FETCH_1); - target = expand_builtin_sync_operation (mode, exp, MINUS, - true, target, ignore); + case BUILT_IN_SYNC_SUB_AND_FETCH_1: + case BUILT_IN_SYNC_SUB_AND_FETCH_2: + case BUILT_IN_SYNC_SUB_AND_FETCH_4: + case BUILT_IN_SYNC_SUB_AND_FETCH_8: + case BUILT_IN_SYNC_SUB_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_SUB_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, exp, MINUS, true, target); if (target) return target; break; - case BUILT_IN_OR_AND_FETCH_1: - case BUILT_IN_OR_AND_FETCH_2: - case BUILT_IN_OR_AND_FETCH_4: - case BUILT_IN_OR_AND_FETCH_8: - case BUILT_IN_OR_AND_FETCH_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_OR_AND_FETCH_1); - target = expand_builtin_sync_operation (mode, exp, IOR, - true, target, ignore); + case BUILT_IN_SYNC_OR_AND_FETCH_1: + case BUILT_IN_SYNC_OR_AND_FETCH_2: + case BUILT_IN_SYNC_OR_AND_FETCH_4: + case BUILT_IN_SYNC_OR_AND_FETCH_8: + case BUILT_IN_SYNC_OR_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_OR_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, exp, IOR, true, target); if (target) return target; break; - case BUILT_IN_AND_AND_FETCH_1: - case BUILT_IN_AND_AND_FETCH_2: - case BUILT_IN_AND_AND_FETCH_4: - case BUILT_IN_AND_AND_FETCH_8: - case BUILT_IN_AND_AND_FETCH_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_AND_AND_FETCH_1); - target = expand_builtin_sync_operation (mode, exp, AND, - true, target, ignore); + case BUILT_IN_SYNC_AND_AND_FETCH_1: + case BUILT_IN_SYNC_AND_AND_FETCH_2: + case BUILT_IN_SYNC_AND_AND_FETCH_4: + case BUILT_IN_SYNC_AND_AND_FETCH_8: + case BUILT_IN_SYNC_AND_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_AND_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, exp, AND, true, target); if (target) return target; break; - case BUILT_IN_XOR_AND_FETCH_1: - case BUILT_IN_XOR_AND_FETCH_2: - case BUILT_IN_XOR_AND_FETCH_4: - case BUILT_IN_XOR_AND_FETCH_8: - case BUILT_IN_XOR_AND_FETCH_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_XOR_AND_FETCH_1); - target = expand_builtin_sync_operation (mode, exp, XOR, - true, target, ignore); + case BUILT_IN_SYNC_XOR_AND_FETCH_1: + case BUILT_IN_SYNC_XOR_AND_FETCH_2: + case BUILT_IN_SYNC_XOR_AND_FETCH_4: + case BUILT_IN_SYNC_XOR_AND_FETCH_8: + case BUILT_IN_SYNC_XOR_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_XOR_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, exp, XOR, true, target); if (target) return target; break; - case BUILT_IN_NAND_AND_FETCH_1: - case BUILT_IN_NAND_AND_FETCH_2: - case BUILT_IN_NAND_AND_FETCH_4: - case BUILT_IN_NAND_AND_FETCH_8: - case BUILT_IN_NAND_AND_FETCH_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_NAND_AND_FETCH_1); - target = expand_builtin_sync_operation (mode, exp, NOT, - true, target, ignore); + case BUILT_IN_SYNC_NAND_AND_FETCH_1: + case BUILT_IN_SYNC_NAND_AND_FETCH_2: + case BUILT_IN_SYNC_NAND_AND_FETCH_4: + case BUILT_IN_SYNC_NAND_AND_FETCH_8: + case BUILT_IN_SYNC_NAND_AND_FETCH_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_NAND_AND_FETCH_1); + target = expand_builtin_sync_operation (mode, exp, NOT, true, target); if (target) return target; break; - case BUILT_IN_BOOL_COMPARE_AND_SWAP_1: - case BUILT_IN_BOOL_COMPARE_AND_SWAP_2: - case BUILT_IN_BOOL_COMPARE_AND_SWAP_4: - case BUILT_IN_BOOL_COMPARE_AND_SWAP_8: - case BUILT_IN_BOOL_COMPARE_AND_SWAP_16: + case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_1: + case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_2: + case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_4: + case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_8: + case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_16: if (mode == VOIDmode) mode = TYPE_MODE (boolean_type_node); if (!target || !register_operand (target, mode)) target = gen_reg_rtx (mode); - mode = get_builtin_sync_mode (fcode - BUILT_IN_BOOL_COMPARE_AND_SWAP_1); + mode = get_builtin_sync_mode + (fcode - BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_1); target = expand_builtin_compare_and_swap (mode, exp, true, target); if (target) return target; break; - case BUILT_IN_VAL_COMPARE_AND_SWAP_1: - case BUILT_IN_VAL_COMPARE_AND_SWAP_2: - case BUILT_IN_VAL_COMPARE_AND_SWAP_4: - case BUILT_IN_VAL_COMPARE_AND_SWAP_8: - case BUILT_IN_VAL_COMPARE_AND_SWAP_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_VAL_COMPARE_AND_SWAP_1); + case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1: + case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_2: + case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_4: + case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_8: + case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_16: + mode = get_builtin_sync_mode + (fcode - BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1); target = expand_builtin_compare_and_swap (mode, exp, false, target); if (target) return target; break; - case BUILT_IN_LOCK_TEST_AND_SET_1: - case BUILT_IN_LOCK_TEST_AND_SET_2: - case BUILT_IN_LOCK_TEST_AND_SET_4: - case BUILT_IN_LOCK_TEST_AND_SET_8: - case BUILT_IN_LOCK_TEST_AND_SET_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_TEST_AND_SET_1); - target = expand_builtin_lock_test_and_set (mode, exp, target); + case BUILT_IN_SYNC_LOCK_TEST_AND_SET_1: + case BUILT_IN_SYNC_LOCK_TEST_AND_SET_2: + case BUILT_IN_SYNC_LOCK_TEST_AND_SET_4: + case BUILT_IN_SYNC_LOCK_TEST_AND_SET_8: + case BUILT_IN_SYNC_LOCK_TEST_AND_SET_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_LOCK_TEST_AND_SET_1); + target = expand_builtin_sync_lock_test_and_set (mode, exp, target); + if (target) + return target; + break; + + case BUILT_IN_SYNC_LOCK_RELEASE_1: + case BUILT_IN_SYNC_LOCK_RELEASE_2: + case BUILT_IN_SYNC_LOCK_RELEASE_4: + case BUILT_IN_SYNC_LOCK_RELEASE_8: + case BUILT_IN_SYNC_LOCK_RELEASE_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_LOCK_RELEASE_1); + expand_builtin_sync_lock_release (mode, exp); + return const0_rtx; + + case BUILT_IN_SYNC_SYNCHRONIZE: + expand_builtin_sync_synchronize (); + return const0_rtx; + + case BUILT_IN_ATOMIC_EXCHANGE_1: + case BUILT_IN_ATOMIC_EXCHANGE_2: + case BUILT_IN_ATOMIC_EXCHANGE_4: + case BUILT_IN_ATOMIC_EXCHANGE_8: + case BUILT_IN_ATOMIC_EXCHANGE_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_EXCHANGE_1); + target = expand_builtin_atomic_exchange (mode, exp, target); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1: + case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_2: + case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4: + case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8: + case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_16: + { + unsigned int nargs, z; + VEC(tree,gc) *vec; + + mode = + get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1); + target = expand_builtin_atomic_compare_exchange (mode, exp, target); + if (target) + return target; + + /* If this is turned into an external library call, the weak parameter + must be dropped to match the expected parameter list. */ + nargs = call_expr_nargs (exp); + vec = VEC_alloc (tree, gc, nargs - 1); + for (z = 0; z < 3; z++) + VEC_quick_push (tree, vec, CALL_EXPR_ARG (exp, z)); + /* Skip the boolean weak parameter. */ + for (z = 4; z < 6; z++) + VEC_quick_push (tree, vec, CALL_EXPR_ARG (exp, z)); + exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), vec); + break; + } + + case BUILT_IN_ATOMIC_LOAD_1: + case BUILT_IN_ATOMIC_LOAD_2: + case BUILT_IN_ATOMIC_LOAD_4: + case BUILT_IN_ATOMIC_LOAD_8: + case BUILT_IN_ATOMIC_LOAD_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_LOAD_1); + target = expand_builtin_atomic_load (mode, exp, target); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_STORE_1: + case BUILT_IN_ATOMIC_STORE_2: + case BUILT_IN_ATOMIC_STORE_4: + case BUILT_IN_ATOMIC_STORE_8: + case BUILT_IN_ATOMIC_STORE_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_STORE_1); + target = expand_builtin_atomic_store (mode, exp); + if (target) + return const0_rtx; + break; + + case BUILT_IN_ATOMIC_ADD_FETCH_1: + case BUILT_IN_ATOMIC_ADD_FETCH_2: + case BUILT_IN_ATOMIC_ADD_FETCH_4: + case BUILT_IN_ATOMIC_ADD_FETCH_8: + case BUILT_IN_ATOMIC_ADD_FETCH_16: + { + enum built_in_function lib; + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1); + lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_ADD_1 + + (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1)); + target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, true, + ignore, lib); + if (target) + return target; + break; + } + case BUILT_IN_ATOMIC_SUB_FETCH_1: + case BUILT_IN_ATOMIC_SUB_FETCH_2: + case BUILT_IN_ATOMIC_SUB_FETCH_4: + case BUILT_IN_ATOMIC_SUB_FETCH_8: + case BUILT_IN_ATOMIC_SUB_FETCH_16: + { + enum built_in_function lib; + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1); + lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_SUB_1 + + (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1)); + target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, true, + ignore, lib); + if (target) + return target; + break; + } + case BUILT_IN_ATOMIC_AND_FETCH_1: + case BUILT_IN_ATOMIC_AND_FETCH_2: + case BUILT_IN_ATOMIC_AND_FETCH_4: + case BUILT_IN_ATOMIC_AND_FETCH_8: + case BUILT_IN_ATOMIC_AND_FETCH_16: + { + enum built_in_function lib; + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_AND_FETCH_1); + lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_AND_1 + + (fcode - BUILT_IN_ATOMIC_AND_FETCH_1)); + target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, true, + ignore, lib); + if (target) + return target; + break; + } + case BUILT_IN_ATOMIC_NAND_FETCH_1: + case BUILT_IN_ATOMIC_NAND_FETCH_2: + case BUILT_IN_ATOMIC_NAND_FETCH_4: + case BUILT_IN_ATOMIC_NAND_FETCH_8: + case BUILT_IN_ATOMIC_NAND_FETCH_16: + { + enum built_in_function lib; + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1); + lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_NAND_1 + + (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1)); + target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, true, + ignore, lib); + if (target) + return target; + break; + } + case BUILT_IN_ATOMIC_XOR_FETCH_1: + case BUILT_IN_ATOMIC_XOR_FETCH_2: + case BUILT_IN_ATOMIC_XOR_FETCH_4: + case BUILT_IN_ATOMIC_XOR_FETCH_8: + case BUILT_IN_ATOMIC_XOR_FETCH_16: + { + enum built_in_function lib; + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1); + lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_XOR_1 + + (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1)); + target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, true, + ignore, lib); + if (target) + return target; + break; + } + case BUILT_IN_ATOMIC_OR_FETCH_1: + case BUILT_IN_ATOMIC_OR_FETCH_2: + case BUILT_IN_ATOMIC_OR_FETCH_4: + case BUILT_IN_ATOMIC_OR_FETCH_8: + case BUILT_IN_ATOMIC_OR_FETCH_16: + { + enum built_in_function lib; + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_OR_FETCH_1); + lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_OR_1 + + (fcode - BUILT_IN_ATOMIC_OR_FETCH_1)); + target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, true, + ignore, lib); + if (target) + return target; + break; + } + case BUILT_IN_ATOMIC_FETCH_ADD_1: + case BUILT_IN_ATOMIC_FETCH_ADD_2: + case BUILT_IN_ATOMIC_FETCH_ADD_4: + case BUILT_IN_ATOMIC_FETCH_ADD_8: + case BUILT_IN_ATOMIC_FETCH_ADD_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_ADD_1); + target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, false, + ignore, BUILT_IN_NONE); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_FETCH_SUB_1: + case BUILT_IN_ATOMIC_FETCH_SUB_2: + case BUILT_IN_ATOMIC_FETCH_SUB_4: + case BUILT_IN_ATOMIC_FETCH_SUB_8: + case BUILT_IN_ATOMIC_FETCH_SUB_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_SUB_1); + target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, false, + ignore, BUILT_IN_NONE); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_FETCH_AND_1: + case BUILT_IN_ATOMIC_FETCH_AND_2: + case BUILT_IN_ATOMIC_FETCH_AND_4: + case BUILT_IN_ATOMIC_FETCH_AND_8: + case BUILT_IN_ATOMIC_FETCH_AND_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_AND_1); + target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, false, + ignore, BUILT_IN_NONE); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_FETCH_NAND_1: + case BUILT_IN_ATOMIC_FETCH_NAND_2: + case BUILT_IN_ATOMIC_FETCH_NAND_4: + case BUILT_IN_ATOMIC_FETCH_NAND_8: + case BUILT_IN_ATOMIC_FETCH_NAND_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_NAND_1); + target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, false, + ignore, BUILT_IN_NONE); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_FETCH_XOR_1: + case BUILT_IN_ATOMIC_FETCH_XOR_2: + case BUILT_IN_ATOMIC_FETCH_XOR_4: + case BUILT_IN_ATOMIC_FETCH_XOR_8: + case BUILT_IN_ATOMIC_FETCH_XOR_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_XOR_1); + target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, false, + ignore, BUILT_IN_NONE); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_FETCH_OR_1: + case BUILT_IN_ATOMIC_FETCH_OR_2: + case BUILT_IN_ATOMIC_FETCH_OR_4: + case BUILT_IN_ATOMIC_FETCH_OR_8: + case BUILT_IN_ATOMIC_FETCH_OR_16: + mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_OR_1); + target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, false, + ignore, BUILT_IN_NONE); if (target) return target; break; - case BUILT_IN_LOCK_RELEASE_1: - case BUILT_IN_LOCK_RELEASE_2: - case BUILT_IN_LOCK_RELEASE_4: - case BUILT_IN_LOCK_RELEASE_8: - case BUILT_IN_LOCK_RELEASE_16: - mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_RELEASE_1); - expand_builtin_lock_release (mode, exp); + case BUILT_IN_ATOMIC_TEST_AND_SET: + return expand_builtin_atomic_test_and_set (exp, target); + + case BUILT_IN_ATOMIC_CLEAR: + return expand_builtin_atomic_clear (exp); + + case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: + return expand_builtin_atomic_always_lock_free (exp); + + case BUILT_IN_ATOMIC_IS_LOCK_FREE: + target = expand_builtin_atomic_is_lock_free (exp); + if (target) + return target; + break; + + case BUILT_IN_ATOMIC_THREAD_FENCE: + expand_builtin_atomic_thread_fence (exp); return const0_rtx; - case BUILT_IN_SYNCHRONIZE: - expand_builtin_synchronize (); + case BUILT_IN_ATOMIC_SIGNAL_FENCE: + expand_builtin_atomic_signal_fence (exp); return const0_rtx; case BUILT_IN_OBJECT_SIZE: @@ -6591,6 +6836,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, case BUILT_IN_STRCPY_CHK: case BUILT_IN_STPCPY_CHK: case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STPNCPY_CHK: case BUILT_IN_STRCAT_CHK: case BUILT_IN_STRNCAT_CHK: case BUILT_IN_SNPRINTF_CHK: @@ -6604,7 +6850,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_FREE: - maybe_emit_free_warning (exp); + if (warn_free_nonheap_object) + maybe_emit_free_warning (exp); break; default: /* just do library call, if unknown builtin */ @@ -6739,7 +6986,7 @@ build_builtin_expect_predicate (location_t loc, tree pred, tree expected) { tree fn, arg_types, pred_type, expected_type, call_expr, ret_type; - fn = built_in_decls[BUILT_IN_EXPECT]; + fn = builtin_decl_explicit (BUILT_IN_EXPECT); arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn)); ret_type = TREE_TYPE (TREE_TYPE (fn)); pred_type = TREE_VALUE (arg_types); @@ -6759,13 +7006,22 @@ build_builtin_expect_predicate (location_t loc, tree pred, tree expected) static tree fold_builtin_expect (location_t loc, tree arg0, tree arg1) { - tree inner, fndecl; + tree inner, fndecl, inner_arg0; enum tree_code code; + /* Distribute the expected value over short-circuiting operators. + See through the cast from truthvalue_type_node to long. */ + inner_arg0 = arg0; + while (TREE_CODE (inner_arg0) == NOP_EXPR + && INTEGRAL_TYPE_P (TREE_TYPE (inner_arg0)) + && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (inner_arg0, 0)))) + inner_arg0 = TREE_OPERAND (inner_arg0, 0); + /* If this is a builtin_expect within a builtin_expect keep the inner one. See through a comparison against a constant. It might have been added to create a thruthvalue. */ - inner = arg0; + inner = inner_arg0; + if (COMPARISON_CLASS_P (inner) && TREE_CODE (TREE_OPERAND (inner, 1)) == INTEGER_CST) inner = TREE_OPERAND (inner, 0); @@ -6776,14 +7032,7 @@ fold_builtin_expect (location_t loc, tree arg0, tree arg1) && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT) return arg0; - /* Distribute the expected value over short-circuiting operators. - See through the cast from truthvalue_type_node to long. */ - inner = arg0; - while (TREE_CODE (inner) == NOP_EXPR - && INTEGRAL_TYPE_P (TREE_TYPE (inner)) - && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (inner, 0)))) - inner = TREE_OPERAND (inner, 0); - + inner = inner_arg0; code = TREE_CODE (inner); if (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR) { @@ -6798,13 +7047,13 @@ fold_builtin_expect (location_t loc, tree arg0, tree arg1) } /* If the argument isn't invariant then there's nothing else we can do. */ - if (!TREE_CONSTANT (arg0)) + if (!TREE_CONSTANT (inner_arg0)) return NULL_TREE; /* If we expect that a comparison against the argument will fold to a constant return the constant. In practice, this means a true constant or the address of a non-weak symbol. */ - inner = arg0; + inner = inner_arg0; STRIP_NOPS (inner); if (TREE_CODE (inner) == ADDR_EXPR) { @@ -6830,9 +7079,9 @@ static tree fold_builtin_classify_type (tree arg) { if (arg == 0) - return build_int_cst (NULL_TREE, no_type_class); + return build_int_cst (integer_type_node, no_type_class); - return build_int_cst (NULL_TREE, type_to_class (TREE_TYPE (arg))); + return build_int_cst (integer_type_node, type_to_class (TREE_TYPE (arg))); } /* Fold a call to __builtin_strlen with argument ARG. */ @@ -7036,6 +7285,42 @@ fold_fixed_mathfn (location_t loc, tree fndecl, tree arg) fold_convert_loc (loc, newtype, arg0)); } + /* Canonicalize iround (x) to lround (x) on ILP32 targets where + sizeof (int) == sizeof (long). */ + if (TYPE_PRECISION (integer_type_node) + == TYPE_PRECISION (long_integer_type_node)) + { + tree newfn = NULL_TREE; + switch (fcode) + { + CASE_FLT_FN (BUILT_IN_ICEIL): + newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL); + break; + + CASE_FLT_FN (BUILT_IN_IFLOOR): + newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR); + break; + + CASE_FLT_FN (BUILT_IN_IROUND): + newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND); + break; + + CASE_FLT_FN (BUILT_IN_IRINT): + newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT); + break; + + default: + break; + } + + if (newfn) + { + tree newcall = build_call_expr_loc (loc, newfn, 1, arg); + return fold_convert_loc (loc, + TREE_TYPE (TREE_TYPE (fndecl)), newcall); + } + } + /* Canonicalize llround (x) to lround (x) on LP64 targets where sizeof (long long) == sizeof (long). */ if (TYPE_PRECISION (long_long_integer_type_node) @@ -7735,16 +8020,19 @@ fold_builtin_int_roundingfn (location_t loc, tree fndecl, tree arg) switch (DECL_FUNCTION_CODE (fndecl)) { + CASE_FLT_FN (BUILT_IN_IFLOOR): CASE_FLT_FN (BUILT_IN_LFLOOR): CASE_FLT_FN (BUILT_IN_LLFLOOR): real_floor (&r, TYPE_MODE (ftype), &x); break; + CASE_FLT_FN (BUILT_IN_ICEIL): CASE_FLT_FN (BUILT_IN_LCEIL): CASE_FLT_FN (BUILT_IN_LLCEIL): real_ceil (&r, TYPE_MODE (ftype), &x); break; + CASE_FLT_FN (BUILT_IN_IROUND): CASE_FLT_FN (BUILT_IN_LROUND): CASE_FLT_FN (BUILT_IN_LLROUND): real_round (&r, TYPE_MODE (ftype), &x); @@ -7802,7 +8090,8 @@ fold_builtin_bitop (tree fndecl, tree arg) { hi = TREE_INT_CST_HIGH (arg); if (width < 2 * HOST_BITS_PER_WIDE_INT) - hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT)); + hi &= ~((unsigned HOST_WIDE_INT) (-1) + << (width - HOST_BITS_PER_WIDE_INT)); } else { @@ -7840,6 +8129,26 @@ fold_builtin_bitop (tree fndecl, tree arg) result = width; break; + CASE_INT_FN (BUILT_IN_CLRSB): + if (width > HOST_BITS_PER_WIDE_INT + && (hi & ((unsigned HOST_WIDE_INT) 1 + << (width - HOST_BITS_PER_WIDE_INT - 1))) != 0) + { + hi = ~hi & ~((unsigned HOST_WIDE_INT) (-1) + << (width - HOST_BITS_PER_WIDE_INT - 1)); + lo = ~lo; + } + else if (width <= HOST_BITS_PER_WIDE_INT + && (lo & ((unsigned HOST_WIDE_INT) 1 << (width - 1))) != 0) + lo = ~lo & ~((unsigned HOST_WIDE_INT) (-1) << (width - 1)); + if (hi != 0) + result = width - floor_log2 (hi) - 2 - HOST_BITS_PER_WIDE_INT; + else if (lo != 0) + result = width - floor_log2 (lo) - 2; + else + result = width - 1; + break; + CASE_INT_FN (BUILT_IN_POPCOUNT): result = 0; while (lo) @@ -8339,7 +8648,7 @@ fold_builtin_memset (location_t loc, tree dest, tree c, tree len, if (integer_zerop (len)) return omit_one_operand_loc (loc, type, dest, c); - if (! host_integerp (c, 1) || TREE_SIDE_EFFECTS (dest)) + if (TREE_CODE (c) != INTEGER_CST || TREE_SIDE_EFFECTS (dest)) return NULL_TREE; var = dest; @@ -8364,8 +8673,7 @@ fold_builtin_memset (location_t loc, tree dest, tree c, tree len, length = tree_low_cst (len, 1); if (GET_MODE_SIZE (TYPE_MODE (etype)) != length - || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT - < length) + || get_pointer_alignment (dest) / BITS_PER_UNIT < length) return NULL_TREE; if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT) @@ -8378,7 +8686,7 @@ fold_builtin_memset (location_t loc, tree dest, tree c, tree len, if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64) return NULL_TREE; - cval = tree_low_cst (c, 1); + cval = TREE_INT_CST_LOW (c); cval &= 0xff; cval |= cval << 8; cval |= cval << 16; @@ -8416,7 +8724,7 @@ fold_builtin_bzero (location_t loc, tree dest, tree size, bool ignore) calling bzero instead of memset. */ return fold_builtin_memset (loc, dest, integer_zero_node, - fold_convert_loc (loc, sizetype, size), + fold_convert_loc (loc, size_type_node, size), void_type_node, ignore); } @@ -8455,8 +8763,8 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, if (endp == 3) { - src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); - dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + src_align = get_pointer_alignment (src); + dest_align = get_pointer_alignment (dest); /* Both DEST and SRC must be pointer types. ??? This is what old code did. Is the testing for pointer types @@ -8470,7 +8778,7 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, && (MIN (src_align, dest_align) / BITS_PER_UNIT >= (unsigned HOST_WIDE_INT) tree_low_cst (len, 1)))) { - tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return NULL_TREE; return build_call_expr_loc (loc, fn, 3, dest, src, len); @@ -8529,7 +8837,7 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, else return NULL_TREE; - fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return NULL_TREE; return build_call_expr_loc (loc, fn, 3, dest, src, len); @@ -8548,7 +8856,7 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, if (!refs_may_alias_p_1 (&destr, &srcr, false)) { tree fn; - fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return NULL_TREE; return build_call_expr_loc (loc, fn, 3, dest, src, len); @@ -8566,6 +8874,9 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, Perhaps we ought to inherit type from non-VOID argument here? */ STRIP_NOPS (src); STRIP_NOPS (dest); + if (!POINTER_TYPE_P (TREE_TYPE (src)) + || !POINTER_TYPE_P (TREE_TYPE (dest))) + return NULL_TREE; /* As we fold (void *)(p + CST) to (void *)p + CST undo this here. */ if (TREE_CODE (src) == POINTER_PLUS_EXPR) { @@ -8582,8 +8893,7 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest); } srctype = TREE_TYPE (TREE_TYPE (src)); - if (srctype - && TREE_CODE (srctype) == ARRAY_TYPE + if (TREE_CODE (srctype) == ARRAY_TYPE && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len)) { srctype = TREE_TYPE (srctype); @@ -8591,25 +8901,19 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, src = build1 (NOP_EXPR, build_pointer_type (srctype), src); } desttype = TREE_TYPE (TREE_TYPE (dest)); - if (desttype - && TREE_CODE (desttype) == ARRAY_TYPE + if (TREE_CODE (desttype) == ARRAY_TYPE && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len)) { desttype = TREE_TYPE (desttype); STRIP_NOPS (dest); dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest); } - if (!srctype || !desttype - || TREE_ADDRESSABLE (srctype) - || TREE_ADDRESSABLE (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) + if (TREE_ADDRESSABLE (srctype) + || TREE_ADDRESSABLE (desttype)) return NULL_TREE; - src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT); - dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + src_align = get_pointer_alignment (src); + dest_align = get_pointer_alignment (dest); if (dest_align < TYPE_ALIGN (desttype) || src_align < TYPE_ALIGN (srctype)) return NULL_TREE; @@ -8700,8 +9004,7 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len, ssize_int (1)); - len = fold_convert_loc (loc, sizetype, len); - dest = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (dest), dest, len); + dest = fold_build_pointer_plus_loc (loc, dest, len); dest = fold_convert_loc (loc, type, dest); if (expr) dest = omit_one_operand_loc (loc, type, dest, expr); @@ -8728,7 +9031,7 @@ fold_builtin_strcpy (location_t loc, tree fndecl, tree dest, tree src, tree len) if (optimize_function_for_size_p (cfun)) return NULL_TREE; - fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return NULL_TREE; @@ -8739,7 +9042,8 @@ fold_builtin_strcpy (location_t loc, tree fndecl, tree dest, tree src, tree len) return NULL_TREE; } - len = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1)); + len = fold_convert_loc (loc, size_type_node, len); + len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1)); return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), build_call_expr_loc (loc, fn, 3, dest, src, len)); } @@ -8766,19 +9070,20 @@ fold_builtin_stpcpy (location_t loc, tree fndecl, tree dest, tree src) && !integer_zerop (len)) return NULL_TREE; - fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return NULL_TREE; - lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1)); + lenp1 = size_binop_loc (loc, PLUS_EXPR, + fold_convert_loc (loc, size_type_node, len), + build_int_cst (size_type_node, 1)); /* We use dest twice in building our expression. Save it from multiple expansions. */ dest = builtin_save_expr (dest); call = build_call_expr_loc (loc, fn, 3, dest, src, lenp1); type = TREE_TYPE (TREE_TYPE (fndecl)); - len = fold_convert_loc (loc, sizetype, len); - dest = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (dest), dest, len); + dest = fold_build_pointer_plus_loc (loc, dest, len); dest = fold_convert_loc (loc, type, dest); dest = omit_one_operand_loc (loc, type, dest, call); return dest; @@ -8824,9 +9129,11 @@ fold_builtin_strncpy (location_t loc, tree fndecl, tree dest, return NULL_TREE; /* OK transform into builtin memcpy. */ - fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return NULL_TREE; + + len = fold_convert_loc (loc, size_type_node, len); return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), build_call_expr_loc (loc, fn, 3, dest, src, len)); } @@ -8860,13 +9167,12 @@ fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type) if (target_char_cast (arg2, &c)) return NULL_TREE; - r = (char *) memchr (p1, c, tree_low_cst (len, 1)); + r = (const char *) memchr (p1, c, tree_low_cst (len, 1)); if (r == NULL) return build_int_cst (TREE_TYPE (arg1), 0); - tem = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (arg1), arg1, - size_int (r - p1)); + tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1); return fold_convert_loc (loc, type, tem); } return NULL_TREE; @@ -9104,8 +9410,6 @@ fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len) static tree fold_builtin_signbit (location_t loc, tree arg, tree type) { - tree temp; - if (!validate_arg (arg, REAL_TYPE)) return NULL_TREE; @@ -9116,8 +9420,9 @@ fold_builtin_signbit (location_t loc, tree arg, tree type) REAL_VALUE_TYPE c; c = TREE_REAL_CST (arg); - temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node; - return fold_convert_loc (loc, type, temp); + return (REAL_VALUE_NEGATIVE (c) + ? build_one_cst (type) + : build_zero_cst (type)); } /* If ARG is non-negative, the result is always zero. */ @@ -9126,8 +9431,9 @@ fold_builtin_signbit (location_t loc, tree arg, tree type) /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */ if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg)))) - return fold_build2_loc (loc, LT_EXPR, type, arg, - build_real (TREE_TYPE (arg), dconst0)); + return fold_convert (type, + fold_build2_loc (loc, LT_EXPR, boolean_type_node, arg, + build_real (TREE_TYPE (arg), dconst0))); return NULL_TREE; } @@ -9191,10 +9497,10 @@ fold_builtin_isascii (location_t loc, tree arg) { /* Transform isascii(c) -> ((c & ~0x7f) == 0). */ arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg, - build_int_cst (NULL_TREE, + build_int_cst (integer_type_node, ~ (unsigned HOST_WIDE_INT) 0x7f)); return fold_build2_loc (loc, EQ_EXPR, integer_type_node, - arg, integer_zero_node); + arg, integer_zero_node); } } @@ -9208,7 +9514,7 @@ fold_builtin_toascii (location_t loc, tree arg) /* Transform toascii(c) -> (c & 0x7f). */ return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg, - build_int_cst (NULL_TREE, 0x7f)); + build_int_cst (integer_type_node, 0x7f)); } /* Fold a call to builtin isdigit with argument ARG. */ @@ -9265,6 +9571,40 @@ fold_builtin_abs (location_t loc, tree arg, tree type) return fold_build1_loc (loc, ABS_EXPR, type, arg); } +/* Fold a fma operation with arguments ARG[012]. */ + +tree +fold_fma (location_t loc ATTRIBUTE_UNUSED, + tree type, tree arg0, tree arg1, tree arg2) +{ + if (TREE_CODE (arg0) == REAL_CST + && TREE_CODE (arg1) == REAL_CST + && TREE_CODE (arg2) == REAL_CST) + return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma); + + return NULL_TREE; +} + +/* Fold a call to fma, fmaf, or fmal with arguments ARG[012]. */ + +static tree +fold_builtin_fma (location_t loc, tree arg0, tree arg1, tree arg2, tree type) +{ + if (validate_arg (arg0, REAL_TYPE) + && validate_arg(arg1, REAL_TYPE) + && validate_arg(arg2, REAL_TYPE)) + { + tree tem = fold_fma (loc, type, arg0, arg1, arg2); + if (tem) + return tem; + + /* ??? Only expand to FMA_EXPR if it's directly supported. */ + if (optab_handler (fma_optab, TYPE_MODE (type)) != CODE_FOR_nothing) + return fold_build3_loc (loc, FMA_EXPR, type, arg0, arg1, arg2); + } + return NULL_TREE; +} + /* Fold a call to builtin fmin or fmax. */ static tree @@ -9352,7 +9692,16 @@ fold_builtin_logb (location_t loc, tree arg, tree rettype) case rvc_inf: /* If arg is Inf or NaN and we're logb, return it. */ if (TREE_CODE (rettype) == REAL_TYPE) - return fold_convert_loc (loc, rettype, arg); + { + /* For logb(-Inf) we have to return +Inf. */ + if (real_isinf (value) && real_isneg (value)) + { + REAL_VALUE_TYPE tem; + real_inf (&tem); + return build_real (rettype, tem); + } + return fold_convert_loc (loc, rettype, arg); + } /* Fall through... */ case rvc_zero: /* Zero may set errno and/or raise an exception for logb, also @@ -9365,7 +9714,7 @@ fold_builtin_logb (location_t loc, tree arg, tree rettype) exponent and subtract 1. */ if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2) return fold_convert_loc (loc, rettype, - build_int_cst (NULL_TREE, + build_int_cst (integer_type_node, REAL_EXP (value)-1)); break; } @@ -9453,7 +9802,7 @@ fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype) REAL_VALUE_TYPE frac_rvt = *value; SET_REAL_EXP (&frac_rvt, 0); frac = build_real (rettype, frac_rvt); - exp = build_int_cst (NULL_TREE, REAL_EXP (value)); + exp = build_int_cst (integer_type_node, REAL_EXP (value)); } break; default: @@ -9612,7 +9961,7 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg) CASE_FLT_FN (BUILT_IN_ISINF): { /* isinf(x) -> isgreater(fabs(x),DBL_MAX). */ - tree const isgr_fn = built_in_decls[BUILT_IN_ISGREATER]; + tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER); tree const type = TREE_TYPE (arg); REAL_VALUE_TYPE r; char buf[128]; @@ -9628,7 +9977,7 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg) case BUILT_IN_ISFINITE: { /* isfinite(x) -> islessequal(fabs(x),DBL_MAX). */ - tree const isle_fn = built_in_decls[BUILT_IN_ISLESSEQUAL]; + tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL); tree const type = TREE_TYPE (arg); REAL_VALUE_TYPE r; char buf[128]; @@ -9651,8 +10000,8 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg) { /* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) & islessequal(fabs(x),DBL_MAX). */ - tree const isle_fn = built_in_decls[BUILT_IN_ISLESSEQUAL]; - tree const isge_fn = built_in_decls[BUILT_IN_ISGREATEREQUAL]; + tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL); + tree const isge_fn = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL); tree const type = TREE_TYPE (arg); REAL_VALUE_TYPE rmax, rmin; char buf[128]; @@ -9713,7 +10062,7 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) 1. So e.g. "if (isinf_sign(x))" would be folded to just "if (isinf(x) ? 1 : 0)" which becomes "if (isinf(x))". */ tree signbit_fn = mathfn_built_in_1 (TREE_TYPE (arg), BUILT_IN_SIGNBIT, 0); - tree isinf_fn = built_in_decls[BUILT_IN_ISINF]; + tree isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF); tree tmp = NULL_TREE; arg = builtin_save_expr (arg); @@ -10226,14 +10575,18 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore) CASE_FLT_FN (BUILT_IN_RINT): return fold_trunc_transparent_mathfn (loc, fndecl, arg0); + CASE_FLT_FN (BUILT_IN_ICEIL): CASE_FLT_FN (BUILT_IN_LCEIL): CASE_FLT_FN (BUILT_IN_LLCEIL): CASE_FLT_FN (BUILT_IN_LFLOOR): + CASE_FLT_FN (BUILT_IN_IFLOOR): CASE_FLT_FN (BUILT_IN_LLFLOOR): + CASE_FLT_FN (BUILT_IN_IROUND): CASE_FLT_FN (BUILT_IN_LROUND): CASE_FLT_FN (BUILT_IN_LLROUND): return fold_builtin_int_roundingfn (loc, fndecl, arg0); + CASE_FLT_FN (BUILT_IN_IRINT): CASE_FLT_FN (BUILT_IN_LRINT): CASE_FLT_FN (BUILT_IN_LLRINT): return fold_fixed_mathfn (loc, fndecl, arg0); @@ -10245,6 +10598,7 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool ignore) CASE_INT_FN (BUILT_IN_FFS): CASE_INT_FN (BUILT_IN_CLZ): CASE_INT_FN (BUILT_IN_CTZ): + CASE_INT_FN (BUILT_IN_CLRSB): CASE_INT_FN (BUILT_IN_POPCOUNT): CASE_INT_FN (BUILT_IN_PARITY): return fold_builtin_bitop (fndecl, arg0); @@ -10431,7 +10785,7 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) case BUILT_IN_STPCPY: if (ignore) { - tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); if (!fn) break; @@ -10516,6 +10870,12 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) return fold_builtin_fprintf (loc, fndecl, arg0, arg1, NULL_TREE, ignore, fcode); + case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: + return fold_builtin_atomic_always_lock_free (arg0, arg1); + + case BUILT_IN_ATOMIC_IS_LOCK_FREE: + return fold_builtin_atomic_is_lock_free (arg0, arg1); + default: break; } @@ -10539,10 +10899,7 @@ fold_builtin_3 (location_t loc, tree fndecl, return fold_builtin_sincos (loc, arg0, arg1, arg2); CASE_FLT_FN (BUILT_IN_FMA): - if (validate_arg (arg0, REAL_TYPE) - && validate_arg(arg1, REAL_TYPE) - && validate_arg(arg2, REAL_TYPE)) - return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma); + return fold_builtin_fma (loc, arg0, arg1, arg2, type); break; CASE_FLT_FN (BUILT_IN_REMQUO): @@ -10590,6 +10947,9 @@ fold_builtin_3 (location_t loc, tree fndecl, case BUILT_IN_SPRINTF: return fold_builtin_sprintf (loc, arg0, arg1, arg2, ignore); + case BUILT_IN_SNPRINTF: + return fold_builtin_snprintf (loc, arg0, arg1, arg2, NULL_TREE, ignore); + case BUILT_IN_STRCPY_CHK: case BUILT_IN_STPCPY_CHK: return fold_builtin_stxcpy_chk (loc, fndecl, arg0, arg1, arg2, NULL_TREE, @@ -10650,11 +11010,16 @@ fold_builtin_4 (location_t loc, tree fndecl, DECL_FUNCTION_CODE (fndecl)); case BUILT_IN_STRNCPY_CHK: - return fold_builtin_strncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE); + case BUILT_IN_STPNCPY_CHK: + return fold_builtin_stxncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE, + ignore, fcode); case BUILT_IN_STRNCAT_CHK: return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3); + case BUILT_IN_SNPRINTF: + return fold_builtin_snprintf (loc, arg0, arg1, arg2, arg3, ignore); + case BUILT_IN_FPRINTF_CHK: case BUILT_IN_VFPRINTF_CHK: if (!validate_arg (arg1, INTEGER_TYPE) @@ -10766,7 +11131,7 @@ fold_builtin_varargs (location_t loc, tree fndecl, tree exp, been inlined, otherwise e.g. -D_FORTIFY_SOURCE checking might not be performed. */ -static bool +bool avoid_folding_inline_builtin (tree fndecl) { return (DECL_DECLARED_INLINE_P (fndecl) @@ -11216,8 +11581,7 @@ fold_builtin_strstr (location_t loc, tree s1, tree s2, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (s1), - s1, size_int (r - p1)); + tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); return fold_convert_loc (loc, type, tem); } @@ -11229,13 +11593,14 @@ fold_builtin_strstr (location_t loc, tree s1, tree s2, tree type) if (p2[1] != '\0') return NULL_TREE; - fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + fn = builtin_decl_implicit (BUILT_IN_STRCHR); if (!fn) return NULL_TREE; /* New argument list transforming strstr(s1, s2) to strchr(s1, s2[0]). */ - return build_call_expr_loc (loc, fn, 2, s1, build_int_cst (NULL_TREE, p2[0])); + return build_call_expr_loc (loc, fn, 2, s1, + build_int_cst (integer_type_node, p2[0])); } } @@ -11286,8 +11651,7 @@ fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (s1), - s1, size_int (r - p1)); + tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); return fold_convert_loc (loc, type, tem); } return NULL_TREE; @@ -11342,15 +11706,14 @@ fold_builtin_strrchr (location_t loc, tree s1, tree s2, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (s1), - s1, size_int (r - p1)); + tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); return fold_convert_loc (loc, type, tem); } if (! integer_zerop (s2)) return NULL_TREE; - fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + fn = builtin_decl_implicit (BUILT_IN_STRCHR); if (!fn) return NULL_TREE; @@ -11402,8 +11765,7 @@ fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type) return build_int_cst (TREE_TYPE (s1), 0); /* Return an offset into the constant string argument. */ - tem = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (s1), - s1, size_int (r - p1)); + tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); return fold_convert_loc (loc, type, tem); } @@ -11415,13 +11777,14 @@ fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type) if (p2[1] != '\0') return NULL_TREE; /* Really call strpbrk. */ - fn = implicit_built_in_decls[BUILT_IN_STRCHR]; + fn = builtin_decl_implicit (BUILT_IN_STRCHR); if (!fn) return NULL_TREE; /* New argument list transforming strpbrk(s1, s2) to strchr(s1, s2[0]). */ - return build_call_expr_loc (loc, fn, 2, s1, build_int_cst (NULL_TREE, p2[0])); + return build_call_expr_loc (loc, fn, 2, s1, + build_int_cst (integer_type_node, p2[0])); } } @@ -11461,8 +11824,8 @@ fold_builtin_strcat (location_t loc ATTRIBUTE_UNUSED, tree dst, tree src) { /* See if we can store by pieces into (dst + strlen(dst)). */ tree newdst, call; - tree strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN]; - tree strcpy_fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN); + tree strcpy_fn = builtin_decl_implicit (BUILT_IN_STRCPY); if (!strlen_fn || !strcpy_fn) return NULL_TREE; @@ -11486,8 +11849,7 @@ fold_builtin_strcat (location_t loc ATTRIBUTE_UNUSED, tree dst, tree src) newdst = build_call_expr_loc (loc, strlen_fn, 1, dst); /* Create (dst p+ strlen (dst)). */ - newdst = fold_build2_loc (loc, POINTER_PLUS_EXPR, - TREE_TYPE (dst), dst, newdst); + newdst = fold_build_pointer_plus_loc (loc, dst, newdst); newdst = builtin_save_expr (newdst); call = build_call_expr_loc (loc, strcpy_fn, 2, newdst, src); @@ -11536,7 +11898,7 @@ fold_builtin_strncat (location_t loc, tree dst, tree src, tree len) if (TREE_CODE (len) == INTEGER_CST && p && compare_tree_int (len, strlen (p)) >= 0) { - tree fn = implicit_built_in_decls[BUILT_IN_STRCAT]; + tree fn = builtin_decl_implicit (BUILT_IN_STRCAT); /* If the replacement _DECL isn't initialized, don't do the transformation. */ @@ -11581,7 +11943,7 @@ fold_builtin_strspn (location_t loc, tree s1, tree s2) if (p1 && p2) { const size_t r = strspn (p1, p2); - return size_int (r); + return build_int_cst (size_type_node, r); } /* If either argument is "", return NULL_TREE. */ @@ -11626,7 +11988,7 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2) if (p1 && p2) { const size_t r = strcspn (p1, p2); - return size_int (r); + return build_int_cst (size_type_node, r); } /* If the first argument is "", return NULL_TREE. */ @@ -11641,7 +12003,7 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2) /* If the second argument is "", return __builtin_strlen(s1). */ if (p2 && *p2 == '\0') { - tree fn = implicit_built_in_decls[BUILT_IN_STRLEN]; + tree fn = builtin_decl_implicit (BUILT_IN_STRLEN); /* If the replacement _DECL isn't initialized, don't do the transformation. */ @@ -11667,10 +12029,12 @@ fold_builtin_fputs (location_t loc, tree arg0, tree arg1, { /* If we're using an unlocked function, assume the other unlocked functions exist explicitly. */ - tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_FPUTC]; - tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED] - : implicit_built_in_decls[BUILT_IN_FWRITE]; + tree const fn_fputc = (unlocked + ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED) + : builtin_decl_implicit (BUILT_IN_FPUTC)); + tree const fn_fwrite = (unlocked + ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED) + : builtin_decl_implicit (BUILT_IN_FWRITE)); /* If the return value is used, don't do the transformation. */ if (!ignore) @@ -11704,7 +12068,8 @@ fold_builtin_fputs (location_t loc, tree arg0, tree arg1, { if (fn_fputc) return build_call_expr_loc (loc, fn_fputc, 2, - build_int_cst (NULL_TREE, p[0]), arg1); + build_int_cst + (integer_type_node, p[0]), arg1); else return NULL_TREE; } @@ -11863,7 +12228,7 @@ fold_builtin_sprintf (location_t loc, tree dest, tree fmt, /* If the format doesn't contain % args or %%, use strcpy. */ if (strchr (fmt_str, target_percent) == NULL) { - tree fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); if (!fn) return NULL_TREE; @@ -11876,14 +12241,14 @@ fold_builtin_sprintf (location_t loc, tree dest, tree fmt, 'format' is known to contain no % formats. */ call = build_call_expr_loc (loc, fn, 2, dest, fmt); if (!ignored) - retval = build_int_cst (NULL_TREE, strlen (fmt_str)); + retval = build_int_cst (integer_type_node, strlen (fmt_str)); } /* If the format is "%s", use strcpy if the result isn't used. */ else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0) { tree fn; - fn = implicit_built_in_decls[BUILT_IN_STRCPY]; + fn = builtin_decl_implicit (BUILT_IN_STRCPY); if (!fn) return NULL_TREE; @@ -11905,7 +12270,7 @@ fold_builtin_sprintf (location_t loc, tree dest, tree fmt, if (call && retval) { retval = fold_convert_loc - (loc, TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SPRINTF])), + (loc, TREE_TYPE (TREE_TYPE (builtin_decl_implicit (BUILT_IN_SPRINTF))), retval); return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); } @@ -11913,6 +12278,126 @@ fold_builtin_sprintf (location_t loc, tree dest, tree fmt, return call; } +/* Simplify a call to the snprintf builtin with arguments DEST, DESTSIZE, + FMT, and ORIG. ORIG may be null if this is a 3-argument call. We don't + attempt to simplify calls with more than 4 arguments. + + Return NULL_TREE if no simplification was possible, otherwise return the + simplified form of the call as a tree. If IGNORED is true, it means that + the caller does not use the returned value of the function. */ + +static tree +fold_builtin_snprintf (location_t loc, tree dest, tree destsize, tree fmt, + tree orig, int ignored) +{ + tree call, retval; + const char *fmt_str = NULL; + unsigned HOST_WIDE_INT destlen; + + /* Verify the required arguments in the original call. We deal with two + types of snprintf() calls: 'snprintf (str, cst, fmt)' and + 'snprintf (dest, cst, "%s", orig)'. */ + if (!validate_arg (dest, POINTER_TYPE) + || !validate_arg (destsize, INTEGER_TYPE) + || !validate_arg (fmt, POINTER_TYPE)) + return NULL_TREE; + if (orig && !validate_arg (orig, POINTER_TYPE)) + return NULL_TREE; + + if (!host_integerp (destsize, 1)) + return NULL_TREE; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + call = NULL_TREE; + retval = NULL_TREE; + + if (!init_target_chars ()) + return NULL_TREE; + + destlen = tree_low_cst (destsize, 1); + + /* If the format doesn't contain % args or %%, use strcpy. */ + if (strchr (fmt_str, target_percent) == NULL) + { + tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); + size_t len = strlen (fmt_str); + + /* Don't optimize snprintf (buf, 4, "abc", ptr++). */ + if (orig) + return NULL_TREE; + + /* We could expand this as + memcpy (str, fmt, cst - 1); str[cst - 1] = '\0'; + or to + memcpy (str, fmt_with_nul_at_cstm1, cst); + but in the former case that might increase code size + and in the latter case grow .rodata section too much. + So punt for now. */ + if (len >= destlen) + return NULL_TREE; + + if (!fn) + return NULL_TREE; + + /* Convert snprintf (str, cst, fmt) into strcpy (str, fmt) when + 'format' is known to contain no % formats and + strlen (fmt) < cst. */ + call = build_call_expr_loc (loc, fn, 2, dest, fmt); + + if (!ignored) + retval = build_int_cst (integer_type_node, strlen (fmt_str)); + } + + /* If the format is "%s", use strcpy if the result isn't used. */ + else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0) + { + tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); + unsigned HOST_WIDE_INT origlen; + + /* Don't crash on snprintf (str1, cst, "%s"). */ + if (!orig) + return NULL_TREE; + + retval = c_strlen (orig, 1); + if (!retval || !host_integerp (retval, 1)) + return NULL_TREE; + + origlen = tree_low_cst (retval, 1); + /* We could expand this as + memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0'; + or to + memcpy (str1, str2_with_nul_at_cstm1, cst); + but in the former case that might increase code size + and in the latter case grow .rodata section too much. + So punt for now. */ + if (origlen >= destlen) + return NULL_TREE; + + /* Convert snprintf (str1, cst, "%s", str2) into + strcpy (str1, str2) if strlen (str2) < cst. */ + if (!fn) + return NULL_TREE; + + call = build_call_expr_loc (loc, fn, 2, dest, orig); + + if (ignored) + retval = NULL_TREE; + } + + if (call && retval) + { + tree fn = builtin_decl_explicit (BUILT_IN_SNPRINTF); + retval = fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fn)), retval); + return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); + } + else + return call; +} + /* Expand a call EXP to __builtin_object_size. */ rtx @@ -11993,16 +12478,16 @@ expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, switch (fcode) { case BUILT_IN_MEMCPY_CHK: - fn = built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_explicit (BUILT_IN_MEMCPY); break; case BUILT_IN_MEMPCPY_CHK: - fn = built_in_decls[BUILT_IN_MEMPCPY]; + fn = builtin_decl_explicit (BUILT_IN_MEMPCPY); break; case BUILT_IN_MEMMOVE_CHK: - fn = built_in_decls[BUILT_IN_MEMMOVE]; + fn = builtin_decl_explicit (BUILT_IN_MEMMOVE); break; case BUILT_IN_MEMSET_CHK: - fn = built_in_decls[BUILT_IN_MEMSET]; + fn = builtin_decl_explicit (BUILT_IN_MEMSET); break; default: break; @@ -12020,8 +12505,7 @@ expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, return NULL_RTX; else { - unsigned int dest_align - = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + unsigned int dest_align = get_pointer_alignment (dest); /* If DEST is not a pointer type, call the normal function. */ if (dest_align == 0) @@ -12039,15 +12523,14 @@ expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, return expand_expr (dest, target, mode, EXPAND_NORMAL); } - expr = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (dest), dest, len); + expr = fold_build_pointer_plus (dest, len); return expand_expr (expr, target, mode, EXPAND_NORMAL); } /* __memmove_chk special case. */ if (fcode == BUILT_IN_MEMMOVE_CHK) { - unsigned int src_align - = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + unsigned int src_align = get_pointer_alignment (src); if (src_align == 0) return NULL_RTX; @@ -12056,7 +12539,7 @@ expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, normal __memcpy_chk. */ if (readonly_data_expr (src)) { - tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); if (!fn) return NULL_RTX; fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 4, @@ -12092,6 +12575,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode) break; case BUILT_IN_STRNCAT_CHK: case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STPNCPY_CHK: len = CALL_EXPR_ARG (exp, 2); size = CALL_EXPR_ARG (exp, 3); break; @@ -12211,11 +12695,11 @@ maybe_emit_free_warning (tree exp) return; if (SSA_VAR_P (arg)) - warning_at (tree_nonartificial_location (exp), - 0, "%Kattempt to free a non-heap object %qD", exp, arg); + warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object, + "%Kattempt to free a non-heap object %qD", exp, arg); else - warning_at (tree_nonartificial_location (exp), - 0, "%Kattempt to free a non-heap object", exp); + warning_at (tree_nonartificial_location (exp), OPT_Wfree_nonheap_object, + "%Kattempt to free a non-heap object", exp); } /* Fold a call to __builtin_object_size with arguments PTR and OST, @@ -12299,8 +12783,7 @@ fold_builtin_memory_chk (location_t loc, tree fndecl, dest, len); else { - tree temp = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (dest), - dest, len); + tree temp = fold_build_pointer_plus_loc (loc, dest, len); return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), temp); } } @@ -12321,7 +12804,7 @@ fold_builtin_memory_chk (location_t loc, tree fndecl, { /* (void) __mempcpy_chk () can be optimized into (void) __memcpy_chk (). */ - fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); if (!fn) return NULL_TREE; @@ -12343,16 +12826,16 @@ fold_builtin_memory_chk (location_t loc, tree fndecl, switch (fcode) { case BUILT_IN_MEMCPY_CHK: - fn = built_in_decls[BUILT_IN_MEMCPY]; + fn = builtin_decl_explicit (BUILT_IN_MEMCPY); break; case BUILT_IN_MEMPCPY_CHK: - fn = built_in_decls[BUILT_IN_MEMPCPY]; + fn = builtin_decl_explicit (BUILT_IN_MEMPCPY); break; case BUILT_IN_MEMMOVE_CHK: - fn = built_in_decls[BUILT_IN_MEMMOVE]; + fn = builtin_decl_explicit (BUILT_IN_MEMMOVE); break; case BUILT_IN_MEMSET_CHK: - fn = built_in_decls[BUILT_IN_MEMSET]; + fn = builtin_decl_explicit (BUILT_IN_MEMSET); break; default: break; @@ -12407,7 +12890,7 @@ fold_builtin_stxcpy_chk (location_t loc, tree fndecl, tree dest, /* If return value of __stpcpy_chk is ignored, optimize into __strcpy_chk. */ - fn = built_in_decls[BUILT_IN_STRCPY_CHK]; + fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK); if (!fn) return NULL_TREE; @@ -12419,11 +12902,13 @@ fold_builtin_stxcpy_chk (location_t loc, tree fndecl, tree dest, /* If c_strlen returned something, but not a constant, transform __strcpy_chk into __memcpy_chk. */ - fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); if (!fn) return NULL_TREE; - len = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1)); + len = fold_convert_loc (loc, size_type_node, len); + len = size_binop_loc (loc, PLUS_EXPR, len, + build_int_cst (size_type_node, 1)); return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), build_call_expr_loc (loc, fn, 4, dest, src, len, size)); @@ -12437,21 +12922,23 @@ fold_builtin_stxcpy_chk (location_t loc, tree fndecl, tree dest, } /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ - fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK - ? BUILT_IN_STPCPY : BUILT_IN_STRCPY]; + fn = builtin_decl_explicit (fcode == BUILT_IN_STPCPY_CHK + ? BUILT_IN_STPCPY : BUILT_IN_STRCPY); if (!fn) return NULL_TREE; return build_call_expr_loc (loc, fn, 2, dest, src); } -/* Fold a call to the __strncpy_chk builtin. DEST, SRC, LEN, and SIZE +/* Fold a call to the __st{r,p}ncpy_chk builtin. DEST, SRC, LEN, and SIZE are the arguments to the call. If MAXLEN is not NULL, it is maximum - length passed as third argument. */ + length passed as third argument. IGNORE is true if return value can be + ignored. FCODE is the BUILT_IN_* code of the builtin. */ tree -fold_builtin_strncpy_chk (location_t loc, tree dest, tree src, - tree len, tree size, tree maxlen) +fold_builtin_stxncpy_chk (location_t loc, tree dest, tree src, + tree len, tree size, tree maxlen, bool ignore, + enum built_in_function fcode) { tree fn; @@ -12461,6 +12948,15 @@ fold_builtin_strncpy_chk (location_t loc, tree dest, tree src, || !validate_arg (size, INTEGER_TYPE)) return NULL_TREE; + if (fcode == BUILT_IN_STPNCPY_CHK && ignore) + { + /* If return value of __stpncpy_chk is ignored, + optimize into __strncpy_chk. */ + fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK); + if (fn) + return build_call_expr_loc (loc, fn, 4, dest, src, len, size); + } + if (! host_integerp (size, 1)) return NULL_TREE; @@ -12481,8 +12977,9 @@ fold_builtin_strncpy_chk (location_t loc, tree dest, tree src, return NULL_TREE; } - /* If __builtin_strncpy_chk is used, assume strncpy is available. */ - fn = built_in_decls[BUILT_IN_STRNCPY]; + /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available. */ + fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK + ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY); if (!fn) return NULL_TREE; @@ -12513,7 +13010,7 @@ fold_builtin_strcat_chk (location_t loc, tree fndecl, tree dest, return NULL_TREE; /* If __builtin_strcat_chk is used, assume strcat is available. */ - fn = built_in_decls[BUILT_IN_STRCAT]; + fn = builtin_decl_explicit (BUILT_IN_STRCAT); if (!fn) return NULL_TREE; @@ -12555,7 +13052,7 @@ fold_builtin_strncat_chk (location_t loc, tree fndecl, && ! tree_int_cst_lt (len, src_len)) { /* If LEN >= strlen (SRC), optimize into __strcat_chk. */ - fn = built_in_decls[BUILT_IN_STRCAT_CHK]; + fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK); if (!fn) return NULL_TREE; @@ -12565,7 +13062,7 @@ fold_builtin_strncat_chk (location_t loc, tree fndecl, } /* If __builtin_strncat_chk is used, assume strncat is available. */ - fn = built_in_decls[BUILT_IN_STRNCAT]; + fn = builtin_decl_explicit (BUILT_IN_STRNCAT); if (!fn) return NULL_TREE; @@ -12656,8 +13153,8 @@ fold_builtin_sprintf_chk_1 (location_t loc, int nargs, tree *args, } /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */ - fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK - ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF]; + fn = builtin_decl_explicit (fcode == BUILT_IN_VSPRINTF_CHK + ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF); if (!fn) return NULL_TREE; @@ -12745,8 +13242,8 @@ fold_builtin_snprintf_chk_1 (location_t loc, int nargs, tree *args, /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is available. */ - fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK - ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF]; + fn = builtin_decl_explicit (fcode == BUILT_IN_VSNPRINTF_CHK + ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF); if (!fn) return NULL_TREE; @@ -12800,13 +13297,13 @@ fold_builtin_printf (location_t loc, tree fndecl, tree fmt, { /* If we're using an unlocked function, assume the other unlocked functions exist explicitly. */ - fn_putchar = built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]; - fn_puts = built_in_decls[BUILT_IN_PUTS_UNLOCKED]; + fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED); + fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED); } else { - fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR]; - fn_puts = implicit_built_in_decls[BUILT_IN_PUTS]; + fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR); + fn_puts = builtin_decl_implicit (BUILT_IN_PUTS); } if (!init_target_chars ()) @@ -12848,7 +13345,7 @@ fold_builtin_printf (location_t loc, tree fndecl, tree fmt, /* Given printf("c"), (where c is any one character,) convert "c"[0] to an int and pass that to the replacement function. */ - newarg = build_int_cst (NULL_TREE, str[0]); + newarg = build_int_cst (integer_type_node, str[0]); if (fn_putchar) call = build_call_expr_loc (loc, fn_putchar, 1, newarg); } @@ -12856,15 +13353,28 @@ fold_builtin_printf (location_t loc, tree fndecl, tree fmt, { /* If the string was "string\n", call puts("string"). */ size_t len = strlen (str); - if ((unsigned char)str[len - 1] == target_newline) + if ((unsigned char)str[len - 1] == target_newline + && (size_t) (int) len == len + && (int) len > 0) { + char *newstr; + tree offset_node, string_cst; + /* Create a NUL-terminated string that's one char shorter than the original, stripping off the trailing '\n'. */ - char *newstr = XALLOCAVEC (char, len); - memcpy (newstr, str, len - 1); - newstr[len - 1] = 0; - - newarg = build_string_literal (len, newstr); + newarg = build_string_literal (len, str); + string_cst = string_constant (newarg, &offset_node); + gcc_checking_assert (string_cst + && (TREE_STRING_LENGTH (string_cst) + == (int) len) + && integer_zerop (offset_node) + && (unsigned char) + TREE_STRING_POINTER (string_cst)[len - 1] + == target_newline); + /* build_string_literal creates a new STRING_CST, + modify it in place to avoid double copying. */ + newstr = CONST_CAST (char *, TREE_STRING_POINTER (string_cst)); + newstr[len - 1] = '\0'; if (fn_puts) call = build_call_expr_loc (loc, fn_puts, 1, newarg); } @@ -12938,13 +13448,13 @@ fold_builtin_fprintf (location_t loc, tree fndecl, tree fp, { /* If we're using an unlocked function, assume the other unlocked functions exist explicitly. */ - fn_fputc = built_in_decls[BUILT_IN_FPUTC_UNLOCKED]; - fn_fputs = built_in_decls[BUILT_IN_FPUTS_UNLOCKED]; + fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED); + fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED); } else { - fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC]; - fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS]; + fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC); + fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS); } if (!init_target_chars ()) @@ -13437,9 +13947,10 @@ do_mpfr_remquo (tree arg0, tree arg1, tree arg_quo) if (TYPE_MAIN_VARIANT (TREE_TYPE (arg_quo)) == integer_type_node) { /* Set the value. */ - tree result_quo = fold_build2 (MODIFY_EXPR, - TREE_TYPE (arg_quo), arg_quo, - build_int_cst (NULL, integer_quo)); + tree result_quo + = fold_build2 (MODIFY_EXPR, TREE_TYPE (arg_quo), arg_quo, + build_int_cst (TREE_TYPE (arg_quo), + integer_quo)); TREE_SIDE_EFFECTS (result_quo) = 1; /* Combine the quo assignment with the rem. */ result = non_lvalue (fold_build2 (COMPOUND_EXPR, type, @@ -13504,7 +14015,7 @@ do_mpfr_lgamma_r (tree arg, tree arg_sg, tree type) /* Assign the signgam value into *arg_sg. */ result_sg = fold_build2 (MODIFY_EXPR, TREE_TYPE (arg_sg), arg_sg, - build_int_cst (NULL, sg)); + build_int_cst (TREE_TYPE (arg_sg), sg)); TREE_SIDE_EFFECTS (result_sg) = 1; /* Combine the signgam assignment with the lgamma result. */ result = non_lvalue (fold_build2 (COMPOUND_EXPR, type, @@ -13745,7 +14256,7 @@ fold_call_stmt (gimple stmt, bool ignore) return NULL_TREE; } -/* Look up the function in built_in_decls that corresponds to DECL +/* Look up the function in builtin_decl that corresponds to DECL and set ASMSPEC as its user assembler name. DECL must be a function decl that declares a builtin. */ @@ -13757,7 +14268,7 @@ set_builtin_user_assembler_name (tree decl, const char *asmspec) && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL && asmspec != 0); - builtin = built_in_decls [DECL_FUNCTION_CODE (decl)]; + builtin = builtin_decl_explicit (DECL_FUNCTION_CODE (decl)); set_user_assembler_name (builtin, asmspec); switch (DECL_FUNCTION_CODE (decl)) { @@ -13805,6 +14316,7 @@ is_simple_builtin (tree decl) case BUILT_IN_OBJECT_SIZE: case BUILT_IN_UNREACHABLE: /* Simple register moves or loads from stack. */ + case BUILT_IN_ASSUME_ALIGNED: case BUILT_IN_RETURN_ADDRESS: case BUILT_IN_EXTRACT_RETURN_ADDR: case BUILT_IN_FROB_RETURN_ADDR: @@ -13842,6 +14354,7 @@ is_inexpensive_builtin (tree decl) { case BUILT_IN_ABS: case BUILT_IN_ALLOCA: + case BUILT_IN_ALLOCA_WITH_ALIGN: case BUILT_IN_BSWAP32: case BUILT_IN_BSWAP64: case BUILT_IN_CLZ: