190075Sobrien/* Expand builtin functions.
2132727Skan   Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
3169699Skan   2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
490075Sobrien
590075SobrienThis file is part of GCC.
690075Sobrien
790075SobrienGCC is free software; you can redistribute it and/or modify it under
890075Sobrienthe terms of the GNU General Public License as published by the Free
990075SobrienSoftware Foundation; either version 2, or (at your option) any later
1090075Sobrienversion.
1190075Sobrien
1290075SobrienGCC is distributed in the hope that it will be useful, but WITHOUT ANY
1390075SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or
1490075SobrienFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1590075Sobrienfor more details.
1690075Sobrien
1790075SobrienYou should have received a copy of the GNU General Public License
1890075Sobrienalong with GCC; see the file COPYING.  If not, write to the Free
19169699SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
20169699Skan02110-1301, USA.  */
2190075Sobrien
2290075Sobrien#include "config.h"
2390075Sobrien#include "system.h"
24132727Skan#include "coretypes.h"
25132727Skan#include "tm.h"
2690075Sobrien#include "machmode.h"
27117404Skan#include "real.h"
2890075Sobrien#include "rtl.h"
2990075Sobrien#include "tree.h"
30169699Skan#include "tree-gimple.h"
3190075Sobrien#include "flags.h"
3290075Sobrien#include "regs.h"
3390075Sobrien#include "hard-reg-set.h"
3490075Sobrien#include "except.h"
3590075Sobrien#include "function.h"
3690075Sobrien#include "insn-config.h"
3790075Sobrien#include "expr.h"
3890075Sobrien#include "optabs.h"
3990075Sobrien#include "libfuncs.h"
4090075Sobrien#include "recog.h"
4190075Sobrien#include "output.h"
4290075Sobrien#include "typeclass.h"
4390075Sobrien#include "toplev.h"
4490075Sobrien#include "predict.h"
4590075Sobrien#include "tm_p.h"
4690075Sobrien#include "target.h"
47117404Skan#include "langhooks.h"
48169699Skan#include "basic-block.h"
49169699Skan#include "tree-mudflap.h"
5090075Sobrien
5190075Sobrien#ifndef PAD_VARARGS_DOWN
5290075Sobrien#define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
5390075Sobrien#endif
5490075Sobrien
5590075Sobrien/* Define the names of the builtin function types and codes.  */
5690075Sobrienconst char *const built_in_class_names[4]
5790075Sobrien  = {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"};
5890075Sobrien
59169699Skan#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X,
60169699Skanconst char * built_in_names[(int) END_BUILTINS] =
6190075Sobrien{
6290075Sobrien#include "builtins.def"
6390075Sobrien};
6490075Sobrien#undef DEF_BUILTIN
6590075Sobrien
6690075Sobrien/* Setup an array of _DECL trees, make sure each element is
6790075Sobrien   initialized to NULL_TREE.  */
6890075Sobrientree built_in_decls[(int) END_BUILTINS];
69132727Skan/* Declarations used when constructing the builtin implicitly in the compiler.
70132727Skan   It may be NULL_TREE when this is invalid (for instance runtime is not
71169699Skan   required to implement the function call in all cases).  */
72132727Skantree implicit_built_in_decls[(int) END_BUILTINS];
7390075Sobrien
74132727Skanstatic int get_pointer_alignment (tree, unsigned int);
75132727Skanstatic const char *c_getstr (tree);
76132727Skanstatic rtx c_readstr (const char *, enum machine_mode);
77132727Skanstatic int target_char_cast (tree, char *);
78169699Skanstatic rtx get_memory_rtx (tree, tree);
79132727Skanstatic int apply_args_size (void);
80132727Skanstatic int apply_result_size (void);
8190075Sobrien#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
82132727Skanstatic rtx result_vector (int, rtx);
8390075Sobrien#endif
84169699Skanstatic void expand_builtin_update_setjmp_buf (rtx);
85132727Skanstatic void expand_builtin_prefetch (tree);
86132727Skanstatic rtx expand_builtin_apply_args (void);
87132727Skanstatic rtx expand_builtin_apply_args_1 (void);
88132727Skanstatic rtx expand_builtin_apply (rtx, rtx, rtx);
89132727Skanstatic void expand_builtin_return (rtx);
90132727Skanstatic enum type_class type_to_class (tree);
91132727Skanstatic rtx expand_builtin_classify_type (tree);
92132727Skanstatic void expand_errno_check (tree, rtx);
93132727Skanstatic rtx expand_builtin_mathfn (tree, rtx, rtx);
94132727Skanstatic rtx expand_builtin_mathfn_2 (tree, rtx, rtx);
95169699Skanstatic rtx expand_builtin_mathfn_3 (tree, rtx, rtx);
96169699Skanstatic rtx expand_builtin_sincos (tree);
97169699Skanstatic rtx expand_builtin_int_roundingfn (tree, rtx, rtx);
98132727Skanstatic rtx expand_builtin_args_info (tree);
99169699Skanstatic rtx expand_builtin_next_arg (void);
100132727Skanstatic rtx expand_builtin_va_start (tree);
101132727Skanstatic rtx expand_builtin_va_end (tree);
102132727Skanstatic rtx expand_builtin_va_copy (tree);
103132727Skanstatic rtx expand_builtin_memcmp (tree, tree, rtx, enum machine_mode);
104132727Skanstatic rtx expand_builtin_strcmp (tree, rtx, enum machine_mode);
105132727Skanstatic rtx expand_builtin_strncmp (tree, rtx, enum machine_mode);
106132727Skanstatic rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
107169699Skanstatic rtx expand_builtin_strcat (tree, tree, rtx, enum machine_mode);
108132727Skanstatic rtx expand_builtin_strncat (tree, rtx, enum machine_mode);
109132727Skanstatic rtx expand_builtin_strspn (tree, rtx, enum machine_mode);
110132727Skanstatic rtx expand_builtin_strcspn (tree, rtx, enum machine_mode);
111132727Skanstatic rtx expand_builtin_memcpy (tree, rtx, enum machine_mode);
112169699Skanstatic rtx expand_builtin_mempcpy (tree, tree, rtx, enum machine_mode, int);
113169699Skanstatic rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree);
114132727Skanstatic rtx expand_builtin_bcopy (tree);
115169699Skanstatic rtx expand_builtin_strcpy (tree, tree, rtx, enum machine_mode);
116132727Skanstatic rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode);
117132727Skanstatic rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
118132727Skanstatic rtx expand_builtin_strncpy (tree, rtx, enum machine_mode);
119132727Skanstatic rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode);
120132727Skanstatic rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode);
121169699Skanstatic rtx expand_builtin_memset (tree, rtx, enum machine_mode, tree);
122132727Skanstatic rtx expand_builtin_bzero (tree);
123132727Skanstatic rtx expand_builtin_strlen (tree, rtx, enum machine_mode);
124169699Skanstatic rtx expand_builtin_strstr (tree, tree, rtx, enum machine_mode);
125169699Skanstatic rtx expand_builtin_strpbrk (tree, tree, rtx, enum machine_mode);
126169699Skanstatic rtx expand_builtin_strchr (tree, tree, rtx, enum machine_mode);
127169699Skanstatic rtx expand_builtin_strrchr (tree, tree, rtx, enum machine_mode);
128132727Skanstatic rtx expand_builtin_alloca (tree, rtx);
129132727Skanstatic rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab);
130132727Skanstatic rtx expand_builtin_frame_address (tree, tree);
131132727Skanstatic rtx expand_builtin_fputs (tree, rtx, bool);
132132727Skanstatic rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool);
133132727Skanstatic rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool);
134132727Skanstatic rtx expand_builtin_sprintf (tree, rtx, enum machine_mode);
135132727Skanstatic tree stabilize_va_list (tree, int);
136132727Skanstatic rtx expand_builtin_expect (tree, rtx);
137132727Skanstatic tree fold_builtin_constant_p (tree);
138132727Skanstatic tree fold_builtin_classify_type (tree);
139169699Skanstatic tree fold_builtin_strlen (tree);
140132727Skanstatic tree fold_builtin_inf (tree, int);
141132727Skanstatic tree fold_builtin_nan (tree, tree, int);
142132727Skanstatic int validate_arglist (tree, ...);
143132727Skanstatic bool integer_valued_real_p (tree);
144169699Skanstatic tree fold_trunc_transparent_mathfn (tree, tree);
145132727Skanstatic bool readonly_data_expr (tree);
146132727Skanstatic rtx expand_builtin_fabs (tree, rtx, rtx);
147169699Skanstatic rtx expand_builtin_signbit (tree, rtx);
148169699Skanstatic tree fold_builtin_sqrt (tree, tree);
149169699Skanstatic tree fold_builtin_cbrt (tree, tree);
150169699Skanstatic tree fold_builtin_pow (tree, tree, tree);
151169699Skanstatic tree fold_builtin_powi (tree, tree, tree);
152169699Skanstatic tree fold_builtin_sin (tree);
153169699Skanstatic tree fold_builtin_cos (tree, tree, tree);
154169699Skanstatic tree fold_builtin_tan (tree);
155169699Skanstatic tree fold_builtin_atan (tree, tree);
156169699Skanstatic tree fold_builtin_trunc (tree, tree);
157169699Skanstatic tree fold_builtin_floor (tree, tree);
158169699Skanstatic tree fold_builtin_ceil (tree, tree);
159169699Skanstatic tree fold_builtin_round (tree, tree);
160169699Skanstatic tree fold_builtin_int_roundingfn (tree, tree);
161169699Skanstatic tree fold_builtin_bitop (tree, tree);
162169699Skanstatic tree fold_builtin_memory_op (tree, tree, bool, int);
163169699Skanstatic tree fold_builtin_strchr (tree, tree);
164132727Skanstatic tree fold_builtin_memcmp (tree);
165132727Skanstatic tree fold_builtin_strcmp (tree);
166132727Skanstatic tree fold_builtin_strncmp (tree);
167169699Skanstatic tree fold_builtin_signbit (tree, tree);
168169699Skanstatic tree fold_builtin_copysign (tree, tree, tree);
169169699Skanstatic tree fold_builtin_isascii (tree);
170169699Skanstatic tree fold_builtin_toascii (tree);
171169699Skanstatic tree fold_builtin_isdigit (tree);
172169699Skanstatic tree fold_builtin_fabs (tree, tree);
173169699Skanstatic tree fold_builtin_abs (tree, tree);
174169699Skanstatic tree fold_builtin_unordered_cmp (tree, tree, enum tree_code,
175169699Skan					enum tree_code);
176169699Skanstatic tree fold_builtin_1 (tree, tree, bool);
17790075Sobrien
178169699Skanstatic tree fold_builtin_strpbrk (tree, tree);
179169699Skanstatic tree fold_builtin_strstr (tree, tree);
180169699Skanstatic tree fold_builtin_strrchr (tree, tree);
181169699Skanstatic tree fold_builtin_strcat (tree);
182169699Skanstatic tree fold_builtin_strncat (tree);
183169699Skanstatic tree fold_builtin_strspn (tree);
184169699Skanstatic tree fold_builtin_strcspn (tree);
185169699Skanstatic tree fold_builtin_sprintf (tree, int);
186169699Skan
187169699Skanstatic rtx expand_builtin_object_size (tree);
188169699Skanstatic rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode,
189169699Skan				      enum built_in_function);
190169699Skanstatic void maybe_emit_chk_warning (tree, enum built_in_function);
191169699Skanstatic void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
192169699Skanstatic tree fold_builtin_object_size (tree);
193169699Skanstatic tree fold_builtin_strcat_chk (tree, tree);
194169699Skanstatic tree fold_builtin_strncat_chk (tree, tree);
195169699Skanstatic tree fold_builtin_sprintf_chk (tree, enum built_in_function);
196169699Skanstatic tree fold_builtin_printf (tree, tree, bool, enum built_in_function);
197169699Skanstatic tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function);
198169699Skanstatic bool init_target_chars (void);
199169699Skan
200169699Skanstatic unsigned HOST_WIDE_INT target_newline;
201169699Skanstatic unsigned HOST_WIDE_INT target_percent;
202169699Skanstatic unsigned HOST_WIDE_INT target_c;
203169699Skanstatic unsigned HOST_WIDE_INT target_s;
204169699Skanstatic char target_percent_c[3];
205169699Skanstatic char target_percent_s[3];
206169699Skanstatic char target_percent_s_newline[4];
207169699Skan
208169699Skan/* Return true if NODE should be considered for inline expansion regardless
209169699Skan   of the optimization level.  This means whenever a function is invoked with
210169699Skan   its "internal" name, which normally contains the prefix "__builtin".  */
211169699Skan
212169699Skanstatic bool called_as_built_in (tree node)
213169699Skan{
214169699Skan  const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
215169699Skan  if (strncmp (name, "__builtin_", 10) == 0)
216169699Skan    return true;
217169699Skan  if (strncmp (name, "__sync_", 7) == 0)
218169699Skan    return true;
219169699Skan  return false;
220169699Skan}
221169699Skan
22290075Sobrien/* Return the alignment in bits of EXP, a pointer valued expression.
22390075Sobrien   But don't return more than MAX_ALIGN no matter what.
22490075Sobrien   The alignment returned is, by default, the alignment of the thing that
22590075Sobrien   EXP points to.  If it is not a POINTER_TYPE, 0 is returned.
22690075Sobrien
22790075Sobrien   Otherwise, look at the expression to see if we can do better, i.e., if the
22890075Sobrien   expression is actually pointing at an object whose alignment is tighter.  */
22990075Sobrien
23090075Sobrienstatic int
231132727Skanget_pointer_alignment (tree exp, unsigned int max_align)
23290075Sobrien{
23390075Sobrien  unsigned int align, inner;
23490075Sobrien
235169699Skan  /* We rely on TER to compute accurate alignment information.  */
236169699Skan  if (!(optimize && flag_tree_ter))
23790075Sobrien    return 0;
23890075Sobrien
239169699Skan  if (!POINTER_TYPE_P (TREE_TYPE (exp)))
240169699Skan    return 0;
241169699Skan
24290075Sobrien  align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
24390075Sobrien  align = MIN (align, max_align);
24490075Sobrien
24590075Sobrien  while (1)
24690075Sobrien    {
24790075Sobrien      switch (TREE_CODE (exp))
24890075Sobrien	{
24990075Sobrien	case NOP_EXPR:
25090075Sobrien	case CONVERT_EXPR:
25190075Sobrien	case NON_LVALUE_EXPR:
25290075Sobrien	  exp = TREE_OPERAND (exp, 0);
253169699Skan	  if (! POINTER_TYPE_P (TREE_TYPE (exp)))
25490075Sobrien	    return align;
25590075Sobrien
25690075Sobrien	  inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
25790075Sobrien	  align = MIN (inner, max_align);
25890075Sobrien	  break;
25990075Sobrien
26090075Sobrien	case PLUS_EXPR:
26190075Sobrien	  /* If sum of pointer + int, restrict our maximum alignment to that
26290075Sobrien	     imposed by the integer.  If not, we can't do any better than
26390075Sobrien	     ALIGN.  */
26490075Sobrien	  if (! host_integerp (TREE_OPERAND (exp, 1), 1))
26590075Sobrien	    return align;
26690075Sobrien
26790075Sobrien	  while (((tree_low_cst (TREE_OPERAND (exp, 1), 1))
26890075Sobrien		  & (max_align / BITS_PER_UNIT - 1))
26990075Sobrien		 != 0)
27090075Sobrien	    max_align >>= 1;
27190075Sobrien
27290075Sobrien	  exp = TREE_OPERAND (exp, 0);
27390075Sobrien	  break;
27490075Sobrien
27590075Sobrien	case ADDR_EXPR:
27690075Sobrien	  /* See what we are pointing at and look at its alignment.  */
27790075Sobrien	  exp = TREE_OPERAND (exp, 0);
278169699Skan	  inner = max_align;
279169699Skan	  if (handled_component_p (exp))
280169699Skan	    {
281169699Skan	      HOST_WIDE_INT bitsize, bitpos;
282169699Skan	      tree offset;
283169699Skan	      enum machine_mode mode;
284169699Skan	      int unsignedp, volatilep;
285169699Skan
286169699Skan	      exp = get_inner_reference (exp, &bitsize, &bitpos, &offset,
287169699Skan					 &mode, &unsignedp, &volatilep, true);
288169699Skan	      if (bitpos)
289169699Skan		inner = MIN (inner, (unsigned) (bitpos & -bitpos));
290169699Skan	      if (offset && TREE_CODE (offset) == PLUS_EXPR
291169699Skan		  && host_integerp (TREE_OPERAND (offset, 1), 1))
292169699Skan	        {
293169699Skan		  /* Any overflow in calculating offset_bits won't change
294169699Skan		     the alignment.  */
295169699Skan		  unsigned offset_bits
296169699Skan		    = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
297169699Skan		       * BITS_PER_UNIT);
298169699Skan
299169699Skan		  if (offset_bits)
300169699Skan		    inner = MIN (inner, (offset_bits & -offset_bits));
301169699Skan		  offset = TREE_OPERAND (offset, 0);
302169699Skan		}
303169699Skan	      if (offset && TREE_CODE (offset) == MULT_EXPR
304169699Skan		  && host_integerp (TREE_OPERAND (offset, 1), 1))
305169699Skan	        {
306169699Skan		  /* Any overflow in calculating offset_factor won't change
307169699Skan		     the alignment.  */
308169699Skan		  unsigned offset_factor
309169699Skan		    = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
310169699Skan		       * BITS_PER_UNIT);
311169699Skan
312169699Skan		  if (offset_factor)
313169699Skan		    inner = MIN (inner, (offset_factor & -offset_factor));
314169699Skan		}
315169699Skan	      else if (offset)
316169699Skan		inner = MIN (inner, BITS_PER_UNIT);
317169699Skan	    }
318259694Spfg	  if (DECL_P (exp))
319169699Skan	    align = MIN (inner, DECL_ALIGN (exp));
32090075Sobrien#ifdef CONSTANT_ALIGNMENT
321169699Skan	  else if (CONSTANT_CLASS_P (exp))
322169699Skan	    align = MIN (inner, (unsigned)CONSTANT_ALIGNMENT (exp, align));
32390075Sobrien#endif
324169699Skan	  else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR
325169699Skan		   || TREE_CODE (exp) == INDIRECT_REF)
326169699Skan	    align = MIN (TYPE_ALIGN (TREE_TYPE (exp)), inner);
327169699Skan	  else
328169699Skan	    align = MIN (align, inner);
32990075Sobrien	  return MIN (align, max_align);
33090075Sobrien
33190075Sobrien	default:
33290075Sobrien	  return align;
33390075Sobrien	}
33490075Sobrien    }
33590075Sobrien}
33690075Sobrien
33790075Sobrien/* Compute the length of a C string.  TREE_STRING_LENGTH is not the right
33890075Sobrien   way, because it could contain a zero byte in the middle.
33990075Sobrien   TREE_STRING_LENGTH is the size of the character array, not the string.
34090075Sobrien
341132727Skan   ONLY_VALUE should be nonzero if the result is not going to be emitted
342132727Skan   into the instruction stream and zero if it is going to be expanded.
343132727Skan   E.g. with i++ ? "foo" : "bar", if ONLY_VALUE is nonzero, constant 3
344132727Skan   is returned, otherwise NULL, since
345132727Skan   len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not
346132727Skan   evaluate the side-effects.
347132727Skan
34890075Sobrien   The value returned is of type `ssizetype'.
34990075Sobrien
35090075Sobrien   Unfortunately, string_constant can't access the values of const char
35190075Sobrien   arrays with initializers, so neither can we do so here.  */
35290075Sobrien
353169699Skantree
354132727Skanc_strlen (tree src, int only_value)
35590075Sobrien{
35690075Sobrien  tree offset_node;
35790075Sobrien  HOST_WIDE_INT offset;
35890075Sobrien  int max;
35990075Sobrien  const char *ptr;
36090075Sobrien
361132727Skan  STRIP_NOPS (src);
362132727Skan  if (TREE_CODE (src) == COND_EXPR
363132727Skan      && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
364132727Skan    {
365132727Skan      tree len1, len2;
366132727Skan
367132727Skan      len1 = c_strlen (TREE_OPERAND (src, 1), only_value);
368132727Skan      len2 = c_strlen (TREE_OPERAND (src, 2), only_value);
369169699Skan      if (tree_int_cst_equal (len1, len2))
370132727Skan	return len1;
371132727Skan    }
372132727Skan
373132727Skan  if (TREE_CODE (src) == COMPOUND_EXPR
374132727Skan      && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
375132727Skan    return c_strlen (TREE_OPERAND (src, 1), only_value);
376132727Skan
37790075Sobrien  src = string_constant (src, &offset_node);
37890075Sobrien  if (src == 0)
37990075Sobrien    return 0;
38090075Sobrien
38190075Sobrien  max = TREE_STRING_LENGTH (src) - 1;
38290075Sobrien  ptr = TREE_STRING_POINTER (src);
38390075Sobrien
38490075Sobrien  if (offset_node && TREE_CODE (offset_node) != INTEGER_CST)
38590075Sobrien    {
38690075Sobrien      /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't
38790075Sobrien	 compute the offset to the following null if we don't know where to
38890075Sobrien	 start searching for it.  */
38990075Sobrien      int i;
39090075Sobrien
39190075Sobrien      for (i = 0; i < max; i++)
39290075Sobrien	if (ptr[i] == 0)
39390075Sobrien	  return 0;
39490075Sobrien
39590075Sobrien      /* We don't know the starting offset, but we do know that the string
39690075Sobrien	 has no internal zero bytes.  We can assume that the offset falls
39790075Sobrien	 within the bounds of the string; otherwise, the programmer deserves
39890075Sobrien	 what he gets.  Subtract the offset from the length of the string,
39990075Sobrien	 and return that.  This would perhaps not be valid if we were dealing
40090075Sobrien	 with named arrays in addition to literal string constants.  */
40190075Sobrien
40290075Sobrien      return size_diffop (size_int (max), offset_node);
40390075Sobrien    }
40490075Sobrien
40590075Sobrien  /* We have a known offset into the string.  Start searching there for
40690075Sobrien     a null character if we can represent it as a single HOST_WIDE_INT.  */
40790075Sobrien  if (offset_node == 0)
40890075Sobrien    offset = 0;
40990075Sobrien  else if (! host_integerp (offset_node, 0))
41090075Sobrien    offset = -1;
41190075Sobrien  else
41290075Sobrien    offset = tree_low_cst (offset_node, 0);
41390075Sobrien
41490075Sobrien  /* If the offset is known to be out of bounds, warn, and call strlen at
41590075Sobrien     runtime.  */
41690075Sobrien  if (offset < 0 || offset > max)
41790075Sobrien    {
418169699Skan      warning (0, "offset outside bounds of constant string");
41990075Sobrien      return 0;
42090075Sobrien    }
42190075Sobrien
42290075Sobrien  /* Use strlen to search for the first zero byte.  Since any strings
42390075Sobrien     constructed with build_string will have nulls appended, we win even
42490075Sobrien     if we get handed something like (char[4])"abcd".
42590075Sobrien
42690075Sobrien     Since OFFSET is our starting index into the string, no further
42790075Sobrien     calculation is needed.  */
42890075Sobrien  return ssize_int (strlen (ptr + offset));
42990075Sobrien}
43090075Sobrien
43190075Sobrien/* Return a char pointer for a C string if it is a string constant
43290075Sobrien   or sum of string constant and integer constant.  */
43390075Sobrien
43490075Sobrienstatic const char *
435132727Skanc_getstr (tree src)
43690075Sobrien{
43790075Sobrien  tree offset_node;
43890075Sobrien
43990075Sobrien  src = string_constant (src, &offset_node);
44090075Sobrien  if (src == 0)
44190075Sobrien    return 0;
44290075Sobrien
44390075Sobrien  if (offset_node == 0)
44490075Sobrien    return TREE_STRING_POINTER (src);
44590075Sobrien  else if (!host_integerp (offset_node, 1)
44690075Sobrien	   || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0)
44790075Sobrien    return 0;
44890075Sobrien
44990075Sobrien  return TREE_STRING_POINTER (src) + tree_low_cst (offset_node, 1);
45090075Sobrien}
45190075Sobrien
45290075Sobrien/* Return a CONST_INT or CONST_DOUBLE corresponding to target reading
45390075Sobrien   GET_MODE_BITSIZE (MODE) bits from string constant STR.  */
45490075Sobrien
45590075Sobrienstatic rtx
456132727Skanc_readstr (const char *str, enum machine_mode mode)
45790075Sobrien{
45890075Sobrien  HOST_WIDE_INT c[2];
45990075Sobrien  HOST_WIDE_INT ch;
46090075Sobrien  unsigned int i, j;
46190075Sobrien
462169699Skan  gcc_assert (GET_MODE_CLASS (mode) == MODE_INT);
463169699Skan
46490075Sobrien  c[0] = 0;
46590075Sobrien  c[1] = 0;
46690075Sobrien  ch = 1;
46790075Sobrien  for (i = 0; i < GET_MODE_SIZE (mode); i++)
46890075Sobrien    {
46990075Sobrien      j = i;
47090075Sobrien      if (WORDS_BIG_ENDIAN)
47190075Sobrien	j = GET_MODE_SIZE (mode) - i - 1;
47290075Sobrien      if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
47390075Sobrien	  && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
47490075Sobrien	j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1;
47590075Sobrien      j *= BITS_PER_UNIT;
476169699Skan      gcc_assert (j <= 2 * HOST_BITS_PER_WIDE_INT);
477169699Skan
47890075Sobrien      if (ch)
47990075Sobrien	ch = (unsigned char) str[i];
48090075Sobrien      c[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT);
48190075Sobrien    }
48290075Sobrien  return immed_double_const (c[0], c[1], mode);
48390075Sobrien}
48490075Sobrien
48590075Sobrien/* Cast a target constant CST to target CHAR and if that value fits into
486169699Skan   host char type, return zero and put that value into variable pointed to by
48790075Sobrien   P.  */
48890075Sobrien
48990075Sobrienstatic int
490132727Skantarget_char_cast (tree cst, char *p)
49190075Sobrien{
49290075Sobrien  unsigned HOST_WIDE_INT val, hostval;
49390075Sobrien
49490075Sobrien  if (!host_integerp (cst, 1)
49590075Sobrien      || CHAR_TYPE_SIZE > HOST_BITS_PER_WIDE_INT)
49690075Sobrien    return 1;
49790075Sobrien
49890075Sobrien  val = tree_low_cst (cst, 1);
49990075Sobrien  if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT)
50090075Sobrien    val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1;
50190075Sobrien
50290075Sobrien  hostval = val;
50390075Sobrien  if (HOST_BITS_PER_CHAR < HOST_BITS_PER_WIDE_INT)
50490075Sobrien    hostval &= (((unsigned HOST_WIDE_INT) 1) << HOST_BITS_PER_CHAR) - 1;
50590075Sobrien
50690075Sobrien  if (val != hostval)
50790075Sobrien    return 1;
50890075Sobrien
50990075Sobrien  *p = hostval;
51090075Sobrien  return 0;
51190075Sobrien}
51290075Sobrien
513169699Skan/* Similar to save_expr, but assumes that arbitrary code is not executed
514169699Skan   in between the multiple evaluations.  In particular, we assume that a
515169699Skan   non-addressable local variable will not be modified.  */
516169699Skan
517169699Skanstatic tree
518169699Skanbuiltin_save_expr (tree exp)
519169699Skan{
520169699Skan  if (TREE_ADDRESSABLE (exp) == 0
521169699Skan      && (TREE_CODE (exp) == PARM_DECL
522169699Skan	  || (TREE_CODE (exp) == VAR_DECL && !TREE_STATIC (exp))))
523169699Skan    return exp;
524169699Skan
525169699Skan  return save_expr (exp);
526169699Skan}
527169699Skan
52890075Sobrien/* Given TEM, a pointer to a stack frame, follow the dynamic chain COUNT
52990075Sobrien   times to get the address of either a higher stack frame, or a return
53090075Sobrien   address located within it (depending on FNDECL_CODE).  */
53190075Sobrien
532169699Skanstatic rtx
533169699Skanexpand_builtin_return_addr (enum built_in_function fndecl_code, int count)
53490075Sobrien{
53590075Sobrien  int i;
53690075Sobrien
537169699Skan#ifdef INITIAL_FRAME_ADDRESS_RTX
538169699Skan  rtx tem = INITIAL_FRAME_ADDRESS_RTX;
539169699Skan#else
540169699Skan  rtx tem;
541169699Skan
542169699Skan  /* For a zero count with __builtin_return_address, we don't care what
543169699Skan     frame address we return, because target-specific definitions will
544169699Skan     override us.  Therefore frame pointer elimination is OK, and using
545169699Skan     the soft frame pointer is OK.
546169699Skan
547169699Skan     For a non-zero count, or a zero count with __builtin_frame_address,
548169699Skan     we require a stable offset from the current frame pointer to the
549169699Skan     previous one, so we must use the hard frame pointer, and
550169699Skan     we must disable frame pointer elimination.  */
551169699Skan  if (count == 0 && fndecl_code == BUILT_IN_RETURN_ADDRESS)
552169699Skan    tem = frame_pointer_rtx;
553169699Skan  else
554169699Skan    {
555169699Skan      tem = hard_frame_pointer_rtx;
556169699Skan
557169699Skan      /* Tell reload not to eliminate the frame pointer.  */
558169699Skan      current_function_accesses_prior_frames = 1;
559169699Skan    }
560169699Skan#endif
561169699Skan
56290075Sobrien  /* Some machines need special handling before we can access
563169699Skan     arbitrary frames.  For example, on the SPARC, we must first flush
56490075Sobrien     all register windows to the stack.  */
56590075Sobrien#ifdef SETUP_FRAME_ADDRESSES
56690075Sobrien  if (count > 0)
56790075Sobrien    SETUP_FRAME_ADDRESSES ();
56890075Sobrien#endif
56990075Sobrien
570169699Skan  /* On the SPARC, the return address is not in the frame, it is in a
57190075Sobrien     register.  There is no way to access it off of the current frame
57290075Sobrien     pointer, but it can be accessed off the previous frame pointer by
57390075Sobrien     reading the value from the register window save area.  */
57490075Sobrien#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME
57590075Sobrien  if (fndecl_code == BUILT_IN_RETURN_ADDRESS)
57690075Sobrien    count--;
57790075Sobrien#endif
57890075Sobrien
57990075Sobrien  /* Scan back COUNT frames to the specified frame.  */
58090075Sobrien  for (i = 0; i < count; i++)
58190075Sobrien    {
58290075Sobrien      /* Assume the dynamic chain pointer is in the word that the
58390075Sobrien	 frame address points to, unless otherwise specified.  */
58490075Sobrien#ifdef DYNAMIC_CHAIN_ADDRESS
58590075Sobrien      tem = DYNAMIC_CHAIN_ADDRESS (tem);
58690075Sobrien#endif
58790075Sobrien      tem = memory_address (Pmode, tem);
588169699Skan      tem = gen_frame_mem (Pmode, tem);
58990075Sobrien      tem = copy_to_reg (tem);
59090075Sobrien    }
59190075Sobrien
592169699Skan  /* For __builtin_frame_address, return what we've got.  But, on
593169699Skan     the SPARC for example, we may have to add a bias.  */
59490075Sobrien  if (fndecl_code == BUILT_IN_FRAME_ADDRESS)
595169699Skan#ifdef FRAME_ADDR_RTX
596169699Skan    return FRAME_ADDR_RTX (tem);
597169699Skan#else
59890075Sobrien    return tem;
599169699Skan#endif
60090075Sobrien
601169699Skan  /* For __builtin_return_address, get the return address from that frame.  */
60290075Sobrien#ifdef RETURN_ADDR_RTX
60390075Sobrien  tem = RETURN_ADDR_RTX (count, tem);
60490075Sobrien#else
60590075Sobrien  tem = memory_address (Pmode,
60690075Sobrien			plus_constant (tem, GET_MODE_SIZE (Pmode)));
607169699Skan  tem = gen_frame_mem (Pmode, tem);
60890075Sobrien#endif
60990075Sobrien  return tem;
61090075Sobrien}
61190075Sobrien
61290075Sobrien/* Alias set used for setjmp buffer.  */
61390075Sobrienstatic HOST_WIDE_INT setjmp_alias_set = -1;
61490075Sobrien
61590075Sobrien/* Construct the leading half of a __builtin_setjmp call.  Control will
616169699Skan   return to RECEIVER_LABEL.  This is also called directly by the SJLJ
617169699Skan   exception handling code.  */
61890075Sobrien
61990075Sobrienvoid
620132727Skanexpand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
62190075Sobrien{
62290075Sobrien  enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
62390075Sobrien  rtx stack_save;
62490075Sobrien  rtx mem;
62590075Sobrien
62690075Sobrien  if (setjmp_alias_set == -1)
62790075Sobrien    setjmp_alias_set = new_alias_set ();
62890075Sobrien
629132727Skan  buf_addr = convert_memory_address (Pmode, buf_addr);
63090075Sobrien
63190075Sobrien  buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX));
63290075Sobrien
63390075Sobrien  /* We store the frame pointer and the address of receiver_label in
63490075Sobrien     the buffer and use the rest of it for the stack save area, which
63590075Sobrien     is machine-dependent.  */
63690075Sobrien
63790075Sobrien  mem = gen_rtx_MEM (Pmode, buf_addr);
63890075Sobrien  set_mem_alias_set (mem, setjmp_alias_set);
639169699Skan  emit_move_insn (mem, targetm.builtin_setjmp_frame_value ());
64090075Sobrien
64190075Sobrien  mem = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))),
64290075Sobrien  set_mem_alias_set (mem, setjmp_alias_set);
64390075Sobrien
64490075Sobrien  emit_move_insn (validize_mem (mem),
64590075Sobrien		  force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label)));
64690075Sobrien
64790075Sobrien  stack_save = gen_rtx_MEM (sa_mode,
64890075Sobrien			    plus_constant (buf_addr,
64990075Sobrien					   2 * GET_MODE_SIZE (Pmode)));
65090075Sobrien  set_mem_alias_set (stack_save, setjmp_alias_set);
65190075Sobrien  emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX);
65290075Sobrien
65390075Sobrien  /* If there is further processing to do, do it.  */
65490075Sobrien#ifdef HAVE_builtin_setjmp_setup
65590075Sobrien  if (HAVE_builtin_setjmp_setup)
65690075Sobrien    emit_insn (gen_builtin_setjmp_setup (buf_addr));
65790075Sobrien#endif
65890075Sobrien
65990075Sobrien  /* Tell optimize_save_area_alloca that extra work is going to
66090075Sobrien     need to go on during alloca.  */
66190075Sobrien  current_function_calls_setjmp = 1;
66290075Sobrien
66390075Sobrien  /* Set this so all the registers get saved in our frame; we need to be
66490075Sobrien     able to copy the saved values for any registers from frames we unwind.  */
66590075Sobrien  current_function_has_nonlocal_label = 1;
66690075Sobrien}
66790075Sobrien
668169699Skan/* Construct the trailing part of a __builtin_setjmp call.  This is
669169699Skan   also called directly by the SJLJ exception handling code.  */
67090075Sobrien
67190075Sobrienvoid
672132727Skanexpand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED)
67390075Sobrien{
67490075Sobrien  /* Clobber the FP when we get here, so we have to make sure it's
67590075Sobrien     marked as used by this function.  */
67690075Sobrien  emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
67790075Sobrien
67890075Sobrien  /* Mark the static chain as clobbered here so life information
67990075Sobrien     doesn't get messed up for it.  */
68090075Sobrien  emit_insn (gen_rtx_CLOBBER (VOIDmode, static_chain_rtx));
68190075Sobrien
68290075Sobrien  /* Now put in the code to restore the frame pointer, and argument
683169699Skan     pointer, if needed.  */
68490075Sobrien#ifdef HAVE_nonlocal_goto
68590075Sobrien  if (! HAVE_nonlocal_goto)
68690075Sobrien#endif
687169699Skan    {
688169699Skan      emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
689169699Skan      /* This might change the hard frame pointer in ways that aren't
690169699Skan	 apparent to early optimization passes, so force a clobber.  */
691169699Skan      emit_insn (gen_rtx_CLOBBER (VOIDmode, hard_frame_pointer_rtx));
692169699Skan    }
69390075Sobrien
69490075Sobrien#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
69590075Sobrien  if (fixed_regs[ARG_POINTER_REGNUM])
69690075Sobrien    {
69790075Sobrien#ifdef ELIMINABLE_REGS
69890075Sobrien      size_t i;
69990075Sobrien      static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS;
70090075Sobrien
70190075Sobrien      for (i = 0; i < ARRAY_SIZE (elim_regs); i++)
70290075Sobrien	if (elim_regs[i].from == ARG_POINTER_REGNUM
70390075Sobrien	    && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
70490075Sobrien	  break;
70590075Sobrien
70690075Sobrien      if (i == ARRAY_SIZE (elim_regs))
70790075Sobrien#endif
70890075Sobrien	{
70990075Sobrien	  /* Now restore our arg pointer from the address at which it
71090075Sobrien	     was saved in our stack frame.  */
71190075Sobrien	  emit_move_insn (virtual_incoming_args_rtx,
71290075Sobrien			  copy_to_reg (get_arg_pointer_save_area (cfun)));
71390075Sobrien	}
71490075Sobrien    }
71590075Sobrien#endif
71690075Sobrien
71790075Sobrien#ifdef HAVE_builtin_setjmp_receiver
71890075Sobrien  if (HAVE_builtin_setjmp_receiver)
71990075Sobrien    emit_insn (gen_builtin_setjmp_receiver (receiver_label));
72090075Sobrien  else
72190075Sobrien#endif
72290075Sobrien#ifdef HAVE_nonlocal_goto_receiver
72390075Sobrien    if (HAVE_nonlocal_goto_receiver)
72490075Sobrien      emit_insn (gen_nonlocal_goto_receiver ());
72590075Sobrien    else
72690075Sobrien#endif
72790075Sobrien      { /* Nothing */ }
72890075Sobrien
72990075Sobrien  /* @@@ This is a kludge.  Not all machine descriptions define a blockage
73090075Sobrien     insn, but we must not allow the code we just generated to be reordered
73190075Sobrien     by scheduling.  Specifically, the update of the frame pointer must
73290075Sobrien     happen immediately, not later.  So emit an ASM_INPUT to act as blockage
73390075Sobrien     insn.  */
73490075Sobrien  emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
73590075Sobrien}
73690075Sobrien
73790075Sobrien/* __builtin_longjmp is passed a pointer to an array of five words (not
73890075Sobrien   all will be used on all machines).  It operates similarly to the C
73990075Sobrien   library function of the same name, but is more efficient.  Much of
740169699Skan   the code below is copied from the handling of non-local gotos.  */
74190075Sobrien
742169699Skanstatic void
743132727Skanexpand_builtin_longjmp (rtx buf_addr, rtx value)
74490075Sobrien{
745117404Skan  rtx fp, lab, stack, insn, last;
74690075Sobrien  enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
74790075Sobrien
74890075Sobrien  if (setjmp_alias_set == -1)
74990075Sobrien    setjmp_alias_set = new_alias_set ();
75090075Sobrien
751132727Skan  buf_addr = convert_memory_address (Pmode, buf_addr);
75290075Sobrien
75390075Sobrien  buf_addr = force_reg (Pmode, buf_addr);
75490075Sobrien
75590075Sobrien  /* We used to store value in static_chain_rtx, but that fails if pointers
75690075Sobrien     are smaller than integers.  We instead require that the user must pass
75790075Sobrien     a second argument of 1, because that is what builtin_setjmp will
75890075Sobrien     return.  This also makes EH slightly more efficient, since we are no
75990075Sobrien     longer copying around a value that we don't care about.  */
760169699Skan  gcc_assert (value == const1_rtx);
76190075Sobrien
762117404Skan  last = get_last_insn ();
76390075Sobrien#ifdef HAVE_builtin_longjmp
76490075Sobrien  if (HAVE_builtin_longjmp)
76590075Sobrien    emit_insn (gen_builtin_longjmp (buf_addr));
76690075Sobrien  else
76790075Sobrien#endif
76890075Sobrien    {
76990075Sobrien      fp = gen_rtx_MEM (Pmode, buf_addr);
77090075Sobrien      lab = gen_rtx_MEM (Pmode, plus_constant (buf_addr,
77190075Sobrien					       GET_MODE_SIZE (Pmode)));
77290075Sobrien
77390075Sobrien      stack = gen_rtx_MEM (sa_mode, plus_constant (buf_addr,
77490075Sobrien						   2 * GET_MODE_SIZE (Pmode)));
77590075Sobrien      set_mem_alias_set (fp, setjmp_alias_set);
77690075Sobrien      set_mem_alias_set (lab, setjmp_alias_set);
77790075Sobrien      set_mem_alias_set (stack, setjmp_alias_set);
77890075Sobrien
77990075Sobrien      /* Pick up FP, label, and SP from the block and jump.  This code is
78090075Sobrien	 from expand_goto in stmt.c; see there for detailed comments.  */
781169699Skan#ifdef HAVE_nonlocal_goto
78290075Sobrien      if (HAVE_nonlocal_goto)
78390075Sobrien	/* We have to pass a value to the nonlocal_goto pattern that will
78490075Sobrien	   get copied into the static_chain pointer, but it does not matter
78590075Sobrien	   what that value is, because builtin_setjmp does not use it.  */
78690075Sobrien	emit_insn (gen_nonlocal_goto (value, lab, stack, fp));
78790075Sobrien      else
78890075Sobrien#endif
78990075Sobrien	{
79090075Sobrien	  lab = copy_to_reg (lab);
79190075Sobrien
792132727Skan	  emit_insn (gen_rtx_CLOBBER (VOIDmode,
793132727Skan				      gen_rtx_MEM (BLKmode,
794132727Skan						   gen_rtx_SCRATCH (VOIDmode))));
795132727Skan	  emit_insn (gen_rtx_CLOBBER (VOIDmode,
796132727Skan				      gen_rtx_MEM (BLKmode,
797132727Skan						   hard_frame_pointer_rtx)));
798132727Skan
79990075Sobrien	  emit_move_insn (hard_frame_pointer_rtx, fp);
80090075Sobrien	  emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX);
80190075Sobrien
80290075Sobrien	  emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
80390075Sobrien	  emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
80490075Sobrien	  emit_indirect_jump (lab);
80590075Sobrien	}
80690075Sobrien    }
80790075Sobrien
80890075Sobrien  /* Search backwards and mark the jump insn as a non-local goto.
80990075Sobrien     Note that this precludes the use of __builtin_longjmp to a
81090075Sobrien     __builtin_setjmp target in the same function.  However, we've
81190075Sobrien     already cautioned the user that these functions are for
81290075Sobrien     internal exception handling use only.  */
81390075Sobrien  for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
81490075Sobrien    {
815169699Skan      gcc_assert (insn != last);
816169699Skan
817169699Skan      if (JUMP_P (insn))
81890075Sobrien	{
81990075Sobrien	  REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, const0_rtx,
82090075Sobrien					      REG_NOTES (insn));
82190075Sobrien	  break;
82290075Sobrien	}
823169699Skan      else if (CALL_P (insn))
824117404Skan	break;
82590075Sobrien    }
82690075Sobrien}
82790075Sobrien
828169699Skan/* Expand a call to __builtin_nonlocal_goto.  We're passed the target label
829169699Skan   and the address of the save area.  */
830169699Skan
831169699Skanstatic rtx
832169699Skanexpand_builtin_nonlocal_goto (tree arglist)
833169699Skan{
834169699Skan  tree t_label, t_save_area;
835169699Skan  rtx r_label, r_save_area, r_fp, r_sp, insn;
836169699Skan
837169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
838169699Skan    return NULL_RTX;
839169699Skan
840169699Skan  t_label = TREE_VALUE (arglist);
841169699Skan  arglist = TREE_CHAIN (arglist);
842169699Skan  t_save_area = TREE_VALUE (arglist);
843169699Skan
844169699Skan  r_label = expand_normal (t_label);
845169699Skan  r_label = convert_memory_address (Pmode, r_label);
846169699Skan  r_save_area = expand_normal (t_save_area);
847169699Skan  r_save_area = convert_memory_address (Pmode, r_save_area);
848169699Skan  r_fp = gen_rtx_MEM (Pmode, r_save_area);
849169699Skan  r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL),
850169699Skan		      plus_constant (r_save_area, GET_MODE_SIZE (Pmode)));
851169699Skan
852169699Skan  current_function_has_nonlocal_goto = 1;
853169699Skan
854169699Skan#ifdef HAVE_nonlocal_goto
855169699Skan  /* ??? We no longer need to pass the static chain value, afaik.  */
856169699Skan  if (HAVE_nonlocal_goto)
857169699Skan    emit_insn (gen_nonlocal_goto (const0_rtx, r_label, r_sp, r_fp));
858169699Skan  else
859169699Skan#endif
860169699Skan    {
861169699Skan      r_label = copy_to_reg (r_label);
862169699Skan
863169699Skan      emit_insn (gen_rtx_CLOBBER (VOIDmode,
864169699Skan				  gen_rtx_MEM (BLKmode,
865169699Skan					       gen_rtx_SCRATCH (VOIDmode))));
866169699Skan
867169699Skan      emit_insn (gen_rtx_CLOBBER (VOIDmode,
868169699Skan				  gen_rtx_MEM (BLKmode,
869169699Skan					       hard_frame_pointer_rtx)));
870169699Skan
871169699Skan      /* Restore frame pointer for containing function.
872169699Skan	 This sets the actual hard register used for the frame pointer
873169699Skan	 to the location of the function's incoming static chain info.
874169699Skan	 The non-local goto handler will then adjust it to contain the
875169699Skan	 proper value and reload the argument pointer, if needed.  */
876169699Skan      emit_move_insn (hard_frame_pointer_rtx, r_fp);
877169699Skan      emit_stack_restore (SAVE_NONLOCAL, r_sp, NULL_RTX);
878169699Skan
879169699Skan      /* USE of hard_frame_pointer_rtx added for consistency;
880169699Skan	 not clear if really needed.  */
881169699Skan      emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
882169699Skan      emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
883169699Skan      emit_indirect_jump (r_label);
884169699Skan    }
885169699Skan
886169699Skan  /* Search backwards to the jump insn and mark it as a
887169699Skan     non-local goto.  */
888169699Skan  for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
889169699Skan    {
890169699Skan      if (JUMP_P (insn))
891169699Skan	{
892169699Skan	  REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO,
893169699Skan					      const0_rtx, REG_NOTES (insn));
894169699Skan	  break;
895169699Skan	}
896169699Skan      else if (CALL_P (insn))
897169699Skan	break;
898169699Skan    }
899169699Skan
900169699Skan  return const0_rtx;
901169699Skan}
902169699Skan
903169699Skan/* __builtin_update_setjmp_buf is passed a pointer to an array of five words
904169699Skan   (not all will be used on all machines) that was passed to __builtin_setjmp.
905169699Skan   It updates the stack pointer in that block to correspond to the current
906169699Skan   stack pointer.  */
907169699Skan
908169699Skanstatic void
909169699Skanexpand_builtin_update_setjmp_buf (rtx buf_addr)
910169699Skan{
911169699Skan  enum machine_mode sa_mode = Pmode;
912169699Skan  rtx stack_save;
913169699Skan
914169699Skan
915169699Skan#ifdef HAVE_save_stack_nonlocal
916169699Skan  if (HAVE_save_stack_nonlocal)
917169699Skan    sa_mode = insn_data[(int) CODE_FOR_save_stack_nonlocal].operand[0].mode;
918169699Skan#endif
919169699Skan#ifdef STACK_SAVEAREA_MODE
920169699Skan  sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
921169699Skan#endif
922169699Skan
923169699Skan  stack_save
924169699Skan    = gen_rtx_MEM (sa_mode,
925169699Skan		   memory_address
926169699Skan		   (sa_mode,
927169699Skan		    plus_constant (buf_addr, 2 * GET_MODE_SIZE (Pmode))));
928169699Skan
929169699Skan#ifdef HAVE_setjmp
930169699Skan  if (HAVE_setjmp)
931169699Skan    emit_insn (gen_setjmp ());
932169699Skan#endif
933169699Skan
934169699Skan  emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX);
935169699Skan}
936169699Skan
93790075Sobrien/* Expand a call to __builtin_prefetch.  For a target that does not support
93890075Sobrien   data prefetch, evaluate the memory address argument in case it has side
93990075Sobrien   effects.  */
94090075Sobrien
94190075Sobrienstatic void
942132727Skanexpand_builtin_prefetch (tree arglist)
94390075Sobrien{
94490075Sobrien  tree arg0, arg1, arg2;
94590075Sobrien  rtx op0, op1, op2;
94690075Sobrien
94790075Sobrien  if (!validate_arglist (arglist, POINTER_TYPE, 0))
94890075Sobrien    return;
94990075Sobrien
95090075Sobrien  arg0 = TREE_VALUE (arglist);
95190075Sobrien  /* Arguments 1 and 2 are optional; argument 1 (read/write) defaults to
95290075Sobrien     zero (read) and argument 2 (locality) defaults to 3 (high degree of
95390075Sobrien     locality).  */
95490075Sobrien  if (TREE_CHAIN (arglist))
95590075Sobrien    {
95690075Sobrien      arg1 = TREE_VALUE (TREE_CHAIN (arglist));
95790075Sobrien      if (TREE_CHAIN (TREE_CHAIN (arglist)))
958117404Skan	arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
95990075Sobrien      else
960169699Skan	arg2 = build_int_cst (NULL_TREE, 3);
96190075Sobrien    }
96290075Sobrien  else
96390075Sobrien    {
96490075Sobrien      arg1 = integer_zero_node;
965169699Skan      arg2 = build_int_cst (NULL_TREE, 3);
96690075Sobrien    }
96790075Sobrien
96890075Sobrien  /* Argument 0 is an address.  */
96990075Sobrien  op0 = expand_expr (arg0, NULL_RTX, Pmode, EXPAND_NORMAL);
97090075Sobrien
97190075Sobrien  /* Argument 1 (read/write flag) must be a compile-time constant int.  */
97290075Sobrien  if (TREE_CODE (arg1) != INTEGER_CST)
97390075Sobrien    {
974169699Skan      error ("second argument to %<__builtin_prefetch%> must be a constant");
975117404Skan      arg1 = integer_zero_node;
97690075Sobrien    }
977169699Skan  op1 = expand_normal (arg1);
97890075Sobrien  /* Argument 1 must be either zero or one.  */
97990075Sobrien  if (INTVAL (op1) != 0 && INTVAL (op1) != 1)
98090075Sobrien    {
981169699Skan      warning (0, "invalid second argument to %<__builtin_prefetch%>;"
982169699Skan	       " using zero");
98390075Sobrien      op1 = const0_rtx;
98490075Sobrien    }
98590075Sobrien
98690075Sobrien  /* Argument 2 (locality) must be a compile-time constant int.  */
98790075Sobrien  if (TREE_CODE (arg2) != INTEGER_CST)
98890075Sobrien    {
989169699Skan      error ("third argument to %<__builtin_prefetch%> must be a constant");
99090075Sobrien      arg2 = integer_zero_node;
99190075Sobrien    }
992169699Skan  op2 = expand_normal (arg2);
99390075Sobrien  /* Argument 2 must be 0, 1, 2, or 3.  */
99490075Sobrien  if (INTVAL (op2) < 0 || INTVAL (op2) > 3)
99590075Sobrien    {
996169699Skan      warning (0, "invalid third argument to %<__builtin_prefetch%>; using zero");
99790075Sobrien      op2 = const0_rtx;
99890075Sobrien    }
99990075Sobrien
100090075Sobrien#ifdef HAVE_prefetch
100190075Sobrien  if (HAVE_prefetch)
100290075Sobrien    {
1003117404Skan      if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
1004117404Skan	     (op0,
1005117404Skan	      insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
1006132727Skan	  || (GET_MODE (op0) != Pmode))
1007117404Skan	{
1008132727Skan	  op0 = convert_memory_address (Pmode, op0);
1009117404Skan	  op0 = force_reg (Pmode, op0);
1010117404Skan	}
101190075Sobrien      emit_insn (gen_prefetch (op0, op1, op2));
101290075Sobrien    }
101390075Sobrien#endif
1014169699Skan
1015117404Skan  /* Don't do anything with direct references to volatile memory, but
1016117404Skan     generate code to handle other side effects.  */
1017169699Skan  if (!MEM_P (op0) && side_effects_p (op0))
1018117404Skan    emit_insn (op0);
101990075Sobrien}
102090075Sobrien
102190075Sobrien/* Get a MEM rtx for expression EXP which is the address of an operand
1022169699Skan   to be used in a string instruction (cmpstrsi, movmemsi, ..).  LEN is
1023169699Skan   the maximum length of the block of memory that might be accessed or
1024169699Skan   NULL if unknown.  */
102590075Sobrien
102690075Sobrienstatic rtx
1027169699Skanget_memory_rtx (tree exp, tree len)
102890075Sobrien{
1029169699Skan  rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
1030169699Skan  rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
103190075Sobrien
103290075Sobrien  /* Get an expression we can use to find the attributes to assign to MEM.
103390075Sobrien     If it is an ADDR_EXPR, use the operand.  Otherwise, dereference it if
103490075Sobrien     we can.  First remove any nops.  */
103590075Sobrien  while ((TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
1036117404Skan	  || TREE_CODE (exp) == NON_LVALUE_EXPR)
103790075Sobrien	 && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
103890075Sobrien    exp = TREE_OPERAND (exp, 0);
103990075Sobrien
104090075Sobrien  if (TREE_CODE (exp) == ADDR_EXPR)
1041169699Skan    exp = TREE_OPERAND (exp, 0);
1042169699Skan  else if (POINTER_TYPE_P (TREE_TYPE (exp)))
1043169699Skan    exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
1044169699Skan  else
1045169699Skan    exp = NULL;
1046169699Skan
1047169699Skan  /* Honor attributes derived from exp, except for the alias set
1048169699Skan     (as builtin stringops may alias with anything) and the size
1049169699Skan     (as stringops may access multiple array elements).  */
1050169699Skan  if (exp)
105190075Sobrien    {
105290075Sobrien      set_mem_attributes (mem, exp, 0);
1053169699Skan
1054169699Skan      /* Allow the string and memory builtins to overflow from one
1055169699Skan	 field into another, see http://gcc.gnu.org/PR23561.
1056169699Skan	 Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
1057169699Skan	 memory accessed by the string or memory builtin will fit
1058169699Skan	 within the field.  */
1059169699Skan      if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF)
1060169699Skan	{
1061169699Skan	  tree mem_expr = MEM_EXPR (mem);
1062169699Skan	  HOST_WIDE_INT offset = -1, length = -1;
1063169699Skan	  tree inner = exp;
1064169699Skan
1065169699Skan	  while (TREE_CODE (inner) == ARRAY_REF
1066169699Skan		 || TREE_CODE (inner) == NOP_EXPR
1067169699Skan		 || TREE_CODE (inner) == CONVERT_EXPR
1068169699Skan		 || TREE_CODE (inner) == NON_LVALUE_EXPR
1069169699Skan		 || TREE_CODE (inner) == VIEW_CONVERT_EXPR
1070169699Skan		 || TREE_CODE (inner) == SAVE_EXPR)
1071169699Skan	    inner = TREE_OPERAND (inner, 0);
1072169699Skan
1073169699Skan	  gcc_assert (TREE_CODE (inner) == COMPONENT_REF);
1074169699Skan
1075169699Skan	  if (MEM_OFFSET (mem)
1076169699Skan	      && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
1077169699Skan	    offset = INTVAL (MEM_OFFSET (mem));
1078169699Skan
1079169699Skan	  if (offset >= 0 && len && host_integerp (len, 0))
1080169699Skan	    length = tree_low_cst (len, 0);
1081169699Skan
1082169699Skan	  while (TREE_CODE (inner) == COMPONENT_REF)
1083169699Skan	    {
1084169699Skan	      tree field = TREE_OPERAND (inner, 1);
1085169699Skan	      gcc_assert (! DECL_BIT_FIELD (field));
1086169699Skan	      gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF);
1087169699Skan	      gcc_assert (field == TREE_OPERAND (mem_expr, 1));
1088169699Skan
1089169699Skan	      if (length >= 0
1090169699Skan		  && TYPE_SIZE_UNIT (TREE_TYPE (inner))
1091169699Skan		  && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0))
1092169699Skan		{
1093169699Skan		  HOST_WIDE_INT size
1094169699Skan		    = tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0);
1095169699Skan		  /* If we can prove the memory starting at XEXP (mem, 0)
1096169699Skan		     and ending at XEXP (mem, 0) + LENGTH will fit into
1097169699Skan		     this field, we can keep that COMPONENT_REF in MEM_EXPR.  */
1098169699Skan		  if (offset <= size
1099169699Skan		      && length <= size
1100169699Skan		      && offset + length <= size)
1101169699Skan		    break;
1102169699Skan		}
1103169699Skan
1104169699Skan	      if (offset >= 0
1105169699Skan		  && host_integerp (DECL_FIELD_OFFSET (field), 0))
1106169699Skan		offset += tree_low_cst (DECL_FIELD_OFFSET (field), 0)
1107169699Skan			  + tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
1108169699Skan			    / BITS_PER_UNIT;
1109169699Skan	      else
1110169699Skan		{
1111169699Skan		  offset = -1;
1112169699Skan		  length = -1;
1113169699Skan		}
1114169699Skan
1115169699Skan	      mem_expr = TREE_OPERAND (mem_expr, 0);
1116169699Skan	      inner = TREE_OPERAND (inner, 0);
1117169699Skan	    }
1118169699Skan
1119169699Skan	  if (mem_expr == NULL)
1120169699Skan	    offset = -1;
1121169699Skan	  if (mem_expr != MEM_EXPR (mem))
1122169699Skan	    {
1123169699Skan	      set_mem_expr (mem, mem_expr);
1124169699Skan	      set_mem_offset (mem, offset >= 0 ? GEN_INT (offset) : NULL_RTX);
1125169699Skan	    }
1126169699Skan	}
112790075Sobrien      set_mem_alias_set (mem, 0);
1128169699Skan      set_mem_size (mem, NULL_RTX);
112990075Sobrien    }
113090075Sobrien
113190075Sobrien  return mem;
113290075Sobrien}
113390075Sobrien
113490075Sobrien/* Built-in functions to perform an untyped call and return.  */
113590075Sobrien
113690075Sobrien/* For each register that may be used for calling a function, this
113790075Sobrien   gives a mode used to copy the register's value.  VOIDmode indicates
113890075Sobrien   the register is not used for calling a function.  If the machine
113990075Sobrien   has register windows, this gives only the outbound registers.
114090075Sobrien   INCOMING_REGNO gives the corresponding inbound register.  */
114190075Sobrienstatic enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER];
114290075Sobrien
114390075Sobrien/* For each register that may be used for returning values, this gives
114490075Sobrien   a mode used to copy the register's value.  VOIDmode indicates the
114590075Sobrien   register is not used for returning values.  If the machine has
114690075Sobrien   register windows, this gives only the outbound registers.
114790075Sobrien   INCOMING_REGNO gives the corresponding inbound register.  */
114890075Sobrienstatic enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER];
114990075Sobrien
115090075Sobrien/* For each register that may be used for calling a function, this
115190075Sobrien   gives the offset of that register into the block returned by
115290075Sobrien   __builtin_apply_args.  0 indicates that the register is not
115390075Sobrien   used for calling a function.  */
115490075Sobrienstatic int apply_args_reg_offset[FIRST_PSEUDO_REGISTER];
115590075Sobrien
115690075Sobrien/* Return the size required for the block returned by __builtin_apply_args,
115790075Sobrien   and initialize apply_args_mode.  */
115890075Sobrien
115990075Sobrienstatic int
1160132727Skanapply_args_size (void)
116190075Sobrien{
116290075Sobrien  static int size = -1;
116390075Sobrien  int align;
116490075Sobrien  unsigned int regno;
116590075Sobrien  enum machine_mode mode;
116690075Sobrien
116790075Sobrien  /* The values computed by this function never change.  */
116890075Sobrien  if (size < 0)
116990075Sobrien    {
117090075Sobrien      /* The first value is the incoming arg-pointer.  */
117190075Sobrien      size = GET_MODE_SIZE (Pmode);
117290075Sobrien
117390075Sobrien      /* The second value is the structure value address unless this is
117490075Sobrien	 passed as an "invisible" first argument.  */
1175132727Skan      if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0))
117690075Sobrien	size += GET_MODE_SIZE (Pmode);
117790075Sobrien
117890075Sobrien      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
117990075Sobrien	if (FUNCTION_ARG_REGNO_P (regno))
118090075Sobrien	  {
1181169699Skan	    mode = reg_raw_mode[regno];
118290075Sobrien
1183169699Skan	    gcc_assert (mode != VOIDmode);
118490075Sobrien
118590075Sobrien	    align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
118690075Sobrien	    if (size % align != 0)
118790075Sobrien	      size = CEIL (size, align) * align;
118890075Sobrien	    apply_args_reg_offset[regno] = size;
118990075Sobrien	    size += GET_MODE_SIZE (mode);
119090075Sobrien	    apply_args_mode[regno] = mode;
119190075Sobrien	  }
119290075Sobrien	else
119390075Sobrien	  {
119490075Sobrien	    apply_args_mode[regno] = VOIDmode;
119590075Sobrien	    apply_args_reg_offset[regno] = 0;
119690075Sobrien	  }
119790075Sobrien    }
119890075Sobrien  return size;
119990075Sobrien}
120090075Sobrien
120190075Sobrien/* Return the size required for the block returned by __builtin_apply,
120290075Sobrien   and initialize apply_result_mode.  */
120390075Sobrien
120490075Sobrienstatic int
1205132727Skanapply_result_size (void)
120690075Sobrien{
120790075Sobrien  static int size = -1;
120890075Sobrien  int align, regno;
120990075Sobrien  enum machine_mode mode;
121090075Sobrien
121190075Sobrien  /* The values computed by this function never change.  */
121290075Sobrien  if (size < 0)
121390075Sobrien    {
121490075Sobrien      size = 0;
121590075Sobrien
121690075Sobrien      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
121790075Sobrien	if (FUNCTION_VALUE_REGNO_P (regno))
121890075Sobrien	  {
1219169699Skan	    mode = reg_raw_mode[regno];
122090075Sobrien
1221169699Skan	    gcc_assert (mode != VOIDmode);
122290075Sobrien
122390075Sobrien	    align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
122490075Sobrien	    if (size % align != 0)
122590075Sobrien	      size = CEIL (size, align) * align;
122690075Sobrien	    size += GET_MODE_SIZE (mode);
122790075Sobrien	    apply_result_mode[regno] = mode;
122890075Sobrien	  }
122990075Sobrien	else
123090075Sobrien	  apply_result_mode[regno] = VOIDmode;
123190075Sobrien
123290075Sobrien      /* Allow targets that use untyped_call and untyped_return to override
123390075Sobrien	 the size so that machine-specific information can be stored here.  */
123490075Sobrien#ifdef APPLY_RESULT_SIZE
123590075Sobrien      size = APPLY_RESULT_SIZE;
123690075Sobrien#endif
123790075Sobrien    }
123890075Sobrien  return size;
123990075Sobrien}
124090075Sobrien
124190075Sobrien#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
124290075Sobrien/* Create a vector describing the result block RESULT.  If SAVEP is true,
124390075Sobrien   the result block is used to save the values; otherwise it is used to
124490075Sobrien   restore the values.  */
124590075Sobrien
124690075Sobrienstatic rtx
1247132727Skanresult_vector (int savep, rtx result)
124890075Sobrien{
124990075Sobrien  int regno, size, align, nelts;
125090075Sobrien  enum machine_mode mode;
125190075Sobrien  rtx reg, mem;
1252132727Skan  rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
125390075Sobrien
125490075Sobrien  size = nelts = 0;
125590075Sobrien  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
125690075Sobrien    if ((mode = apply_result_mode[regno]) != VOIDmode)
125790075Sobrien      {
125890075Sobrien	align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
125990075Sobrien	if (size % align != 0)
126090075Sobrien	  size = CEIL (size, align) * align;
126190075Sobrien	reg = gen_rtx_REG (mode, savep ? regno : INCOMING_REGNO (regno));
126290075Sobrien	mem = adjust_address (result, mode, size);
126390075Sobrien	savevec[nelts++] = (savep
126490075Sobrien			    ? gen_rtx_SET (VOIDmode, mem, reg)
126590075Sobrien			    : gen_rtx_SET (VOIDmode, reg, mem));
126690075Sobrien	size += GET_MODE_SIZE (mode);
126790075Sobrien      }
126890075Sobrien  return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nelts, savevec));
126990075Sobrien}
127090075Sobrien#endif /* HAVE_untyped_call or HAVE_untyped_return */
127190075Sobrien
127290075Sobrien/* Save the state required to perform an untyped call with the same
127390075Sobrien   arguments as were passed to the current function.  */
127490075Sobrien
127590075Sobrienstatic rtx
1276132727Skanexpand_builtin_apply_args_1 (void)
127790075Sobrien{
1278132727Skan  rtx registers, tem;
127990075Sobrien  int size, align, regno;
128090075Sobrien  enum machine_mode mode;
1281132727Skan  rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1);
128290075Sobrien
128390075Sobrien  /* Create a block where the arg-pointer, structure value address,
128490075Sobrien     and argument registers can be saved.  */
128590075Sobrien  registers = assign_stack_local (BLKmode, apply_args_size (), -1);
128690075Sobrien
128790075Sobrien  /* Walk past the arg-pointer and structure value address.  */
128890075Sobrien  size = GET_MODE_SIZE (Pmode);
1289132727Skan  if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0))
129090075Sobrien    size += GET_MODE_SIZE (Pmode);
129190075Sobrien
129290075Sobrien  /* Save each register used in calling a function to the block.  */
129390075Sobrien  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
129490075Sobrien    if ((mode = apply_args_mode[regno]) != VOIDmode)
129590075Sobrien      {
129690075Sobrien	align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
129790075Sobrien	if (size % align != 0)
129890075Sobrien	  size = CEIL (size, align) * align;
129990075Sobrien
130090075Sobrien	tem = gen_rtx_REG (mode, INCOMING_REGNO (regno));
130190075Sobrien
130290075Sobrien	emit_move_insn (adjust_address (registers, mode, size), tem);
130390075Sobrien	size += GET_MODE_SIZE (mode);
130490075Sobrien      }
130590075Sobrien
130690075Sobrien  /* Save the arg pointer to the block.  */
1307132727Skan  tem = copy_to_reg (virtual_incoming_args_rtx);
1308132727Skan#ifdef STACK_GROWS_DOWNWARD
1309132727Skan  /* We need the pointer as the caller actually passed them to us, not
1310132727Skan     as we might have pretended they were passed.  Make sure it's a valid
1311132727Skan     operand, as emit_move_insn isn't expected to handle a PLUS.  */
1312132727Skan  tem
1313132727Skan    = force_operand (plus_constant (tem, current_function_pretend_args_size),
1314132727Skan		     NULL_RTX);
1315132727Skan#endif
1316132727Skan  emit_move_insn (adjust_address (registers, Pmode, 0), tem);
1317169699Skan
131890075Sobrien  size = GET_MODE_SIZE (Pmode);
131990075Sobrien
132090075Sobrien  /* Save the structure value address unless this is passed as an
132190075Sobrien     "invisible" first argument.  */
1322132727Skan  if (struct_incoming_value)
132390075Sobrien    {
132490075Sobrien      emit_move_insn (adjust_address (registers, Pmode, size),
1325132727Skan		      copy_to_reg (struct_incoming_value));
132690075Sobrien      size += GET_MODE_SIZE (Pmode);
132790075Sobrien    }
132890075Sobrien
132990075Sobrien  /* Return the address of the block.  */
133090075Sobrien  return copy_addr_to_reg (XEXP (registers, 0));
133190075Sobrien}
133290075Sobrien
133390075Sobrien/* __builtin_apply_args returns block of memory allocated on
133490075Sobrien   the stack into which is stored the arg pointer, structure
133590075Sobrien   value address, static chain, and all the registers that might
133690075Sobrien   possibly be used in performing a function call.  The code is
133790075Sobrien   moved to the start of the function so the incoming values are
133890075Sobrien   saved.  */
133990075Sobrien
134090075Sobrienstatic rtx
1341132727Skanexpand_builtin_apply_args (void)
134290075Sobrien{
134390075Sobrien  /* Don't do __builtin_apply_args more than once in a function.
134490075Sobrien     Save the result of the first call and reuse it.  */
134590075Sobrien  if (apply_args_value != 0)
134690075Sobrien    return apply_args_value;
134790075Sobrien  {
134890075Sobrien    /* When this function is called, it means that registers must be
134990075Sobrien       saved on entry to this function.  So we migrate the
135090075Sobrien       call to the first insn of this function.  */
135190075Sobrien    rtx temp;
135290075Sobrien    rtx seq;
135390075Sobrien
135490075Sobrien    start_sequence ();
135590075Sobrien    temp = expand_builtin_apply_args_1 ();
135690075Sobrien    seq = get_insns ();
135790075Sobrien    end_sequence ();
135890075Sobrien
135990075Sobrien    apply_args_value = temp;
136090075Sobrien
1361117404Skan    /* Put the insns after the NOTE that starts the function.
1362117404Skan       If this is inside a start_sequence, make the outer-level insn
136390075Sobrien       chain current, so the code is placed at the start of the
136490075Sobrien       function.  */
136590075Sobrien    push_topmost_sequence ();
1366169699Skan    emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
136790075Sobrien    pop_topmost_sequence ();
136890075Sobrien    return temp;
136990075Sobrien  }
137090075Sobrien}
137190075Sobrien
137290075Sobrien/* Perform an untyped call and save the state required to perform an
137390075Sobrien   untyped return of whatever value was returned by the given function.  */
137490075Sobrien
137590075Sobrienstatic rtx
1376132727Skanexpand_builtin_apply (rtx function, rtx arguments, rtx argsize)
137790075Sobrien{
137890075Sobrien  int size, align, regno;
137990075Sobrien  enum machine_mode mode;
138090075Sobrien  rtx incoming_args, result, reg, dest, src, call_insn;
138190075Sobrien  rtx old_stack_level = 0;
138290075Sobrien  rtx call_fusage = 0;
1383132727Skan  rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0);
138490075Sobrien
1385132727Skan  arguments = convert_memory_address (Pmode, arguments);
138690075Sobrien
138790075Sobrien  /* Create a block where the return registers can be saved.  */
138890075Sobrien  result = assign_stack_local (BLKmode, apply_result_size (), -1);
138990075Sobrien
139090075Sobrien  /* Fetch the arg pointer from the ARGUMENTS block.  */
139190075Sobrien  incoming_args = gen_reg_rtx (Pmode);
139290075Sobrien  emit_move_insn (incoming_args, gen_rtx_MEM (Pmode, arguments));
139390075Sobrien#ifndef STACK_GROWS_DOWNWARD
139490075Sobrien  incoming_args = expand_simple_binop (Pmode, MINUS, incoming_args, argsize,
139590075Sobrien				       incoming_args, 0, OPTAB_LIB_WIDEN);
139690075Sobrien#endif
139790075Sobrien
139890075Sobrien  /* Push a new argument block and copy the arguments.  Do not allow
139990075Sobrien     the (potential) memcpy call below to interfere with our stack
140090075Sobrien     manipulations.  */
140190075Sobrien  do_pending_stack_adjust ();
140290075Sobrien  NO_DEFER_POP;
140390075Sobrien
1404132727Skan  /* Save the stack with nonlocal if available.  */
140590075Sobrien#ifdef HAVE_save_stack_nonlocal
140690075Sobrien  if (HAVE_save_stack_nonlocal)
140790075Sobrien    emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX);
140890075Sobrien  else
140990075Sobrien#endif
141090075Sobrien    emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
141190075Sobrien
1412132727Skan  /* Allocate a block of memory onto the stack and copy the memory
1413132727Skan     arguments to the outgoing arguments address.  */
1414132727Skan  allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
1415132727Skan  dest = virtual_outgoing_args_rtx;
1416132727Skan#ifndef STACK_GROWS_DOWNWARD
1417132727Skan  if (GET_CODE (argsize) == CONST_INT)
1418132727Skan    dest = plus_constant (dest, -INTVAL (argsize));
1419132727Skan  else
1420132727Skan    dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize));
1421132727Skan#endif
142290075Sobrien  dest = gen_rtx_MEM (BLKmode, dest);
142390075Sobrien  set_mem_align (dest, PARM_BOUNDARY);
142490075Sobrien  src = gen_rtx_MEM (BLKmode, incoming_args);
142590075Sobrien  set_mem_align (src, PARM_BOUNDARY);
1426117404Skan  emit_block_move (dest, src, argsize, BLOCK_OP_NORMAL);
142790075Sobrien
142890075Sobrien  /* Refer to the argument block.  */
142990075Sobrien  apply_args_size ();
143090075Sobrien  arguments = gen_rtx_MEM (BLKmode, arguments);
143190075Sobrien  set_mem_align (arguments, PARM_BOUNDARY);
143290075Sobrien
143390075Sobrien  /* Walk past the arg-pointer and structure value address.  */
143490075Sobrien  size = GET_MODE_SIZE (Pmode);
1435132727Skan  if (struct_value)
143690075Sobrien    size += GET_MODE_SIZE (Pmode);
143790075Sobrien
143890075Sobrien  /* Restore each of the registers previously saved.  Make USE insns
143990075Sobrien     for each of these registers for use in making the call.  */
144090075Sobrien  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
144190075Sobrien    if ((mode = apply_args_mode[regno]) != VOIDmode)
144290075Sobrien      {
144390075Sobrien	align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
144490075Sobrien	if (size % align != 0)
144590075Sobrien	  size = CEIL (size, align) * align;
144690075Sobrien	reg = gen_rtx_REG (mode, regno);
144790075Sobrien	emit_move_insn (reg, adjust_address (arguments, mode, size));
144890075Sobrien	use_reg (&call_fusage, reg);
144990075Sobrien	size += GET_MODE_SIZE (mode);
145090075Sobrien      }
145190075Sobrien
145290075Sobrien  /* Restore the structure value address unless this is passed as an
145390075Sobrien     "invisible" first argument.  */
145490075Sobrien  size = GET_MODE_SIZE (Pmode);
1455132727Skan  if (struct_value)
145690075Sobrien    {
145790075Sobrien      rtx value = gen_reg_rtx (Pmode);
145890075Sobrien      emit_move_insn (value, adjust_address (arguments, Pmode, size));
1459132727Skan      emit_move_insn (struct_value, value);
1460169699Skan      if (REG_P (struct_value))
1461132727Skan	use_reg (&call_fusage, struct_value);
146290075Sobrien      size += GET_MODE_SIZE (Pmode);
146390075Sobrien    }
146490075Sobrien
146590075Sobrien  /* All arguments and registers used for the call are set up by now!  */
1466169699Skan  function = prepare_call_address (function, NULL, &call_fusage, 0, 0);
146790075Sobrien
146890075Sobrien  /* Ensure address is valid.  SYMBOL_REF is already valid, so no need,
146990075Sobrien     and we don't want to load it into a register as an optimization,
147090075Sobrien     because prepare_call_address already did it if it should be done.  */
147190075Sobrien  if (GET_CODE (function) != SYMBOL_REF)
147290075Sobrien    function = memory_address (FUNCTION_MODE, function);
147390075Sobrien
147490075Sobrien  /* Generate the actual call instruction and save the return value.  */
147590075Sobrien#ifdef HAVE_untyped_call
147690075Sobrien  if (HAVE_untyped_call)
147790075Sobrien    emit_call_insn (gen_untyped_call (gen_rtx_MEM (FUNCTION_MODE, function),
147890075Sobrien				      result, result_vector (1, result)));
147990075Sobrien  else
148090075Sobrien#endif
148190075Sobrien#ifdef HAVE_call_value
148290075Sobrien  if (HAVE_call_value)
148390075Sobrien    {
148490075Sobrien      rtx valreg = 0;
148590075Sobrien
148690075Sobrien      /* Locate the unique return register.  It is not possible to
148790075Sobrien	 express a call that sets more than one return register using
148890075Sobrien	 call_value; use untyped_call for that.  In fact, untyped_call
148990075Sobrien	 only needs to save the return registers in the given block.  */
149090075Sobrien      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
149190075Sobrien	if ((mode = apply_result_mode[regno]) != VOIDmode)
149290075Sobrien	  {
1493169699Skan	    gcc_assert (!valreg); /* HAVE_untyped_call required.  */
1494169699Skan
149590075Sobrien	    valreg = gen_rtx_REG (mode, regno);
149690075Sobrien	  }
149790075Sobrien
149890075Sobrien      emit_call_insn (GEN_CALL_VALUE (valreg,
149990075Sobrien				      gen_rtx_MEM (FUNCTION_MODE, function),
150090075Sobrien				      const0_rtx, NULL_RTX, const0_rtx));
150190075Sobrien
150290075Sobrien      emit_move_insn (adjust_address (result, GET_MODE (valreg), 0), valreg);
150390075Sobrien    }
150490075Sobrien  else
150590075Sobrien#endif
1506169699Skan    gcc_unreachable ();
150790075Sobrien
1508132727Skan  /* Find the CALL insn we just emitted, and attach the register usage
1509132727Skan     information.  */
1510132727Skan  call_insn = last_call_insn ();
1511132727Skan  add_function_usage_to (call_insn, call_fusage);
151290075Sobrien
151390075Sobrien  /* Restore the stack.  */
151490075Sobrien#ifdef HAVE_save_stack_nonlocal
151590075Sobrien  if (HAVE_save_stack_nonlocal)
151690075Sobrien    emit_stack_restore (SAVE_NONLOCAL, old_stack_level, NULL_RTX);
151790075Sobrien  else
151890075Sobrien#endif
151990075Sobrien    emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
152090075Sobrien
152190075Sobrien  OK_DEFER_POP;
152290075Sobrien
152390075Sobrien  /* Return the address of the result block.  */
1524122190Skan  result = copy_addr_to_reg (XEXP (result, 0));
1525132727Skan  return convert_memory_address (ptr_mode, result);
152690075Sobrien}
152790075Sobrien
152890075Sobrien/* Perform an untyped return.  */
152990075Sobrien
153090075Sobrienstatic void
1531132727Skanexpand_builtin_return (rtx result)
153290075Sobrien{
153390075Sobrien  int size, align, regno;
153490075Sobrien  enum machine_mode mode;
153590075Sobrien  rtx reg;
153690075Sobrien  rtx call_fusage = 0;
153790075Sobrien
1538132727Skan  result = convert_memory_address (Pmode, result);
153990075Sobrien
154090075Sobrien  apply_result_size ();
154190075Sobrien  result = gen_rtx_MEM (BLKmode, result);
154290075Sobrien
154390075Sobrien#ifdef HAVE_untyped_return
154490075Sobrien  if (HAVE_untyped_return)
154590075Sobrien    {
154690075Sobrien      emit_jump_insn (gen_untyped_return (result, result_vector (0, result)));
154790075Sobrien      emit_barrier ();
154890075Sobrien      return;
154990075Sobrien    }
155090075Sobrien#endif
155190075Sobrien
155290075Sobrien  /* Restore the return value and note that each value is used.  */
155390075Sobrien  size = 0;
155490075Sobrien  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
155590075Sobrien    if ((mode = apply_result_mode[regno]) != VOIDmode)
155690075Sobrien      {
155790075Sobrien	align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
155890075Sobrien	if (size % align != 0)
155990075Sobrien	  size = CEIL (size, align) * align;
156090075Sobrien	reg = gen_rtx_REG (mode, INCOMING_REGNO (regno));
156190075Sobrien	emit_move_insn (reg, adjust_address (result, mode, size));
156290075Sobrien
156390075Sobrien	push_to_sequence (call_fusage);
156490075Sobrien	emit_insn (gen_rtx_USE (VOIDmode, reg));
156590075Sobrien	call_fusage = get_insns ();
156690075Sobrien	end_sequence ();
156790075Sobrien	size += GET_MODE_SIZE (mode);
156890075Sobrien      }
156990075Sobrien
157090075Sobrien  /* Put the USE insns before the return.  */
1571117404Skan  emit_insn (call_fusage);
157290075Sobrien
157390075Sobrien  /* Return whatever values was restored by jumping directly to the end
157490075Sobrien     of the function.  */
1575132727Skan  expand_naked_return ();
157690075Sobrien}
157790075Sobrien
157890075Sobrien/* Used by expand_builtin_classify_type and fold_builtin_classify_type.  */
157990075Sobrien
158090075Sobrienstatic enum type_class
1581132727Skantype_to_class (tree type)
158290075Sobrien{
158390075Sobrien  switch (TREE_CODE (type))
158490075Sobrien    {
158590075Sobrien    case VOID_TYPE:	   return void_type_class;
158690075Sobrien    case INTEGER_TYPE:	   return integer_type_class;
158790075Sobrien    case ENUMERAL_TYPE:	   return enumeral_type_class;
158890075Sobrien    case BOOLEAN_TYPE:	   return boolean_type_class;
158990075Sobrien    case POINTER_TYPE:	   return pointer_type_class;
159090075Sobrien    case REFERENCE_TYPE:   return reference_type_class;
159190075Sobrien    case OFFSET_TYPE:	   return offset_type_class;
159290075Sobrien    case REAL_TYPE:	   return real_type_class;
159390075Sobrien    case COMPLEX_TYPE:	   return complex_type_class;
159490075Sobrien    case FUNCTION_TYPE:	   return function_type_class;
159590075Sobrien    case METHOD_TYPE:	   return method_type_class;
159690075Sobrien    case RECORD_TYPE:	   return record_type_class;
159790075Sobrien    case UNION_TYPE:
159890075Sobrien    case QUAL_UNION_TYPE:  return union_type_class;
159990075Sobrien    case ARRAY_TYPE:	   return (TYPE_STRING_FLAG (type)
160090075Sobrien				   ? string_type_class : array_type_class);
160190075Sobrien    case LANG_TYPE:	   return lang_type_class;
160290075Sobrien    default:		   return no_type_class;
160390075Sobrien    }
160490075Sobrien}
160590075Sobrien
160690075Sobrien/* Expand a call to __builtin_classify_type with arguments found in
160790075Sobrien   ARGLIST.  */
160890075Sobrien
160990075Sobrienstatic rtx
1610132727Skanexpand_builtin_classify_type (tree arglist)
161190075Sobrien{
161290075Sobrien  if (arglist != 0)
161390075Sobrien    return GEN_INT (type_to_class (TREE_TYPE (TREE_VALUE (arglist))));
161490075Sobrien  return GEN_INT (no_type_class);
161590075Sobrien}
161690075Sobrien
1617132727Skan/* This helper macro, meant to be used in mathfn_built_in below,
1618132727Skan   determines which among a set of three builtin math functions is
1619132727Skan   appropriate for a given type mode.  The `F' and `L' cases are
1620132727Skan   automatically generated from the `double' case.  */
1621132727Skan#define CASE_MATHFN(BUILT_IN_MATHFN) \
1622132727Skan  case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \
1623132727Skan  fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \
1624132727Skan  fcodel = BUILT_IN_MATHFN##L ; break;
1625132727Skan
1626132727Skan/* Return mathematic function equivalent to FN but operating directly
1627132727Skan   on TYPE, if available.  If we can't do the conversion, return zero.  */
1628132727Skantree
1629132727Skanmathfn_built_in (tree type, enum built_in_function fn)
1630132727Skan{
1631132727Skan  enum built_in_function fcode, fcodef, fcodel;
1632132727Skan
1633132727Skan  switch (fn)
1634132727Skan    {
1635132727Skan      CASE_MATHFN (BUILT_IN_ACOS)
1636132727Skan      CASE_MATHFN (BUILT_IN_ACOSH)
1637132727Skan      CASE_MATHFN (BUILT_IN_ASIN)
1638132727Skan      CASE_MATHFN (BUILT_IN_ASINH)
1639132727Skan      CASE_MATHFN (BUILT_IN_ATAN)
1640132727Skan      CASE_MATHFN (BUILT_IN_ATAN2)
1641132727Skan      CASE_MATHFN (BUILT_IN_ATANH)
1642132727Skan      CASE_MATHFN (BUILT_IN_CBRT)
1643132727Skan      CASE_MATHFN (BUILT_IN_CEIL)
1644132727Skan      CASE_MATHFN (BUILT_IN_COPYSIGN)
1645132727Skan      CASE_MATHFN (BUILT_IN_COS)
1646132727Skan      CASE_MATHFN (BUILT_IN_COSH)
1647132727Skan      CASE_MATHFN (BUILT_IN_DREM)
1648132727Skan      CASE_MATHFN (BUILT_IN_ERF)
1649132727Skan      CASE_MATHFN (BUILT_IN_ERFC)
1650132727Skan      CASE_MATHFN (BUILT_IN_EXP)
1651132727Skan      CASE_MATHFN (BUILT_IN_EXP10)
1652132727Skan      CASE_MATHFN (BUILT_IN_EXP2)
1653132727Skan      CASE_MATHFN (BUILT_IN_EXPM1)
1654132727Skan      CASE_MATHFN (BUILT_IN_FABS)
1655132727Skan      CASE_MATHFN (BUILT_IN_FDIM)
1656132727Skan      CASE_MATHFN (BUILT_IN_FLOOR)
1657132727Skan      CASE_MATHFN (BUILT_IN_FMA)
1658132727Skan      CASE_MATHFN (BUILT_IN_FMAX)
1659132727Skan      CASE_MATHFN (BUILT_IN_FMIN)
1660132727Skan      CASE_MATHFN (BUILT_IN_FMOD)
1661132727Skan      CASE_MATHFN (BUILT_IN_FREXP)
1662132727Skan      CASE_MATHFN (BUILT_IN_GAMMA)
1663132727Skan      CASE_MATHFN (BUILT_IN_HUGE_VAL)
1664132727Skan      CASE_MATHFN (BUILT_IN_HYPOT)
1665132727Skan      CASE_MATHFN (BUILT_IN_ILOGB)
1666132727Skan      CASE_MATHFN (BUILT_IN_INF)
1667132727Skan      CASE_MATHFN (BUILT_IN_J0)
1668132727Skan      CASE_MATHFN (BUILT_IN_J1)
1669132727Skan      CASE_MATHFN (BUILT_IN_JN)
1670169699Skan      CASE_MATHFN (BUILT_IN_LCEIL)
1671132727Skan      CASE_MATHFN (BUILT_IN_LDEXP)
1672169699Skan      CASE_MATHFN (BUILT_IN_LFLOOR)
1673132727Skan      CASE_MATHFN (BUILT_IN_LGAMMA)
1674169699Skan      CASE_MATHFN (BUILT_IN_LLCEIL)
1675169699Skan      CASE_MATHFN (BUILT_IN_LLFLOOR)
1676132727Skan      CASE_MATHFN (BUILT_IN_LLRINT)
1677132727Skan      CASE_MATHFN (BUILT_IN_LLROUND)
1678132727Skan      CASE_MATHFN (BUILT_IN_LOG)
1679132727Skan      CASE_MATHFN (BUILT_IN_LOG10)
1680132727Skan      CASE_MATHFN (BUILT_IN_LOG1P)
1681132727Skan      CASE_MATHFN (BUILT_IN_LOG2)
1682132727Skan      CASE_MATHFN (BUILT_IN_LOGB)
1683132727Skan      CASE_MATHFN (BUILT_IN_LRINT)
1684132727Skan      CASE_MATHFN (BUILT_IN_LROUND)
1685132727Skan      CASE_MATHFN (BUILT_IN_MODF)
1686132727Skan      CASE_MATHFN (BUILT_IN_NAN)
1687132727Skan      CASE_MATHFN (BUILT_IN_NANS)
1688132727Skan      CASE_MATHFN (BUILT_IN_NEARBYINT)
1689132727Skan      CASE_MATHFN (BUILT_IN_NEXTAFTER)
1690132727Skan      CASE_MATHFN (BUILT_IN_NEXTTOWARD)
1691132727Skan      CASE_MATHFN (BUILT_IN_POW)
1692169699Skan      CASE_MATHFN (BUILT_IN_POWI)
1693132727Skan      CASE_MATHFN (BUILT_IN_POW10)
1694132727Skan      CASE_MATHFN (BUILT_IN_REMAINDER)
1695132727Skan      CASE_MATHFN (BUILT_IN_REMQUO)
1696132727Skan      CASE_MATHFN (BUILT_IN_RINT)
1697132727Skan      CASE_MATHFN (BUILT_IN_ROUND)
1698132727Skan      CASE_MATHFN (BUILT_IN_SCALB)
1699132727Skan      CASE_MATHFN (BUILT_IN_SCALBLN)
1700132727Skan      CASE_MATHFN (BUILT_IN_SCALBN)
1701132727Skan      CASE_MATHFN (BUILT_IN_SIGNIFICAND)
1702132727Skan      CASE_MATHFN (BUILT_IN_SIN)
1703132727Skan      CASE_MATHFN (BUILT_IN_SINCOS)
1704132727Skan      CASE_MATHFN (BUILT_IN_SINH)
1705132727Skan      CASE_MATHFN (BUILT_IN_SQRT)
1706132727Skan      CASE_MATHFN (BUILT_IN_TAN)
1707132727Skan      CASE_MATHFN (BUILT_IN_TANH)
1708132727Skan      CASE_MATHFN (BUILT_IN_TGAMMA)
1709132727Skan      CASE_MATHFN (BUILT_IN_TRUNC)
1710132727Skan      CASE_MATHFN (BUILT_IN_Y0)
1711132727Skan      CASE_MATHFN (BUILT_IN_Y1)
1712132727Skan      CASE_MATHFN (BUILT_IN_YN)
1713132727Skan
1714132727Skan      default:
1715132727Skan	return 0;
1716132727Skan      }
1717132727Skan
1718169699Skan  if (TYPE_MAIN_VARIANT (type) == double_type_node)
1719132727Skan    return implicit_built_in_decls[fcode];
1720169699Skan  else if (TYPE_MAIN_VARIANT (type) == float_type_node)
1721132727Skan    return implicit_built_in_decls[fcodef];
1722169699Skan  else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
1723132727Skan    return implicit_built_in_decls[fcodel];
1724132727Skan  else
1725132727Skan    return 0;
1726132727Skan}
1727132727Skan
1728132727Skan/* If errno must be maintained, expand the RTL to check if the result,
1729132727Skan   TARGET, of a built-in function call, EXP, is NaN, and if so set
1730132727Skan   errno to EDOM.  */
1731132727Skan
1732132727Skanstatic void
1733132727Skanexpand_errno_check (tree exp, rtx target)
1734132727Skan{
1735132727Skan  rtx lab = gen_label_rtx ();
1736132727Skan
1737132727Skan  /* Test the result; if it is NaN, set errno=EDOM because
1738132727Skan     the argument was not in the domain.  */
1739132727Skan  emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target),
1740132727Skan			   0, lab);
1741132727Skan
1742132727Skan#ifdef TARGET_EDOM
1743132727Skan  /* If this built-in doesn't throw an exception, set errno directly.  */
1744132727Skan  if (TREE_NOTHROW (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
1745132727Skan    {
1746132727Skan#ifdef GEN_ERRNO_RTX
1747132727Skan      rtx errno_rtx = GEN_ERRNO_RTX;
1748132727Skan#else
1749132727Skan      rtx errno_rtx
1750132727Skan	  = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
1751132727Skan#endif
1752132727Skan      emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
1753132727Skan      emit_label (lab);
1754132727Skan      return;
1755132727Skan    }
1756132727Skan#endif
1757132727Skan
1758132727Skan  /* We can't set errno=EDOM directly; let the library call do it.
1759132727Skan     Pop the arguments right away in case the call gets deleted.  */
1760132727Skan  NO_DEFER_POP;
1761132727Skan  expand_call (exp, target, 0);
1762132727Skan  OK_DEFER_POP;
1763132727Skan  emit_label (lab);
1764132727Skan}
1765132727Skan
1766132727Skan
1767169699Skan/* Expand a call to one of the builtin math functions (sqrt, exp, or log).
176890075Sobrien   Return 0 if a normal call should be emitted rather than expanding the
176990075Sobrien   function in-line.  EXP is the expression that is a call to the builtin
177090075Sobrien   function; if convenient, the result should be placed in TARGET.
177190075Sobrien   SUBTARGET may be used as the target for computing one of EXP's operands.  */
177290075Sobrien
177390075Sobrienstatic rtx
1774132727Skanexpand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
177590075Sobrien{
177690075Sobrien  optab builtin_optab;
1777132727Skan  rtx op0, insns, before_call;
1778132727Skan  tree fndecl = get_callee_fndecl (exp);
177990075Sobrien  tree arglist = TREE_OPERAND (exp, 1);
1780132727Skan  enum machine_mode mode;
1781132727Skan  bool errno_set = false;
1782132727Skan  tree arg, narg;
178390075Sobrien
178490075Sobrien  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
178590075Sobrien    return 0;
178690075Sobrien
1787132727Skan  arg = TREE_VALUE (arglist);
178890075Sobrien
178990075Sobrien  switch (DECL_FUNCTION_CODE (fndecl))
179090075Sobrien    {
1791169699Skan    CASE_FLT_FN (BUILT_IN_SQRT):
1792132727Skan      errno_set = ! tree_expr_nonnegative_p (arg);
1793132727Skan      builtin_optab = sqrt_optab;
1794132727Skan      break;
1795169699Skan    CASE_FLT_FN (BUILT_IN_EXP):
1796132727Skan      errno_set = true; builtin_optab = exp_optab; break;
1797169699Skan    CASE_FLT_FN (BUILT_IN_EXP10):
1798169699Skan    CASE_FLT_FN (BUILT_IN_POW10):
1799169699Skan      errno_set = true; builtin_optab = exp10_optab; break;
1800169699Skan    CASE_FLT_FN (BUILT_IN_EXP2):
1801169699Skan      errno_set = true; builtin_optab = exp2_optab; break;
1802169699Skan    CASE_FLT_FN (BUILT_IN_EXPM1):
1803169699Skan      errno_set = true; builtin_optab = expm1_optab; break;
1804169699Skan    CASE_FLT_FN (BUILT_IN_LOGB):
1805169699Skan      errno_set = true; builtin_optab = logb_optab; break;
1806169699Skan    CASE_FLT_FN (BUILT_IN_ILOGB):
1807169699Skan      errno_set = true; builtin_optab = ilogb_optab; break;
1808169699Skan    CASE_FLT_FN (BUILT_IN_LOG):
1809132727Skan      errno_set = true; builtin_optab = log_optab; break;
1810169699Skan    CASE_FLT_FN (BUILT_IN_LOG10):
1811169699Skan      errno_set = true; builtin_optab = log10_optab; break;
1812169699Skan    CASE_FLT_FN (BUILT_IN_LOG2):
1813169699Skan      errno_set = true; builtin_optab = log2_optab; break;
1814169699Skan    CASE_FLT_FN (BUILT_IN_LOG1P):
1815169699Skan      errno_set = true; builtin_optab = log1p_optab; break;
1816169699Skan    CASE_FLT_FN (BUILT_IN_ASIN):
1817169699Skan      builtin_optab = asin_optab; break;
1818169699Skan    CASE_FLT_FN (BUILT_IN_ACOS):
1819169699Skan      builtin_optab = acos_optab; break;
1820169699Skan    CASE_FLT_FN (BUILT_IN_TAN):
1821132727Skan      builtin_optab = tan_optab; break;
1822169699Skan    CASE_FLT_FN (BUILT_IN_ATAN):
1823132727Skan      builtin_optab = atan_optab; break;
1824169699Skan    CASE_FLT_FN (BUILT_IN_FLOOR):
1825132727Skan      builtin_optab = floor_optab; break;
1826169699Skan    CASE_FLT_FN (BUILT_IN_CEIL):
1827132727Skan      builtin_optab = ceil_optab; break;
1828169699Skan    CASE_FLT_FN (BUILT_IN_TRUNC):
1829132727Skan      builtin_optab = btrunc_optab; break;
1830169699Skan    CASE_FLT_FN (BUILT_IN_ROUND):
1831132727Skan      builtin_optab = round_optab; break;
1832169699Skan    CASE_FLT_FN (BUILT_IN_NEARBYINT):
1833132727Skan      builtin_optab = nearbyint_optab; break;
1834169699Skan    CASE_FLT_FN (BUILT_IN_RINT):
1835169699Skan      builtin_optab = rint_optab; break;
1836169699Skan    CASE_FLT_FN (BUILT_IN_LRINT):
1837169699Skan    CASE_FLT_FN (BUILT_IN_LLRINT):
1838169699Skan      builtin_optab = lrint_optab; break;
1839117404Skan    default:
1840169699Skan      gcc_unreachable ();
184190075Sobrien    }
184290075Sobrien
1843132727Skan  /* Make a suitable register to place result in.  */
1844132727Skan  mode = TYPE_MODE (TREE_TYPE (exp));
184590075Sobrien
1846132727Skan  if (! flag_errno_math || ! HONOR_NANS (mode))
1847132727Skan    errno_set = false;
1848132727Skan
1849132727Skan  /* Before working hard, check whether the instruction is available.  */
1850132727Skan  if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
185190075Sobrien    {
1852132727Skan      target = gen_reg_rtx (mode);
1853132727Skan
1854132727Skan      /* Wrap the computation of the argument in a SAVE_EXPR, as we may
1855132727Skan	 need to expand the argument again.  This way, we will not perform
1856132727Skan	 side-effects more the once.  */
1857169699Skan      narg = builtin_save_expr (arg);
1858132727Skan      if (narg != arg)
1859132727Skan	{
1860146906Skan	  arg = narg;
1861132727Skan	  arglist = build_tree_list (NULL_TREE, arg);
1862132727Skan	  exp = build_function_call_expr (fndecl, arglist);
1863132727Skan	}
1864132727Skan
1865132727Skan      op0 = expand_expr (arg, subtarget, VOIDmode, 0);
1866132727Skan
1867132727Skan      start_sequence ();
1868132727Skan
1869132727Skan      /* Compute into TARGET.
1870132727Skan	 Set TARGET to wherever the result comes back.  */
1871132727Skan      target = expand_unop (mode, builtin_optab, op0, target, 0);
1872132727Skan
1873132727Skan      if (target != 0)
1874132727Skan	{
1875132727Skan	  if (errno_set)
1876132727Skan	    expand_errno_check (exp, target);
1877132727Skan
1878132727Skan	  /* Output the entire sequence.  */
1879132727Skan	  insns = get_insns ();
1880132727Skan	  end_sequence ();
1881132727Skan	  emit_insn (insns);
1882132727Skan	  return target;
1883132727Skan	}
1884132727Skan
1885132727Skan      /* If we were unable to expand via the builtin, stop the sequence
1886132727Skan	 (without outputting the insns) and call to the library function
1887132727Skan	 with the stabilized argument list.  */
188890075Sobrien      end_sequence ();
188990075Sobrien    }
189090075Sobrien
1891132727Skan  before_call = get_last_insn ();
189290075Sobrien
1893132727Skan  target = expand_call (exp, target, target == const0_rtx);
1894132727Skan
1895132727Skan  /* If this is a sqrt operation and we don't care about errno, try to
1896132727Skan     attach a REG_EQUAL note with a SQRT rtx to the emitted libcall.
1897132727Skan     This allows the semantics of the libcall to be visible to the RTL
1898132727Skan     optimizers.  */
1899132727Skan  if (builtin_optab == sqrt_optab && !errno_set)
190090075Sobrien    {
1901132727Skan      /* Search backwards through the insns emitted by expand_call looking
1902132727Skan	 for the instruction with the REG_RETVAL note.  */
1903132727Skan      rtx last = get_last_insn ();
1904132727Skan      while (last != before_call)
1905132727Skan	{
1906132727Skan	  if (find_reg_note (last, REG_RETVAL, NULL))
1907132727Skan	    {
1908132727Skan	      rtx note = find_reg_note (last, REG_EQUAL, NULL);
1909132727Skan	      /* Check that the REQ_EQUAL note is an EXPR_LIST with
1910132727Skan		 two elements, i.e. symbol_ref(sqrt) and the operand.  */
1911132727Skan	      if (note
1912132727Skan		  && GET_CODE (note) == EXPR_LIST
1913132727Skan		  && GET_CODE (XEXP (note, 0)) == EXPR_LIST
1914132727Skan		  && XEXP (XEXP (note, 0), 1) != NULL_RTX
1915132727Skan		  && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX)
1916132727Skan		{
1917132727Skan		  rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
1918132727Skan		  /* Check operand is a register with expected mode.  */
1919132727Skan		  if (operand
1920169699Skan		      && REG_P (operand)
1921132727Skan		      && GET_MODE (operand) == mode)
1922132727Skan		    {
1923132727Skan		      /* Replace the REG_EQUAL note with a SQRT rtx.  */
1924132727Skan		      rtx equiv = gen_rtx_SQRT (mode, operand);
1925132727Skan		      set_unique_reg_note (last, REG_EQUAL, equiv);
1926132727Skan		    }
1927132727Skan		}
1928132727Skan	      break;
1929132727Skan	    }
1930132727Skan	  last = PREV_INSN (last);
1931132727Skan	}
1932132727Skan    }
193390075Sobrien
1934132727Skan  return target;
1935132727Skan}
193690075Sobrien
1937132727Skan/* Expand a call to the builtin binary math functions (pow and atan2).
1938132727Skan   Return 0 if a normal call should be emitted rather than expanding the
1939132727Skan   function in-line.  EXP is the expression that is a call to the builtin
1940132727Skan   function; if convenient, the result should be placed in TARGET.
1941132727Skan   SUBTARGET may be used as the target for computing one of EXP's
1942132727Skan   operands.  */
194390075Sobrien
1944132727Skanstatic rtx
1945132727Skanexpand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
1946132727Skan{
1947132727Skan  optab builtin_optab;
1948132727Skan  rtx op0, op1, insns;
1949169699Skan  int op1_type = REAL_TYPE;
1950132727Skan  tree fndecl = get_callee_fndecl (exp);
1951132727Skan  tree arglist = TREE_OPERAND (exp, 1);
1952132727Skan  tree arg0, arg1, temp, narg;
1953132727Skan  enum machine_mode mode;
1954132727Skan  bool errno_set = true;
1955132727Skan  bool stable = true;
195690075Sobrien
1957169699Skan  if ((DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXP)
1958169699Skan      || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPF)
1959169699Skan      || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPL))
1960169699Skan    op1_type = INTEGER_TYPE;
1961169699Skan
1962169699Skan  if (!validate_arglist (arglist, REAL_TYPE, op1_type, VOID_TYPE))
1963132727Skan    return 0;
196490075Sobrien
1965132727Skan  arg0 = TREE_VALUE (arglist);
1966132727Skan  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
1967132727Skan
1968132727Skan  switch (DECL_FUNCTION_CODE (fndecl))
1969132727Skan    {
1970169699Skan    CASE_FLT_FN (BUILT_IN_POW):
1971132727Skan      builtin_optab = pow_optab; break;
1972169699Skan    CASE_FLT_FN (BUILT_IN_ATAN2):
1973132727Skan      builtin_optab = atan2_optab; break;
1974169699Skan    CASE_FLT_FN (BUILT_IN_LDEXP):
1975169699Skan      builtin_optab = ldexp_optab; break;
1976169699Skan    CASE_FLT_FN (BUILT_IN_FMOD):
1977169699Skan      builtin_optab = fmod_optab; break;
1978169699Skan    CASE_FLT_FN (BUILT_IN_DREM):
1979169699Skan      builtin_optab = drem_optab; break;
1980132727Skan    default:
1981169699Skan      gcc_unreachable ();
198290075Sobrien    }
198390075Sobrien
1984132727Skan  /* Make a suitable register to place result in.  */
1985132727Skan  mode = TYPE_MODE (TREE_TYPE (exp));
1986132727Skan
1987132727Skan  /* Before working hard, check whether the instruction is available.  */
1988132727Skan  if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
1989132727Skan    return 0;
1990132727Skan
1991132727Skan  target = gen_reg_rtx (mode);
1992132727Skan
1993132727Skan  if (! flag_errno_math || ! HONOR_NANS (mode))
1994132727Skan    errno_set = false;
1995132727Skan
1996169699Skan  /* Always stabilize the argument list.  */
1997169699Skan  narg = builtin_save_expr (arg1);
1998132727Skan  if (narg != arg1)
1999132727Skan    {
2000146906Skan      arg1 = narg;
2001132727Skan      temp = build_tree_list (NULL_TREE, narg);
2002132727Skan      stable = false;
2003132727Skan    }
2004132727Skan  else
2005132727Skan    temp = TREE_CHAIN (arglist);
2006132727Skan
2007169699Skan  narg = builtin_save_expr (arg0);
2008132727Skan  if (narg != arg0)
2009132727Skan    {
2010146906Skan      arg0 = narg;
2011132727Skan      arglist = tree_cons (NULL_TREE, narg, temp);
2012132727Skan      stable = false;
2013132727Skan    }
2014132727Skan  else if (! stable)
2015132727Skan    arglist = tree_cons (NULL_TREE, arg0, temp);
2016132727Skan
2017132727Skan  if (! stable)
2018132727Skan    exp = build_function_call_expr (fndecl, arglist);
2019132727Skan
2020169699Skan  op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL);
2021169699Skan  op1 = expand_normal (arg1);
2022132727Skan
2023132727Skan  start_sequence ();
2024132727Skan
2025132727Skan  /* Compute into TARGET.
2026132727Skan     Set TARGET to wherever the result comes back.  */
2027132727Skan  target = expand_binop (mode, builtin_optab, op0, op1,
2028132727Skan			 target, 0, OPTAB_DIRECT);
2029132727Skan
2030132727Skan  /* If we were unable to expand via the builtin, stop the sequence
2031132727Skan     (without outputting the insns) and call to the library function
2032132727Skan     with the stabilized argument list.  */
2033132727Skan  if (target == 0)
2034132727Skan    {
2035132727Skan      end_sequence ();
2036132727Skan      return expand_call (exp, target, target == const0_rtx);
2037132727Skan    }
2038132727Skan
2039132727Skan  if (errno_set)
2040132727Skan    expand_errno_check (exp, target);
2041132727Skan
204290075Sobrien  /* Output the entire sequence.  */
204390075Sobrien  insns = get_insns ();
204490075Sobrien  end_sequence ();
2045117404Skan  emit_insn (insns);
204690075Sobrien
204790075Sobrien  return target;
204890075Sobrien}
204990075Sobrien
2050169699Skan/* Expand a call to the builtin sin and cos math functions.
2051169699Skan   Return 0 if a normal call should be emitted rather than expanding the
2052169699Skan   function in-line.  EXP is the expression that is a call to the builtin
2053169699Skan   function; if convenient, the result should be placed in TARGET.
2054169699Skan   SUBTARGET may be used as the target for computing one of EXP's
2055169699Skan   operands.  */
2056169699Skan
2057169699Skanstatic rtx
2058169699Skanexpand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget)
2059169699Skan{
2060169699Skan  optab builtin_optab;
2061169699Skan  rtx op0, insns;
2062169699Skan  tree fndecl = get_callee_fndecl (exp);
2063169699Skan  tree arglist = TREE_OPERAND (exp, 1);
2064169699Skan  enum machine_mode mode;
2065169699Skan  tree arg, narg;
2066169699Skan
2067169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
2068169699Skan    return 0;
2069169699Skan
2070169699Skan  arg = TREE_VALUE (arglist);
2071169699Skan
2072169699Skan  switch (DECL_FUNCTION_CODE (fndecl))
2073169699Skan    {
2074169699Skan    CASE_FLT_FN (BUILT_IN_SIN):
2075169699Skan    CASE_FLT_FN (BUILT_IN_COS):
2076169699Skan      builtin_optab = sincos_optab; break;
2077169699Skan    default:
2078169699Skan      gcc_unreachable ();
2079169699Skan    }
2080169699Skan
2081169699Skan  /* Make a suitable register to place result in.  */
2082169699Skan  mode = TYPE_MODE (TREE_TYPE (exp));
2083169699Skan
2084169699Skan  /* Check if sincos insn is available, otherwise fallback
2085169699Skan     to sin or cos insn.  */
2086169699Skan  if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) {
2087169699Skan    switch (DECL_FUNCTION_CODE (fndecl))
2088169699Skan      {
2089169699Skan      CASE_FLT_FN (BUILT_IN_SIN):
2090169699Skan	builtin_optab = sin_optab; break;
2091169699Skan      CASE_FLT_FN (BUILT_IN_COS):
2092169699Skan	builtin_optab = cos_optab; break;
2093169699Skan      default:
2094169699Skan	gcc_unreachable ();
2095169699Skan      }
2096169699Skan  }
2097169699Skan
2098169699Skan  /* Before working hard, check whether the instruction is available.  */
2099169699Skan  if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
2100169699Skan    {
2101169699Skan      target = gen_reg_rtx (mode);
2102169699Skan
2103169699Skan      /* Wrap the computation of the argument in a SAVE_EXPR, as we may
2104169699Skan	 need to expand the argument again.  This way, we will not perform
2105169699Skan	 side-effects more the once.  */
2106169699Skan      narg = save_expr (arg);
2107169699Skan      if (narg != arg)
2108169699Skan	{
2109169699Skan	  arg = narg;
2110169699Skan	  arglist = build_tree_list (NULL_TREE, arg);
2111169699Skan	  exp = build_function_call_expr (fndecl, arglist);
2112169699Skan	}
2113169699Skan
2114169699Skan      op0 = expand_expr (arg, subtarget, VOIDmode, 0);
2115169699Skan
2116169699Skan      start_sequence ();
2117169699Skan
2118169699Skan      /* Compute into TARGET.
2119169699Skan	 Set TARGET to wherever the result comes back.  */
2120169699Skan      if (builtin_optab == sincos_optab)
2121169699Skan	{
2122169699Skan	  int result;
2123169699Skan
2124169699Skan	  switch (DECL_FUNCTION_CODE (fndecl))
2125169699Skan	    {
2126169699Skan	    CASE_FLT_FN (BUILT_IN_SIN):
2127169699Skan	      result = expand_twoval_unop (builtin_optab, op0, 0, target, 0);
2128169699Skan	      break;
2129169699Skan	    CASE_FLT_FN (BUILT_IN_COS):
2130169699Skan	      result = expand_twoval_unop (builtin_optab, op0, target, 0, 0);
2131169699Skan	      break;
2132169699Skan	    default:
2133169699Skan	      gcc_unreachable ();
2134169699Skan	    }
2135169699Skan	  gcc_assert (result);
2136169699Skan	}
2137169699Skan      else
2138169699Skan	{
2139169699Skan	  target = expand_unop (mode, builtin_optab, op0, target, 0);
2140169699Skan	}
2141169699Skan
2142169699Skan      if (target != 0)
2143169699Skan	{
2144169699Skan	  /* Output the entire sequence.  */
2145169699Skan	  insns = get_insns ();
2146169699Skan	  end_sequence ();
2147169699Skan	  emit_insn (insns);
2148169699Skan	  return target;
2149169699Skan	}
2150169699Skan
2151169699Skan      /* If we were unable to expand via the builtin, stop the sequence
2152169699Skan	 (without outputting the insns) and call to the library function
2153169699Skan	 with the stabilized argument list.  */
2154169699Skan      end_sequence ();
2155169699Skan    }
2156169699Skan
2157169699Skan  target = expand_call (exp, target, target == const0_rtx);
2158169699Skan
2159169699Skan  return target;
2160169699Skan}
2161169699Skan
2162169699Skan/* Expand a call to the builtin sincos math function.
2163169699Skan   Return 0 if a normal call should be emitted rather than expanding the
2164169699Skan   function in-line.  EXP is the expression that is a call to the builtin
2165169699Skan   function.  */
2166169699Skan
2167169699Skanstatic rtx
2168169699Skanexpand_builtin_sincos (tree exp)
2169169699Skan{
2170169699Skan  rtx op0, op1, op2, target1, target2;
2171169699Skan  tree arglist = TREE_OPERAND (exp, 1);
2172169699Skan  enum machine_mode mode;
2173169699Skan  tree arg, sinp, cosp;
2174169699Skan  int result;
2175169699Skan
2176169699Skan  if (!validate_arglist (arglist, REAL_TYPE,
2177169699Skan			 POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
2178169699Skan    return 0;
2179169699Skan
2180169699Skan  arg = TREE_VALUE (arglist);
2181169699Skan  sinp = TREE_VALUE (TREE_CHAIN (arglist));
2182169699Skan  cosp = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
2183169699Skan
2184169699Skan  /* Make a suitable register to place result in.  */
2185169699Skan  mode = TYPE_MODE (TREE_TYPE (arg));
2186169699Skan
2187169699Skan  /* Check if sincos insn is available, otherwise emit the call.  */
2188169699Skan  if (sincos_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
2189169699Skan    return NULL_RTX;
2190169699Skan
2191169699Skan  target1 = gen_reg_rtx (mode);
2192169699Skan  target2 = gen_reg_rtx (mode);
2193169699Skan
2194169699Skan  op0 = expand_normal (arg);
2195169699Skan  op1 = expand_normal (build_fold_indirect_ref (sinp));
2196169699Skan  op2 = expand_normal (build_fold_indirect_ref (cosp));
2197169699Skan
2198169699Skan  /* Compute into target1 and target2.
2199169699Skan     Set TARGET to wherever the result comes back.  */
2200169699Skan  result = expand_twoval_unop (sincos_optab, op0, target2, target1, 0);
2201169699Skan  gcc_assert (result);
2202169699Skan
2203169699Skan  /* Move target1 and target2 to the memory locations indicated
2204169699Skan     by op1 and op2.  */
2205169699Skan  emit_move_insn (op1, target1);
2206169699Skan  emit_move_insn (op2, target2);
2207169699Skan
2208169699Skan  return const0_rtx;
2209169699Skan}
2210169699Skan
2211169699Skan/* Expand a call to one of the builtin rounding functions (lfloor).
2212169699Skan   If expanding via optab fails, lower expression to (int)(floor(x)).
2213169699Skan   EXP is the expression that is a call to the builtin function;
2214169699Skan   if convenient, the result should be placed in TARGET.  SUBTARGET may
2215169699Skan   be used as the target for computing one of EXP's operands.  */
2216169699Skan
2217169699Skanstatic rtx
2218169699Skanexpand_builtin_int_roundingfn (tree exp, rtx target, rtx subtarget)
2219169699Skan{
2220169699Skan  optab builtin_optab;
2221169699Skan  rtx op0, insns, tmp;
2222169699Skan  tree fndecl = get_callee_fndecl (exp);
2223169699Skan  tree arglist = TREE_OPERAND (exp, 1);
2224169699Skan  enum built_in_function fallback_fn;
2225169699Skan  tree fallback_fndecl;
2226169699Skan  enum machine_mode mode;
2227169699Skan  tree arg, narg;
2228169699Skan
2229169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
2230169699Skan    gcc_unreachable ();
2231169699Skan
2232169699Skan  arg = TREE_VALUE (arglist);
2233169699Skan
2234169699Skan  switch (DECL_FUNCTION_CODE (fndecl))
2235169699Skan    {
2236169699Skan    CASE_FLT_FN (BUILT_IN_LCEIL):
2237169699Skan    CASE_FLT_FN (BUILT_IN_LLCEIL):
2238169699Skan      builtin_optab = lceil_optab;
2239169699Skan      fallback_fn = BUILT_IN_CEIL;
2240169699Skan      break;
2241169699Skan
2242169699Skan    CASE_FLT_FN (BUILT_IN_LFLOOR):
2243169699Skan    CASE_FLT_FN (BUILT_IN_LLFLOOR):
2244169699Skan      builtin_optab = lfloor_optab;
2245169699Skan      fallback_fn = BUILT_IN_FLOOR;
2246169699Skan      break;
2247169699Skan
2248169699Skan    default:
2249169699Skan      gcc_unreachable ();
2250169699Skan    }
2251169699Skan
2252169699Skan  /* Make a suitable register to place result in.  */
2253169699Skan  mode = TYPE_MODE (TREE_TYPE (exp));
2254169699Skan
2255169699Skan  /* Before working hard, check whether the instruction is available.  */
2256169699Skan  if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
2257169699Skan    {
2258169699Skan      target = gen_reg_rtx (mode);
2259169699Skan
2260169699Skan      /* Wrap the computation of the argument in a SAVE_EXPR, as we may
2261169699Skan	 need to expand the argument again.  This way, we will not perform
2262169699Skan	 side-effects more the once.  */
2263169699Skan      narg = builtin_save_expr (arg);
2264169699Skan      if (narg != arg)
2265169699Skan	{
2266169699Skan	  arg = narg;
2267169699Skan	  arglist = build_tree_list (NULL_TREE, arg);
2268169699Skan	  exp = build_function_call_expr (fndecl, arglist);
2269169699Skan	}
2270169699Skan
2271169699Skan      op0 = expand_expr (arg, subtarget, VOIDmode, 0);
2272169699Skan
2273169699Skan      start_sequence ();
2274169699Skan
2275169699Skan      /* Compute into TARGET.
2276169699Skan	 Set TARGET to wherever the result comes back.  */
2277169699Skan      target = expand_unop (mode, builtin_optab, op0, target, 0);
2278169699Skan
2279169699Skan      if (target != 0)
2280169699Skan	{
2281169699Skan	  /* Output the entire sequence.  */
2282169699Skan	  insns = get_insns ();
2283169699Skan	  end_sequence ();
2284169699Skan	  emit_insn (insns);
2285169699Skan	  return target;
2286169699Skan	}
2287169699Skan
2288169699Skan      /* If we were unable to expand via the builtin, stop the sequence
2289169699Skan	 (without outputting the insns).  */
2290169699Skan      end_sequence ();
2291169699Skan    }
2292169699Skan
2293169699Skan  /* Fall back to floating point rounding optab.  */
2294169699Skan  fallback_fndecl = mathfn_built_in (TREE_TYPE (arg), fallback_fn);
2295169699Skan  /* We shouldn't get here on targets without TARGET_C99_FUNCTIONS.
2296169699Skan     ??? Perhaps convert (int)floorf(x) into (int)floor((double)x).  */
2297169699Skan  gcc_assert (fallback_fndecl != NULL_TREE);
2298169699Skan  exp = build_function_call_expr (fallback_fndecl, arglist);
2299169699Skan
2300169699Skan  tmp = expand_normal (exp);
2301169699Skan
2302169699Skan  /* Truncate the result of floating point optab to integer
2303169699Skan     via expand_fix ().  */
2304169699Skan  target = gen_reg_rtx (mode);
2305169699Skan  expand_fix (target, tmp, 0);
2306169699Skan
2307169699Skan  return target;
2308169699Skan}
2309169699Skan
2310132727Skan/* To evaluate powi(x,n), the floating point value x raised to the
2311132727Skan   constant integer exponent n, we use a hybrid algorithm that
2312132727Skan   combines the "window method" with look-up tables.  For an
2313132727Skan   introduction to exponentiation algorithms and "addition chains",
2314132727Skan   see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth,
2315132727Skan   "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming",
2316132727Skan   3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation
2317132727Skan   Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998.  */
2318132727Skan
2319132727Skan/* Provide a default value for POWI_MAX_MULTS, the maximum number of
2320132727Skan   multiplications to inline before calling the system library's pow
2321132727Skan   function.  powi(x,n) requires at worst 2*bits(n)-2 multiplications,
2322132727Skan   so this default never requires calling pow, powf or powl.  */
2323169699Skan
2324132727Skan#ifndef POWI_MAX_MULTS
2325132727Skan#define POWI_MAX_MULTS  (2*HOST_BITS_PER_WIDE_INT-2)
2326132727Skan#endif
2327132727Skan
2328132727Skan/* The size of the "optimal power tree" lookup table.  All
2329132727Skan   exponents less than this value are simply looked up in the
2330132727Skan   powi_table below.  This threshold is also used to size the
2331132727Skan   cache of pseudo registers that hold intermediate results.  */
2332132727Skan#define POWI_TABLE_SIZE 256
2333132727Skan
2334132727Skan/* The size, in bits of the window, used in the "window method"
2335132727Skan   exponentiation algorithm.  This is equivalent to a radix of
2336132727Skan   (1<<POWI_WINDOW_SIZE) in the corresponding "m-ary method".  */
2337132727Skan#define POWI_WINDOW_SIZE 3
2338132727Skan
2339132727Skan/* The following table is an efficient representation of an
2340132727Skan   "optimal power tree".  For each value, i, the corresponding
2341132727Skan   value, j, in the table states than an optimal evaluation
2342132727Skan   sequence for calculating pow(x,i) can be found by evaluating
2343132727Skan   pow(x,j)*pow(x,i-j).  An optimal power tree for the first
2344132727Skan   100 integers is given in Knuth's "Seminumerical algorithms".  */
2345132727Skan
2346132727Skanstatic const unsigned char powi_table[POWI_TABLE_SIZE] =
2347132727Skan  {
2348132727Skan      0,   1,   1,   2,   2,   3,   3,   4,  /*   0 -   7 */
2349132727Skan      4,   6,   5,   6,   6,  10,   7,   9,  /*   8 -  15 */
2350132727Skan      8,  16,   9,  16,  10,  12,  11,  13,  /*  16 -  23 */
2351132727Skan     12,  17,  13,  18,  14,  24,  15,  26,  /*  24 -  31 */
2352132727Skan     16,  17,  17,  19,  18,  33,  19,  26,  /*  32 -  39 */
2353132727Skan     20,  25,  21,  40,  22,  27,  23,  44,  /*  40 -  47 */
2354132727Skan     24,  32,  25,  34,  26,  29,  27,  44,  /*  48 -  55 */
2355132727Skan     28,  31,  29,  34,  30,  60,  31,  36,  /*  56 -  63 */
2356132727Skan     32,  64,  33,  34,  34,  46,  35,  37,  /*  64 -  71 */
2357132727Skan     36,  65,  37,  50,  38,  48,  39,  69,  /*  72 -  79 */
2358132727Skan     40,  49,  41,  43,  42,  51,  43,  58,  /*  80 -  87 */
2359132727Skan     44,  64,  45,  47,  46,  59,  47,  76,  /*  88 -  95 */
2360132727Skan     48,  65,  49,  66,  50,  67,  51,  66,  /*  96 - 103 */
2361132727Skan     52,  70,  53,  74,  54, 104,  55,  74,  /* 104 - 111 */
2362132727Skan     56,  64,  57,  69,  58,  78,  59,  68,  /* 112 - 119 */
2363132727Skan     60,  61,  61,  80,  62,  75,  63,  68,  /* 120 - 127 */
2364132727Skan     64,  65,  65, 128,  66, 129,  67,  90,  /* 128 - 135 */
2365132727Skan     68,  73,  69, 131,  70,  94,  71,  88,  /* 136 - 143 */
2366132727Skan     72, 128,  73,  98,  74, 132,  75, 121,  /* 144 - 151 */
2367132727Skan     76, 102,  77, 124,  78, 132,  79, 106,  /* 152 - 159 */
2368132727Skan     80,  97,  81, 160,  82,  99,  83, 134,  /* 160 - 167 */
2369132727Skan     84,  86,  85,  95,  86, 160,  87, 100,  /* 168 - 175 */
2370132727Skan     88, 113,  89,  98,  90, 107,  91, 122,  /* 176 - 183 */
2371132727Skan     92, 111,  93, 102,  94, 126,  95, 150,  /* 184 - 191 */
2372132727Skan     96, 128,  97, 130,  98, 133,  99, 195,  /* 192 - 199 */
2373132727Skan    100, 128, 101, 123, 102, 164, 103, 138,  /* 200 - 207 */
2374132727Skan    104, 145, 105, 146, 106, 109, 107, 149,  /* 208 - 215 */
2375132727Skan    108, 200, 109, 146, 110, 170, 111, 157,  /* 216 - 223 */
2376132727Skan    112, 128, 113, 130, 114, 182, 115, 132,  /* 224 - 231 */
2377132727Skan    116, 200, 117, 132, 118, 158, 119, 206,  /* 232 - 239 */
2378132727Skan    120, 240, 121, 162, 122, 147, 123, 152,  /* 240 - 247 */
2379132727Skan    124, 166, 125, 214, 126, 138, 127, 153,  /* 248 - 255 */
2380132727Skan  };
2381132727Skan
2382132727Skan
2383132727Skan/* Return the number of multiplications required to calculate
2384132727Skan   powi(x,n) where n is less than POWI_TABLE_SIZE.  This is a
2385132727Skan   subroutine of powi_cost.  CACHE is an array indicating
2386132727Skan   which exponents have already been calculated.  */
2387132727Skan
2388132727Skanstatic int
2389132727Skanpowi_lookup_cost (unsigned HOST_WIDE_INT n, bool *cache)
2390132727Skan{
2391132727Skan  /* If we've already calculated this exponent, then this evaluation
2392132727Skan     doesn't require any additional multiplications.  */
2393132727Skan  if (cache[n])
2394132727Skan    return 0;
2395132727Skan
2396132727Skan  cache[n] = true;
2397132727Skan  return powi_lookup_cost (n - powi_table[n], cache)
2398132727Skan	 + powi_lookup_cost (powi_table[n], cache) + 1;
2399132727Skan}
2400132727Skan
2401132727Skan/* Return the number of multiplications required to calculate
2402132727Skan   powi(x,n) for an arbitrary x, given the exponent N.  This
2403132727Skan   function needs to be kept in sync with expand_powi below.  */
2404132727Skan
2405132727Skanstatic int
2406132727Skanpowi_cost (HOST_WIDE_INT n)
2407132727Skan{
2408132727Skan  bool cache[POWI_TABLE_SIZE];
2409132727Skan  unsigned HOST_WIDE_INT digit;
2410132727Skan  unsigned HOST_WIDE_INT val;
2411132727Skan  int result;
2412132727Skan
2413132727Skan  if (n == 0)
2414132727Skan    return 0;
2415132727Skan
2416132727Skan  /* Ignore the reciprocal when calculating the cost.  */
2417132727Skan  val = (n < 0) ? -n : n;
2418132727Skan
2419132727Skan  /* Initialize the exponent cache.  */
2420132727Skan  memset (cache, 0, POWI_TABLE_SIZE * sizeof (bool));
2421132727Skan  cache[1] = true;
2422132727Skan
2423132727Skan  result = 0;
2424132727Skan
2425132727Skan  while (val >= POWI_TABLE_SIZE)
2426132727Skan    {
2427132727Skan      if (val & 1)
2428132727Skan	{
2429132727Skan	  digit = val & ((1 << POWI_WINDOW_SIZE) - 1);
2430132727Skan	  result += powi_lookup_cost (digit, cache)
2431132727Skan		    + POWI_WINDOW_SIZE + 1;
2432132727Skan	  val >>= POWI_WINDOW_SIZE;
2433132727Skan	}
2434132727Skan      else
2435132727Skan	{
2436132727Skan	  val >>= 1;
2437132727Skan	  result++;
2438132727Skan	}
2439132727Skan    }
2440169699Skan
2441132727Skan  return result + powi_lookup_cost (val, cache);
2442132727Skan}
2443132727Skan
2444132727Skan/* Recursive subroutine of expand_powi.  This function takes the array,
2445132727Skan   CACHE, of already calculated exponents and an exponent N and returns
2446132727Skan   an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE.  */
2447132727Skan
2448132727Skanstatic rtx
2449132727Skanexpand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache)
2450132727Skan{
2451132727Skan  unsigned HOST_WIDE_INT digit;
2452132727Skan  rtx target, result;
2453132727Skan  rtx op0, op1;
2454132727Skan
2455132727Skan  if (n < POWI_TABLE_SIZE)
2456132727Skan    {
2457132727Skan      if (cache[n])
2458169699Skan	return cache[n];
2459132727Skan
2460132727Skan      target = gen_reg_rtx (mode);
2461132727Skan      cache[n] = target;
2462132727Skan
2463132727Skan      op0 = expand_powi_1 (mode, n - powi_table[n], cache);
2464132727Skan      op1 = expand_powi_1 (mode, powi_table[n], cache);
2465132727Skan    }
2466132727Skan  else if (n & 1)
2467132727Skan    {
2468132727Skan      target = gen_reg_rtx (mode);
2469132727Skan      digit = n & ((1 << POWI_WINDOW_SIZE) - 1);
2470132727Skan      op0 = expand_powi_1 (mode, n - digit, cache);
2471132727Skan      op1 = expand_powi_1 (mode, digit, cache);
2472132727Skan    }
2473132727Skan  else
2474132727Skan    {
2475132727Skan      target = gen_reg_rtx (mode);
2476132727Skan      op0 = expand_powi_1 (mode, n >> 1, cache);
2477132727Skan      op1 = op0;
2478132727Skan    }
2479132727Skan
2480132727Skan  result = expand_mult (mode, op0, op1, target, 0);
2481132727Skan  if (result != target)
2482132727Skan    emit_move_insn (target, result);
2483132727Skan  return target;
2484132727Skan}
2485132727Skan
2486132727Skan/* Expand the RTL to evaluate powi(x,n) in mode MODE.  X is the
2487132727Skan   floating point operand in mode MODE, and N is the exponent.  This
2488132727Skan   function needs to be kept in sync with powi_cost above.  */
2489169699Skan
2490132727Skanstatic rtx
2491132727Skanexpand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n)
2492132727Skan{
2493132727Skan  unsigned HOST_WIDE_INT val;
2494132727Skan  rtx cache[POWI_TABLE_SIZE];
2495132727Skan  rtx result;
2496132727Skan
2497132727Skan  if (n == 0)
2498132727Skan    return CONST1_RTX (mode);
2499132727Skan
2500132727Skan  val = (n < 0) ? -n : n;
2501132727Skan
2502132727Skan  memset (cache, 0, sizeof (cache));
2503132727Skan  cache[1] = x;
2504132727Skan
2505132727Skan  result = expand_powi_1 (mode, (n < 0) ? -n : n, cache);
2506132727Skan
2507132727Skan  /* If the original exponent was negative, reciprocate the result.  */
2508132727Skan  if (n < 0)
2509132727Skan    result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode),
2510132727Skan			   result, NULL_RTX, 0, OPTAB_LIB_WIDEN);
2511132727Skan
2512132727Skan  return result;
2513132727Skan}
2514132727Skan
2515132727Skan/* Expand a call to the pow built-in mathematical function.  Return 0 if
2516132727Skan   a normal call should be emitted rather than expanding the function
2517132727Skan   in-line.  EXP is the expression that is a call to the builtin
2518132727Skan   function; if convenient, the result should be placed in TARGET.  */
2519132727Skan
2520132727Skanstatic rtx
2521132727Skanexpand_builtin_pow (tree exp, rtx target, rtx subtarget)
2522132727Skan{
2523132727Skan  tree arglist = TREE_OPERAND (exp, 1);
2524132727Skan  tree arg0, arg1;
2525132727Skan
2526132727Skan  if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
2527132727Skan    return 0;
2528132727Skan
2529132727Skan  arg0 = TREE_VALUE (arglist);
2530132727Skan  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
2531132727Skan
2532132727Skan  if (TREE_CODE (arg1) == REAL_CST
2533132727Skan      && ! TREE_CONSTANT_OVERFLOW (arg1))
2534132727Skan    {
2535132727Skan      REAL_VALUE_TYPE cint;
2536132727Skan      REAL_VALUE_TYPE c;
2537132727Skan      HOST_WIDE_INT n;
2538132727Skan
2539132727Skan      c = TREE_REAL_CST (arg1);
2540132727Skan      n = real_to_integer (&c);
2541132727Skan      real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
2542132727Skan      if (real_identical (&c, &cint))
2543132727Skan	{
2544132727Skan	  /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact.
2545132727Skan	     Otherwise, check the number of multiplications required.
2546132727Skan	     Note that pow never sets errno for an integer exponent.  */
2547132727Skan	  if ((n >= -1 && n <= 2)
2548132727Skan	      || (flag_unsafe_math_optimizations
2549132727Skan		  && ! optimize_size
2550132727Skan		  && powi_cost (n) <= POWI_MAX_MULTS))
2551132727Skan	    {
2552132727Skan	      enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
2553132727Skan	      rtx op = expand_expr (arg0, subtarget, VOIDmode, 0);
2554132727Skan	      op = force_reg (mode, op);
2555132727Skan	      return expand_powi (op, mode, n);
2556132727Skan	    }
2557132727Skan	}
2558132727Skan    }
2559169699Skan
2560169699Skan  if (! flag_unsafe_math_optimizations)
2561169699Skan    return NULL_RTX;
2562169699Skan  return expand_builtin_mathfn_2 (exp, target, subtarget);
2563132727Skan}
2564132727Skan
2565169699Skan/* Expand a call to the powi built-in mathematical function.  Return 0 if
2566169699Skan   a normal call should be emitted rather than expanding the function
2567169699Skan   in-line.  EXP is the expression that is a call to the builtin
2568169699Skan   function; if convenient, the result should be placed in TARGET.  */
2569169699Skan
2570169699Skanstatic rtx
2571169699Skanexpand_builtin_powi (tree exp, rtx target, rtx subtarget)
2572169699Skan{
2573169699Skan  tree arglist = TREE_OPERAND (exp, 1);
2574169699Skan  tree arg0, arg1;
2575169699Skan  rtx op0, op1;
2576169699Skan  enum machine_mode mode;
2577169699Skan  enum machine_mode mode2;
2578169699Skan
2579169699Skan  if (! validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE))
2580169699Skan    return 0;
2581169699Skan
2582169699Skan  arg0 = TREE_VALUE (arglist);
2583169699Skan  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
2584169699Skan  mode = TYPE_MODE (TREE_TYPE (exp));
2585169699Skan
2586169699Skan  /* Handle constant power.  */
2587169699Skan
2588169699Skan  if (TREE_CODE (arg1) == INTEGER_CST
2589169699Skan      && ! TREE_CONSTANT_OVERFLOW (arg1))
2590169699Skan    {
2591169699Skan      HOST_WIDE_INT n = TREE_INT_CST_LOW (arg1);
2592169699Skan
2593169699Skan      /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact.
2594169699Skan	 Otherwise, check the number of multiplications required.  */
2595169699Skan      if ((TREE_INT_CST_HIGH (arg1) == 0
2596169699Skan	   || TREE_INT_CST_HIGH (arg1) == -1)
2597169699Skan	  && ((n >= -1 && n <= 2)
2598169699Skan	      || (! optimize_size
2599169699Skan		  && powi_cost (n) <= POWI_MAX_MULTS)))
2600169699Skan	{
2601169699Skan	  op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
2602169699Skan	  op0 = force_reg (mode, op0);
2603169699Skan	  return expand_powi (op0, mode, n);
2604169699Skan	}
2605169699Skan    }
2606169699Skan
2607169699Skan  /* Emit a libcall to libgcc.  */
2608169699Skan
2609169699Skan  /* Mode of the 2nd argument must match that of an int. */
2610169699Skan  mode2 = mode_for_size (INT_TYPE_SIZE, MODE_INT, 0);
2611169699Skan
2612169699Skan  if (target == NULL_RTX)
2613169699Skan    target = gen_reg_rtx (mode);
2614169699Skan
2615169699Skan  op0 = expand_expr (arg0, subtarget, mode, 0);
2616169699Skan  if (GET_MODE (op0) != mode)
2617169699Skan    op0 = convert_to_mode (mode, op0, 0);
2618169699Skan  op1 = expand_expr (arg1, 0, mode2, 0);
2619169699Skan  if (GET_MODE (op1) != mode2)
2620169699Skan    op1 = convert_to_mode (mode2, op1, 0);
2621169699Skan
2622169699Skan  target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc,
2623169699Skan				    target, LCT_CONST_MAKE_BLOCK, mode, 2,
2624169699Skan				    op0, mode, op1, mode2);
2625169699Skan
2626169699Skan  return target;
2627169699Skan}
2628169699Skan
262990075Sobrien/* Expand expression EXP which is a call to the strlen builtin.  Return 0
263090075Sobrien   if we failed the caller should emit a normal call, otherwise
263190075Sobrien   try to get the result in TARGET, if convenient.  */
263290075Sobrien
263390075Sobrienstatic rtx
2634132727Skanexpand_builtin_strlen (tree arglist, rtx target,
2635132727Skan		       enum machine_mode target_mode)
263690075Sobrien{
263790075Sobrien  if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
263890075Sobrien    return 0;
263990075Sobrien  else
264090075Sobrien    {
264190075Sobrien      rtx pat;
2642132727Skan      tree len, src = TREE_VALUE (arglist);
264390075Sobrien      rtx result, src_reg, char_rtx, before_strlen;
2644132727Skan      enum machine_mode insn_mode = target_mode, char_mode;
264590075Sobrien      enum insn_code icode = CODE_FOR_nothing;
2646132727Skan      int align;
264790075Sobrien
2648132727Skan      /* If the length can be computed at compile-time, return it.  */
2649132727Skan      len = c_strlen (src, 0);
2650132727Skan      if (len)
2651132727Skan	return expand_expr (len, target, target_mode, EXPAND_NORMAL);
2652132727Skan
2653132727Skan      /* If the length can be computed at compile-time and is constant
2654132727Skan	 integer, but there are side-effects in src, evaluate
2655132727Skan	 src for side-effects, then return len.
2656132727Skan	 E.g. x = strlen (i++ ? "xfoo" + 1 : "bar");
2657132727Skan	 can be optimized into: i++; x = 3;  */
2658132727Skan      len = c_strlen (src, 1);
2659132727Skan      if (len && TREE_CODE (len) == INTEGER_CST)
2660132727Skan	{
2661132727Skan	  expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
2662132727Skan	  return expand_expr (len, target, target_mode, EXPAND_NORMAL);
2663132727Skan	}
2664132727Skan
2665132727Skan      align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
2666132727Skan
266790075Sobrien      /* If SRC is not a pointer type, don't do this operation inline.  */
266890075Sobrien      if (align == 0)
266990075Sobrien	return 0;
267090075Sobrien
267190075Sobrien      /* Bail out if we can't compute strlen in the right mode.  */
267290075Sobrien      while (insn_mode != VOIDmode)
267390075Sobrien	{
267490075Sobrien	  icode = strlen_optab->handlers[(int) insn_mode].insn_code;
267590075Sobrien	  if (icode != CODE_FOR_nothing)
267690075Sobrien	    break;
267790075Sobrien
267890075Sobrien	  insn_mode = GET_MODE_WIDER_MODE (insn_mode);
267990075Sobrien	}
268090075Sobrien      if (insn_mode == VOIDmode)
268190075Sobrien	return 0;
268290075Sobrien
268390075Sobrien      /* Make a place to write the result of the instruction.  */
268490075Sobrien      result = target;
268590075Sobrien      if (! (result != 0
2686169699Skan	     && REG_P (result)
268790075Sobrien	     && GET_MODE (result) == insn_mode
268890075Sobrien	     && REGNO (result) >= FIRST_PSEUDO_REGISTER))
268990075Sobrien	result = gen_reg_rtx (insn_mode);
269090075Sobrien
269190075Sobrien      /* Make a place to hold the source address.  We will not expand
269290075Sobrien	 the actual source until we are sure that the expansion will
269390075Sobrien	 not fail -- there are trees that cannot be expanded twice.  */
269490075Sobrien      src_reg = gen_reg_rtx (Pmode);
269590075Sobrien
269690075Sobrien      /* Mark the beginning of the strlen sequence so we can emit the
269790075Sobrien	 source operand later.  */
2698117404Skan      before_strlen = get_last_insn ();
269990075Sobrien
270090075Sobrien      char_rtx = const0_rtx;
270190075Sobrien      char_mode = insn_data[(int) icode].operand[2].mode;
270290075Sobrien      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
270390075Sobrien							    char_mode))
270490075Sobrien	char_rtx = copy_to_mode_reg (char_mode, char_rtx);
270590075Sobrien
270690075Sobrien      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
270790075Sobrien			     char_rtx, GEN_INT (align));
270890075Sobrien      if (! pat)
270990075Sobrien	return 0;
271090075Sobrien      emit_insn (pat);
271190075Sobrien
271290075Sobrien      /* Now that we are assured of success, expand the source.  */
271390075Sobrien      start_sequence ();
2714169699Skan      pat = expand_expr (src, src_reg, ptr_mode, EXPAND_NORMAL);
271590075Sobrien      if (pat != src_reg)
271690075Sobrien	emit_move_insn (src_reg, pat);
2717117404Skan      pat = get_insns ();
271890075Sobrien      end_sequence ();
271990075Sobrien
272090075Sobrien      if (before_strlen)
272190075Sobrien	emit_insn_after (pat, before_strlen);
272290075Sobrien      else
272390075Sobrien	emit_insn_before (pat, get_insns ());
272490075Sobrien
272590075Sobrien      /* Return the value in the proper mode for this function.  */
2726132727Skan      if (GET_MODE (result) == target_mode)
272790075Sobrien	target = result;
272890075Sobrien      else if (target != 0)
272990075Sobrien	convert_move (target, result, 0);
273090075Sobrien      else
2731132727Skan	target = convert_to_mode (target_mode, result, 0);
273290075Sobrien
273390075Sobrien      return target;
273490075Sobrien    }
273590075Sobrien}
273690075Sobrien
273790075Sobrien/* Expand a call to the strstr builtin.  Return 0 if we failed the
273890075Sobrien   caller should emit a normal call, otherwise try to get the result
273990075Sobrien   in TARGET, if convenient (and in mode MODE if that's convenient).  */
274090075Sobrien
274190075Sobrienstatic rtx
2742169699Skanexpand_builtin_strstr (tree arglist, tree type, rtx target, enum machine_mode mode)
274390075Sobrien{
2744169699Skan  if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
274590075Sobrien    {
2746169699Skan      tree result = fold_builtin_strstr (arglist, type);
2747169699Skan      if (result)
2748169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
274990075Sobrien    }
2750169699Skan  return 0;
275190075Sobrien}
275290075Sobrien
275390075Sobrien/* Expand a call to the strchr builtin.  Return 0 if we failed the
275490075Sobrien   caller should emit a normal call, otherwise try to get the result
275590075Sobrien   in TARGET, if convenient (and in mode MODE if that's convenient).  */
275690075Sobrien
275790075Sobrienstatic rtx
2758169699Skanexpand_builtin_strchr (tree arglist, tree type, rtx target, enum machine_mode mode)
275990075Sobrien{
2760169699Skan  if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
276190075Sobrien    {
2762169699Skan      tree result = fold_builtin_strchr (arglist, type);
2763169699Skan      if (result)
2764169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
276590075Sobrien
2766169699Skan      /* FIXME: Should use strchrM optab so that ports can optimize this.  */
276790075Sobrien    }
2768169699Skan  return 0;
276990075Sobrien}
277090075Sobrien
277190075Sobrien/* Expand a call to the strrchr builtin.  Return 0 if we failed the
277290075Sobrien   caller should emit a normal call, otherwise try to get the result
277390075Sobrien   in TARGET, if convenient (and in mode MODE if that's convenient).  */
277490075Sobrien
277590075Sobrienstatic rtx
2776169699Skanexpand_builtin_strrchr (tree arglist, tree type, rtx target, enum machine_mode mode)
277790075Sobrien{
2778169699Skan  if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
277990075Sobrien    {
2780169699Skan      tree result = fold_builtin_strrchr (arglist, type);
2781169699Skan      if (result)
2782169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
278390075Sobrien    }
2784169699Skan  return 0;
278590075Sobrien}
278690075Sobrien
278790075Sobrien/* Expand a call to the strpbrk builtin.  Return 0 if we failed the
278890075Sobrien   caller should emit a normal call, otherwise try to get the result
278990075Sobrien   in TARGET, if convenient (and in mode MODE if that's convenient).  */
279090075Sobrien
279190075Sobrienstatic rtx
2792169699Skanexpand_builtin_strpbrk (tree arglist, tree type, rtx target, enum machine_mode mode)
279390075Sobrien{
2794169699Skan  if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
279590075Sobrien    {
2796169699Skan      tree result = fold_builtin_strpbrk (arglist, type);
2797169699Skan      if (result)
2798169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
279990075Sobrien    }
2800169699Skan  return 0;
280190075Sobrien}
280290075Sobrien
280390075Sobrien/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
280490075Sobrien   bytes from constant string DATA + OFFSET and return it as target
280590075Sobrien   constant.  */
280690075Sobrien
280790075Sobrienstatic rtx
2808132727Skanbuiltin_memcpy_read_str (void *data, HOST_WIDE_INT offset,
2809132727Skan			 enum machine_mode mode)
281090075Sobrien{
281190075Sobrien  const char *str = (const char *) data;
281290075Sobrien
2813169699Skan  gcc_assert (offset >= 0
2814169699Skan	      && ((unsigned HOST_WIDE_INT) offset + GET_MODE_SIZE (mode)
2815169699Skan		  <= strlen (str) + 1));
281690075Sobrien
281790075Sobrien  return c_readstr (str + offset, mode);
281890075Sobrien}
281990075Sobrien
282090075Sobrien/* Expand a call to the memcpy builtin, with arguments in ARGLIST.
2821132727Skan   Return 0 if we failed, the caller should emit a normal call,
2822132727Skan   otherwise try to get the result in TARGET, if convenient (and in
2823132727Skan   mode MODE if that's convenient).  */
282490075Sobrienstatic rtx
2825169699Skanexpand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
282690075Sobrien{
2827169699Skan  tree fndecl = get_callee_fndecl (exp);
2828169699Skan  tree arglist = TREE_OPERAND (exp, 1);
282990075Sobrien  if (!validate_arglist (arglist,
283090075Sobrien			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
283190075Sobrien    return 0;
283290075Sobrien  else
283390075Sobrien    {
283490075Sobrien      tree dest = TREE_VALUE (arglist);
283590075Sobrien      tree src = TREE_VALUE (TREE_CHAIN (arglist));
283690075Sobrien      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
283790075Sobrien      const char *src_str;
283890075Sobrien      unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
283990075Sobrien      unsigned int dest_align
284090075Sobrien	= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
284190075Sobrien      rtx dest_mem, src_mem, dest_addr, len_rtx;
2842169699Skan      tree result = fold_builtin_memory_op (arglist, TREE_TYPE (TREE_TYPE (fndecl)),
2843169699Skan					    false, /*endp=*/0);
284490075Sobrien
2845169699Skan      if (result)
2846169699Skan	{
2847169699Skan	  while (TREE_CODE (result) == COMPOUND_EXPR)
2848169699Skan	    {
2849169699Skan	      expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
2850169699Skan			   EXPAND_NORMAL);
2851169699Skan	      result = TREE_OPERAND (result, 1);
2852169699Skan	    }
2853169699Skan	  return expand_expr (result, target, mode, EXPAND_NORMAL);
2854169699Skan	}
2855169699Skan
285690075Sobrien      /* If DEST is not a pointer type, call the normal function.  */
285790075Sobrien      if (dest_align == 0)
2858117404Skan	return 0;
285990075Sobrien
286090075Sobrien      /* If either SRC is not a pointer type, don't do this
2861169699Skan	 operation in-line.  */
286290075Sobrien      if (src_align == 0)
2863117404Skan	return 0;
286490075Sobrien
2865169699Skan      dest_mem = get_memory_rtx (dest, len);
286690075Sobrien      set_mem_align (dest_mem, dest_align);
2867169699Skan      len_rtx = expand_normal (len);
286890075Sobrien      src_str = c_getstr (src);
286990075Sobrien
287090075Sobrien      /* If SRC is a string constant and block move would be done
287190075Sobrien	 by pieces, we can avoid loading the string from memory
287290075Sobrien	 and only stored the computed constants.  */
287390075Sobrien      if (src_str
287490075Sobrien	  && GET_CODE (len_rtx) == CONST_INT
287590075Sobrien	  && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
287690075Sobrien	  && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
2877132727Skan				  (void *) src_str, dest_align))
287890075Sobrien	{
2879132727Skan	  dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
2880132727Skan				      builtin_memcpy_read_str,
2881132727Skan				      (void *) src_str, dest_align, 0);
2882117404Skan	  dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
2883132727Skan	  dest_mem = convert_memory_address (ptr_mode, dest_mem);
2884117404Skan	  return dest_mem;
288590075Sobrien	}
288690075Sobrien
2887169699Skan      src_mem = get_memory_rtx (src, len);
288890075Sobrien      set_mem_align (src_mem, src_align);
288990075Sobrien
289090075Sobrien      /* Copy word part most expediently.  */
2891117404Skan      dest_addr = emit_block_move (dest_mem, src_mem, len_rtx,
2892169699Skan				   CALL_EXPR_TAILCALL (exp)
2893169699Skan				   ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
289490075Sobrien
289590075Sobrien      if (dest_addr == 0)
2896117404Skan	{
2897117404Skan	  dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
2898132727Skan	  dest_addr = convert_memory_address (ptr_mode, dest_addr);
2899117404Skan	}
290090075Sobrien      return dest_addr;
290190075Sobrien    }
290290075Sobrien}
290390075Sobrien
2904132727Skan/* Expand a call to the mempcpy builtin, with arguments in ARGLIST.
2905169699Skan   Return 0 if we failed; the caller should emit a normal call,
2906132727Skan   otherwise try to get the result in TARGET, if convenient (and in
2907132727Skan   mode MODE if that's convenient).  If ENDP is 0 return the
2908132727Skan   destination pointer, if ENDP is 1 return the end pointer ala
2909132727Skan   mempcpy, and if ENDP is 2 return the end pointer minus one ala
2910132727Skan   stpcpy.  */
2911132727Skan
2912132727Skanstatic rtx
2913169699Skanexpand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode mode,
2914132727Skan			int endp)
2915132727Skan{
2916132727Skan  if (!validate_arglist (arglist,
2917132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
2918132727Skan    return 0;
2919132727Skan  /* If return value is ignored, transform mempcpy into memcpy.  */
2920132727Skan  else if (target == const0_rtx)
2921132727Skan    {
2922132727Skan      tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
2923132727Skan
2924132727Skan      if (!fn)
2925132727Skan	return 0;
2926132727Skan
2927132727Skan      return expand_expr (build_function_call_expr (fn, arglist),
2928132727Skan			  target, mode, EXPAND_NORMAL);
2929132727Skan    }
2930132727Skan  else
2931132727Skan    {
2932132727Skan      tree dest = TREE_VALUE (arglist);
2933132727Skan      tree src = TREE_VALUE (TREE_CHAIN (arglist));
2934132727Skan      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
2935132727Skan      const char *src_str;
2936132727Skan      unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
2937132727Skan      unsigned int dest_align
2938132727Skan	= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
2939132727Skan      rtx dest_mem, src_mem, len_rtx;
2940169699Skan      tree result = fold_builtin_memory_op (arglist, type, false, endp);
2941132727Skan
2942169699Skan      if (result)
2943132727Skan	{
2944169699Skan	  while (TREE_CODE (result) == COMPOUND_EXPR)
2945132727Skan	    {
2946169699Skan	      expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
2947169699Skan			   EXPAND_NORMAL);
2948169699Skan	      result = TREE_OPERAND (result, 1);
2949132727Skan	    }
2950169699Skan	  return expand_expr (result, target, mode, EXPAND_NORMAL);
2951132727Skan	}
2952132727Skan
2953169699Skan      /* If either SRC or DEST is not a pointer type, don't do this
2954169699Skan	 operation in-line.  */
2955169699Skan      if (dest_align == 0 || src_align == 0)
2956169699Skan	return 0;
2957169699Skan
2958132727Skan      /* If LEN is not constant, call the normal function.  */
2959132727Skan      if (! host_integerp (len, 1))
2960132727Skan	return 0;
2961132727Skan
2962169699Skan      len_rtx = expand_normal (len);
2963132727Skan      src_str = c_getstr (src);
2964132727Skan
2965132727Skan      /* If SRC is a string constant and block move would be done
2966132727Skan	 by pieces, we can avoid loading the string from memory
2967132727Skan	 and only stored the computed constants.  */
2968132727Skan      if (src_str
2969132727Skan	  && GET_CODE (len_rtx) == CONST_INT
2970132727Skan	  && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
2971132727Skan	  && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
2972132727Skan				  (void *) src_str, dest_align))
2973132727Skan	{
2974169699Skan	  dest_mem = get_memory_rtx (dest, len);
2975132727Skan	  set_mem_align (dest_mem, dest_align);
2976132727Skan	  dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
2977132727Skan				      builtin_memcpy_read_str,
2978132727Skan				      (void *) src_str, dest_align, endp);
2979132727Skan	  dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
2980132727Skan	  dest_mem = convert_memory_address (ptr_mode, dest_mem);
2981132727Skan	  return dest_mem;
2982132727Skan	}
2983132727Skan
2984132727Skan      if (GET_CODE (len_rtx) == CONST_INT
2985132727Skan	  && can_move_by_pieces (INTVAL (len_rtx),
2986132727Skan				 MIN (dest_align, src_align)))
2987132727Skan	{
2988169699Skan	  dest_mem = get_memory_rtx (dest, len);
2989132727Skan	  set_mem_align (dest_mem, dest_align);
2990169699Skan	  src_mem = get_memory_rtx (src, len);
2991132727Skan	  set_mem_align (src_mem, src_align);
2992132727Skan	  dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx),
2993132727Skan				     MIN (dest_align, src_align), endp);
2994132727Skan	  dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
2995132727Skan	  dest_mem = convert_memory_address (ptr_mode, dest_mem);
2996132727Skan	  return dest_mem;
2997132727Skan	}
2998132727Skan
2999132727Skan      return 0;
3000132727Skan    }
3001132727Skan}
3002132727Skan
3003132727Skan/* Expand expression EXP, which is a call to the memmove builtin.  Return 0
3004169699Skan   if we failed; the caller should emit a normal call.  */
3005132727Skan
3006132727Skanstatic rtx
3007169699Skanexpand_builtin_memmove (tree arglist, tree type, rtx target,
3008169699Skan			enum machine_mode mode, tree orig_exp)
3009132727Skan{
3010132727Skan  if (!validate_arglist (arglist,
3011132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
3012132727Skan    return 0;
3013132727Skan  else
3014132727Skan    {
3015132727Skan      tree dest = TREE_VALUE (arglist);
3016132727Skan      tree src = TREE_VALUE (TREE_CHAIN (arglist));
3017132727Skan      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
3018132727Skan
3019132727Skan      unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
3020132727Skan      unsigned int dest_align
3021132727Skan	= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
3022169699Skan      tree result = fold_builtin_memory_op (arglist, type, false, /*endp=*/3);
3023132727Skan
3024169699Skan      if (result)
3025169699Skan	{
3026169699Skan	  while (TREE_CODE (result) == COMPOUND_EXPR)
3027169699Skan	    {
3028169699Skan	      expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
3029169699Skan			   EXPAND_NORMAL);
3030169699Skan	      result = TREE_OPERAND (result, 1);
3031169699Skan	    }
3032169699Skan	  return expand_expr (result, target, mode, EXPAND_NORMAL);
3033169699Skan	}
3034169699Skan
3035132727Skan      /* If DEST is not a pointer type, call the normal function.  */
3036132727Skan      if (dest_align == 0)
3037132727Skan	return 0;
3038132727Skan
3039132727Skan      /* If either SRC is not a pointer type, don't do this
3040169699Skan	 operation in-line.  */
3041132727Skan      if (src_align == 0)
3042132727Skan	return 0;
3043132727Skan
3044132727Skan      /* If src is categorized for a readonly section we can use
3045132727Skan	 normal memcpy.  */
3046132727Skan      if (readonly_data_expr (src))
3047169699Skan	{
3048169699Skan	  tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
3049132727Skan	  if (!fn)
3050132727Skan	    return 0;
3051169699Skan	  fn = build_function_call_expr (fn, arglist);
3052169699Skan	  if (TREE_CODE (fn) == CALL_EXPR)
3053169699Skan	    CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
3054169699Skan	  return expand_expr (fn, target, mode, EXPAND_NORMAL);
3055132727Skan	}
3056132727Skan
3057169699Skan      /* If length is 1 and we can expand memcpy call inline,
3058169699Skan	 it is ok to use memcpy as well.  */
3059169699Skan      if (integer_onep (len))
3060169699Skan	{
3061169699Skan	  rtx ret = expand_builtin_mempcpy (arglist, type, target, mode,
3062169699Skan					    /*endp=*/0);
3063169699Skan	  if (ret)
3064169699Skan	    return ret;
3065169699Skan	}
3066169699Skan
3067132727Skan      /* Otherwise, call the normal function.  */
3068132727Skan      return 0;
3069132727Skan   }
3070132727Skan}
3071132727Skan
3072132727Skan/* Expand expression EXP, which is a call to the bcopy builtin.  Return 0
3073132727Skan   if we failed the caller should emit a normal call.  */
3074132727Skan
3075132727Skanstatic rtx
3076169699Skanexpand_builtin_bcopy (tree exp)
3077132727Skan{
3078169699Skan  tree arglist = TREE_OPERAND (exp, 1);
3079169699Skan  tree type = TREE_TYPE (exp);
3080132727Skan  tree src, dest, size, newarglist;
3081132727Skan
3082132727Skan  if (!validate_arglist (arglist,
3083132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
3084132727Skan    return NULL_RTX;
3085132727Skan
3086132727Skan  src = TREE_VALUE (arglist);
3087132727Skan  dest = TREE_VALUE (TREE_CHAIN (arglist));
3088132727Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
3089132727Skan
3090132727Skan  /* New argument list transforming bcopy(ptr x, ptr y, int z) to
3091132727Skan     memmove(ptr y, ptr x, size_t z).   This is done this way
3092132727Skan     so that if it isn't expanded inline, we fallback to
3093132727Skan     calling bcopy instead of memmove.  */
3094132727Skan
3095169699Skan  newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size));
3096132727Skan  newarglist = tree_cons (NULL_TREE, src, newarglist);
3097132727Skan  newarglist = tree_cons (NULL_TREE, dest, newarglist);
3098132727Skan
3099169699Skan  return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp);
3100132727Skan}
3101132727Skan
3102169699Skan#ifndef HAVE_movstr
3103169699Skan# define HAVE_movstr 0
3104169699Skan# define CODE_FOR_movstr CODE_FOR_nothing
3105169699Skan#endif
310690075Sobrien
3107169699Skan/* Expand into a movstr instruction, if one is available.  Return 0 if
3108169699Skan   we failed, the caller should emit a normal call, otherwise try to
3109169699Skan   get the result in TARGET, if convenient.  If ENDP is 0 return the
3110169699Skan   destination pointer, if ENDP is 1 return the end pointer ala
3111169699Skan   mempcpy, and if ENDP is 2 return the end pointer minus one ala
3112169699Skan   stpcpy.  */
3113169699Skan
311490075Sobrienstatic rtx
3115169699Skanexpand_movstr (tree dest, tree src, rtx target, int endp)
311690075Sobrien{
3117169699Skan  rtx end;
3118169699Skan  rtx dest_mem;
3119169699Skan  rtx src_mem;
3120169699Skan  rtx insn;
3121169699Skan  const struct insn_data * data;
312290075Sobrien
3123169699Skan  if (!HAVE_movstr)
312490075Sobrien    return 0;
312590075Sobrien
3126169699Skan  dest_mem = get_memory_rtx (dest, NULL);
3127169699Skan  src_mem = get_memory_rtx (src, NULL);
3128169699Skan  if (!endp)
3129169699Skan    {
3130169699Skan      target = force_reg (Pmode, XEXP (dest_mem, 0));
3131169699Skan      dest_mem = replace_equiv_address (dest_mem, target);
3132169699Skan      end = gen_reg_rtx (Pmode);
3133169699Skan    }
3134169699Skan  else
3135169699Skan    {
3136169699Skan      if (target == 0 || target == const0_rtx)
3137169699Skan	{
3138169699Skan	  end = gen_reg_rtx (Pmode);
3139169699Skan	  if (target == 0)
3140169699Skan	    target = end;
3141169699Skan	}
3142169699Skan      else
3143169699Skan	end = target;
3144169699Skan    }
3145132727Skan
3146169699Skan  data = insn_data + CODE_FOR_movstr;
3147132727Skan
3148169699Skan  if (data->operand[0].mode != VOIDmode)
3149169699Skan    end = gen_lowpart (data->operand[0].mode, end);
315090075Sobrien
3151169699Skan  insn = data->genfun (end, dest_mem, src_mem);
315290075Sobrien
3153169699Skan  gcc_assert (insn);
3154169699Skan
3155169699Skan  emit_insn (insn);
3156169699Skan
3157169699Skan  /* movstr is supposed to set end to the address of the NUL
3158169699Skan     terminator.  If the caller requested a mempcpy-like return value,
3159169699Skan     adjust it.  */
3160169699Skan  if (endp == 1 && target != const0_rtx)
3161169699Skan    {
3162169699Skan      rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
3163169699Skan      emit_move_insn (target, force_operand (tem, NULL_RTX));
3164169699Skan    }
3165169699Skan
3166169699Skan  return target;
316790075Sobrien}
316890075Sobrien
3169169699Skan/* Expand expression EXP, which is a call to the strcpy builtin.  Return 0
3170169699Skan   if we failed the caller should emit a normal call, otherwise try to get
3171169699Skan   the result in TARGET, if convenient (and in mode MODE if that's
3172169699Skan   convenient).  */
3173169699Skan
3174169699Skanstatic rtx
3175169699Skanexpand_builtin_strcpy (tree fndecl, tree arglist, rtx target, enum machine_mode mode)
3176169699Skan{
3177169699Skan  if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
3178169699Skan    {
3179169699Skan      tree result = fold_builtin_strcpy (fndecl, arglist, 0);
3180169699Skan      if (result)
3181169699Skan	{
3182169699Skan	  while (TREE_CODE (result) == COMPOUND_EXPR)
3183169699Skan	    {
3184169699Skan	      expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
3185169699Skan			   EXPAND_NORMAL);
3186169699Skan	      result = TREE_OPERAND (result, 1);
3187169699Skan	    }
3188169699Skan	  return expand_expr (result, target, mode, EXPAND_NORMAL);
3189169699Skan	}
3190169699Skan
3191169699Skan      return expand_movstr (TREE_VALUE (arglist),
3192169699Skan			    TREE_VALUE (TREE_CHAIN (arglist)),
3193169699Skan			    target, /*endp=*/0);
3194169699Skan    }
3195169699Skan  return 0;
3196169699Skan}
3197169699Skan
3198132727Skan/* Expand a call to the stpcpy builtin, with arguments in ARGLIST.
3199132727Skan   Return 0 if we failed the caller should emit a normal call,
3200132727Skan   otherwise try to get the result in TARGET, if convenient (and in
3201132727Skan   mode MODE if that's convenient).  */
3202132727Skan
3203132727Skanstatic rtx
3204169699Skanexpand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode)
3205132727Skan{
3206169699Skan  tree arglist = TREE_OPERAND (exp, 1);
3207169699Skan  /* If return value is ignored, transform stpcpy into strcpy.  */
3208169699Skan  if (target == const0_rtx)
3209169699Skan    {
3210169699Skan      tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
3211169699Skan      if (!fn)
3212169699Skan	return 0;
3213169699Skan
3214169699Skan      return expand_expr (build_function_call_expr (fn, arglist),
3215169699Skan			  target, mode, EXPAND_NORMAL);
3216169699Skan    }
3217169699Skan
3218132727Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
3219132727Skan    return 0;
3220132727Skan  else
3221132727Skan    {
3222169699Skan      tree dst, src, len, lenp1;
3223169699Skan      tree narglist;
3224169699Skan      rtx ret;
3225132727Skan
3226132727Skan      /* Ensure we get an actual string whose length can be evaluated at
3227169699Skan	 compile-time, not an expression containing a string.  This is
3228169699Skan	 because the latter will potentially produce pessimized code
3229169699Skan	 when used to produce the return value.  */
3230132727Skan      src = TREE_VALUE (TREE_CHAIN (arglist));
3231132727Skan      if (! c_getstr (src) || ! (len = c_strlen (src, 0)))
3232169699Skan	return expand_movstr (TREE_VALUE (arglist),
3233169699Skan			      TREE_VALUE (TREE_CHAIN (arglist)),
3234169699Skan			      target, /*endp=*/2);
3235132727Skan
3236132727Skan      dst = TREE_VALUE (arglist);
3237169699Skan      lenp1 = size_binop (PLUS_EXPR, len, ssize_int (1));
3238169699Skan      narglist = build_tree_list (NULL_TREE, lenp1);
3239169699Skan      narglist = tree_cons (NULL_TREE, src, narglist);
3240169699Skan      narglist = tree_cons (NULL_TREE, dst, narglist);
3241169699Skan      ret = expand_builtin_mempcpy (narglist, TREE_TYPE (exp),
3242169699Skan				    target, mode, /*endp=*/2);
3243169699Skan
3244169699Skan      if (ret)
3245169699Skan	return ret;
3246169699Skan
3247169699Skan      if (TREE_CODE (len) == INTEGER_CST)
3248169699Skan	{
3249169699Skan	  rtx len_rtx = expand_normal (len);
3250169699Skan
3251169699Skan	  if (GET_CODE (len_rtx) == CONST_INT)
3252169699Skan	    {
3253169699Skan	      ret = expand_builtin_strcpy (get_callee_fndecl (exp),
3254169699Skan					   arglist, target, mode);
3255169699Skan
3256169699Skan	      if (ret)
3257169699Skan		{
3258169699Skan		  if (! target)
3259169699Skan		    {
3260169699Skan		      if (mode != VOIDmode)
3261169699Skan			target = gen_reg_rtx (mode);
3262169699Skan		      else
3263169699Skan			target = gen_reg_rtx (GET_MODE (ret));
3264169699Skan		    }
3265169699Skan		  if (GET_MODE (target) != GET_MODE (ret))
3266169699Skan		    ret = gen_lowpart (GET_MODE (target), ret);
3267169699Skan
3268169699Skan		  ret = plus_constant (ret, INTVAL (len_rtx));
3269169699Skan		  ret = emit_move_insn (target, force_operand (ret, NULL_RTX));
3270169699Skan		  gcc_assert (ret);
3271169699Skan
3272169699Skan		  return target;
3273169699Skan		}
3274169699Skan	    }
3275169699Skan	}
3276169699Skan
3277169699Skan      return expand_movstr (TREE_VALUE (arglist),
3278169699Skan			    TREE_VALUE (TREE_CHAIN (arglist)),
3279169699Skan			    target, /*endp=*/2);
3280132727Skan    }
3281132727Skan}
3282132727Skan
328390075Sobrien/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
328490075Sobrien   bytes from constant string DATA + OFFSET and return it as target
328590075Sobrien   constant.  */
328690075Sobrien
328790075Sobrienstatic rtx
3288132727Skanbuiltin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
3289132727Skan			  enum machine_mode mode)
329090075Sobrien{
329190075Sobrien  const char *str = (const char *) data;
329290075Sobrien
329390075Sobrien  if ((unsigned HOST_WIDE_INT) offset > strlen (str))
329490075Sobrien    return const0_rtx;
329590075Sobrien
329690075Sobrien  return c_readstr (str + offset, mode);
329790075Sobrien}
329890075Sobrien
329990075Sobrien/* Expand expression EXP, which is a call to the strncpy builtin.  Return 0
330090075Sobrien   if we failed the caller should emit a normal call.  */
330190075Sobrien
330290075Sobrienstatic rtx
3303169699Skanexpand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode)
330490075Sobrien{
3305169699Skan  tree fndecl = get_callee_fndecl (exp);
3306169699Skan  tree arglist = TREE_OPERAND (exp, 1);
3307169699Skan  if (validate_arglist (arglist,
3308169699Skan			POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
330990075Sobrien    {
3310132727Skan      tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
331190075Sobrien      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
3312169699Skan      tree result = fold_builtin_strncpy (fndecl, arglist, slen);
331390075Sobrien
3314169699Skan      if (result)
3315117404Skan	{
3316169699Skan	  while (TREE_CODE (result) == COMPOUND_EXPR)
3317169699Skan	    {
3318169699Skan	      expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
3319169699Skan			   EXPAND_NORMAL);
3320169699Skan	      result = TREE_OPERAND (result, 1);
3321169699Skan	    }
3322169699Skan	  return expand_expr (result, target, mode, EXPAND_NORMAL);
332390075Sobrien	}
332490075Sobrien
3325169699Skan      /* We must be passed a constant len and src parameter.  */
3326169699Skan      if (!host_integerp (len, 1) || !slen || !host_integerp (slen, 1))
332790075Sobrien	return 0;
332890075Sobrien
332990075Sobrien      slen = size_binop (PLUS_EXPR, slen, ssize_int (1));
333090075Sobrien
333190075Sobrien      /* We're required to pad with trailing zeros if the requested
3332169699Skan	 len is greater than strlen(s2)+1.  In that case try to
333390075Sobrien	 use store_by_pieces, if it fails, punt.  */
333490075Sobrien      if (tree_int_cst_lt (slen, len))
333590075Sobrien	{
333690075Sobrien	  tree dest = TREE_VALUE (arglist);
333790075Sobrien	  unsigned int dest_align
333890075Sobrien	    = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
333990075Sobrien	  const char *p = c_getstr (TREE_VALUE (TREE_CHAIN (arglist)));
334090075Sobrien	  rtx dest_mem;
334190075Sobrien
334290075Sobrien	  if (!p || dest_align == 0 || !host_integerp (len, 1)
334390075Sobrien	      || !can_store_by_pieces (tree_low_cst (len, 1),
334490075Sobrien				       builtin_strncpy_read_str,
3345132727Skan				       (void *) p, dest_align))
334690075Sobrien	    return 0;
334790075Sobrien
3348169699Skan	  dest_mem = get_memory_rtx (dest, len);
334990075Sobrien	  store_by_pieces (dest_mem, tree_low_cst (len, 1),
335090075Sobrien			   builtin_strncpy_read_str,
3351132727Skan			   (void *) p, dest_align, 0);
3352117404Skan	  dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
3353132727Skan	  dest_mem = convert_memory_address (ptr_mode, dest_mem);
3354117404Skan	  return dest_mem;
335590075Sobrien	}
335690075Sobrien    }
3357169699Skan  return 0;
335890075Sobrien}
335990075Sobrien
336090075Sobrien/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
336190075Sobrien   bytes from constant string DATA + OFFSET and return it as target
336290075Sobrien   constant.  */
336390075Sobrien
336490075Sobrienstatic rtx
3365132727Skanbuiltin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
3366132727Skan			 enum machine_mode mode)
336790075Sobrien{
336890075Sobrien  const char *c = (const char *) data;
336990075Sobrien  char *p = alloca (GET_MODE_SIZE (mode));
337090075Sobrien
337190075Sobrien  memset (p, *c, GET_MODE_SIZE (mode));
337290075Sobrien
337390075Sobrien  return c_readstr (p, mode);
337490075Sobrien}
337590075Sobrien
3376117404Skan/* Callback routine for store_by_pieces.  Return the RTL of a register
3377117404Skan   containing GET_MODE_SIZE (MODE) consecutive copies of the unsigned
3378117404Skan   char value given in the RTL register data.  For example, if mode is
3379117404Skan   4 bytes wide, return the RTL for 0x01010101*data.  */
3380117404Skan
3381117404Skanstatic rtx
3382132727Skanbuiltin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
3383132727Skan			enum machine_mode mode)
3384117404Skan{
3385117404Skan  rtx target, coeff;
3386117404Skan  size_t size;
3387117404Skan  char *p;
3388117404Skan
3389117404Skan  size = GET_MODE_SIZE (mode);
3390117404Skan  if (size == 1)
3391117404Skan    return (rtx) data;
3392117404Skan
3393117404Skan  p = alloca (size);
3394117404Skan  memset (p, 1, size);
3395117404Skan  coeff = c_readstr (p, mode);
3396117404Skan
3397117404Skan  target = convert_to_mode (mode, (rtx) data, 1);
3398117404Skan  target = expand_mult (mode, target, coeff, NULL_RTX, 1);
3399117404Skan  return force_reg (mode, target);
3400117404Skan}
3401117404Skan
340290075Sobrien/* Expand expression EXP, which is a call to the memset builtin.  Return 0
340390075Sobrien   if we failed the caller should emit a normal call, otherwise try to get
340490075Sobrien   the result in TARGET, if convenient (and in mode MODE if that's
340590075Sobrien   convenient).  */
340690075Sobrien
340790075Sobrienstatic rtx
3408169699Skanexpand_builtin_memset (tree arglist, rtx target, enum machine_mode mode,
3409169699Skan		       tree orig_exp)
341090075Sobrien{
341190075Sobrien  if (!validate_arglist (arglist,
341290075Sobrien			 POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
341390075Sobrien    return 0;
341490075Sobrien  else
341590075Sobrien    {
341690075Sobrien      tree dest = TREE_VALUE (arglist);
341790075Sobrien      tree val = TREE_VALUE (TREE_CHAIN (arglist));
341890075Sobrien      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
3419169699Skan      tree fndecl, fn;
3420169699Skan      enum built_in_function fcode;
342190075Sobrien      char c;
3422169699Skan      unsigned int dest_align;
342390075Sobrien      rtx dest_mem, dest_addr, len_rtx;
342490075Sobrien
3425169699Skan      dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
3426169699Skan
342790075Sobrien      /* If DEST is not a pointer type, don't do this
342890075Sobrien	 operation in-line.  */
342990075Sobrien      if (dest_align == 0)
343090075Sobrien	return 0;
343190075Sobrien
343290075Sobrien      /* If the LEN parameter is zero, return DEST.  */
3433132727Skan      if (integer_zerop (len))
3434117404Skan	{
3435117404Skan	  /* Evaluate and ignore VAL in case it has side-effects.  */
3436117404Skan	  expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
3437117404Skan	  return expand_expr (dest, target, mode, EXPAND_NORMAL);
3438117404Skan	}
343990075Sobrien
3440169699Skan      /* Stabilize the arguments in case we fail.  */
3441169699Skan      dest = builtin_save_expr (dest);
3442169699Skan      val = builtin_save_expr (val);
3443169699Skan      len = builtin_save_expr (len);
3444169699Skan
3445169699Skan      len_rtx = expand_normal (len);
3446169699Skan      dest_mem = get_memory_rtx (dest, len);
3447169699Skan
344890075Sobrien      if (TREE_CODE (val) != INTEGER_CST)
3449117404Skan	{
3450117404Skan	  rtx val_rtx;
345190075Sobrien
3452169699Skan	  val_rtx = expand_normal (val);
3453169699Skan	  val_rtx = convert_to_mode (TYPE_MODE (unsigned_char_type_node),
3454169699Skan				     val_rtx, 0);
3455117404Skan
3456117404Skan	  /* Assume that we can memset by pieces if we can store the
3457117404Skan	   * the coefficients by pieces (in the required modes).
3458117404Skan	   * We can't pass builtin_memset_gen_str as that emits RTL.  */
3459117404Skan	  c = 1;
3460169699Skan	  if (host_integerp (len, 1)
3461169699Skan	      && !(optimize_size && tree_low_cst (len, 1) > 1)
3462169699Skan	      && can_store_by_pieces (tree_low_cst (len, 1),
3463169699Skan				      builtin_memset_read_str, &c, dest_align))
3464169699Skan	    {
3465169699Skan	      val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node),
3466169699Skan				   val_rtx);
3467169699Skan	      store_by_pieces (dest_mem, tree_low_cst (len, 1),
3468169699Skan			       builtin_memset_gen_str, val_rtx, dest_align, 0);
3469169699Skan	    }
3470169699Skan	  else if (!set_storage_via_setmem (dest_mem, len_rtx, val_rtx,
3471169699Skan					    dest_align))
3472169699Skan	    goto do_libcall;
3473117404Skan
3474117404Skan	  dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
3475132727Skan	  dest_mem = convert_memory_address (ptr_mode, dest_mem);
3476117404Skan	  return dest_mem;
3477117404Skan	}
3478117404Skan
347990075Sobrien      if (target_char_cast (val, &c))
3480169699Skan	goto do_libcall;
348190075Sobrien
348290075Sobrien      if (c)
348390075Sobrien	{
3484169699Skan	  if (host_integerp (len, 1)
3485169699Skan	      && !(optimize_size && tree_low_cst (len, 1) > 1)
3486169699Skan	      && can_store_by_pieces (tree_low_cst (len, 1),
3487169699Skan				      builtin_memset_read_str, &c, dest_align))
3488169699Skan	    store_by_pieces (dest_mem, tree_low_cst (len, 1),
3489169699Skan			     builtin_memset_read_str, &c, dest_align, 0);
3490169699Skan	  else if (!set_storage_via_setmem (dest_mem, len_rtx, GEN_INT (c),
3491169699Skan					    dest_align))
3492169699Skan	    goto do_libcall;
349390075Sobrien
3494117404Skan	  dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
3495132727Skan	  dest_mem = convert_memory_address (ptr_mode, dest_mem);
3496117404Skan	  return dest_mem;
349790075Sobrien	}
349890075Sobrien
349990075Sobrien      set_mem_align (dest_mem, dest_align);
3500169699Skan      dest_addr = clear_storage (dest_mem, len_rtx,
3501169699Skan				 CALL_EXPR_TAILCALL (orig_exp)
3502169699Skan				 ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
350390075Sobrien
350490075Sobrien      if (dest_addr == 0)
3505117404Skan	{
3506117404Skan	  dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
3507132727Skan	  dest_addr = convert_memory_address (ptr_mode, dest_addr);
3508117404Skan	}
350990075Sobrien
351090075Sobrien      return dest_addr;
3511169699Skan
3512169699Skan    do_libcall:
3513169699Skan      fndecl = get_callee_fndecl (orig_exp);
3514169699Skan      fcode = DECL_FUNCTION_CODE (fndecl);
3515169699Skan      gcc_assert (fcode == BUILT_IN_MEMSET || fcode == BUILT_IN_BZERO);
3516169699Skan      arglist = build_tree_list (NULL_TREE, len);
3517169699Skan      if (fcode == BUILT_IN_MEMSET)
3518169699Skan	arglist = tree_cons (NULL_TREE, val, arglist);
3519169699Skan      arglist = tree_cons (NULL_TREE, dest, arglist);
3520169699Skan      fn = build_function_call_expr (fndecl, arglist);
3521169699Skan      if (TREE_CODE (fn) == CALL_EXPR)
3522169699Skan	CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
3523169699Skan      return expand_call (fn, target, target == const0_rtx);
352490075Sobrien    }
352590075Sobrien}
352690075Sobrien
352790075Sobrien/* Expand expression EXP, which is a call to the bzero builtin.  Return 0
352890075Sobrien   if we failed the caller should emit a normal call.  */
352990075Sobrien
353090075Sobrienstatic rtx
3531169699Skanexpand_builtin_bzero (tree exp)
353290075Sobrien{
3533169699Skan  tree arglist = TREE_OPERAND (exp, 1);
353490075Sobrien  tree dest, size, newarglist;
353590075Sobrien
353690075Sobrien  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
353790075Sobrien    return NULL_RTX;
353890075Sobrien
353990075Sobrien  dest = TREE_VALUE (arglist);
354090075Sobrien  size = TREE_VALUE (TREE_CHAIN (arglist));
354190075Sobrien
354290075Sobrien  /* New argument list transforming bzero(ptr x, int y) to
354390075Sobrien     memset(ptr x, int 0, size_t y).   This is done this way
354490075Sobrien     so that if it isn't expanded inline, we fallback to
354590075Sobrien     calling bzero instead of memset.  */
354690075Sobrien
3547169699Skan  newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size));
354890075Sobrien  newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
354990075Sobrien  newarglist = tree_cons (NULL_TREE, dest, newarglist);
355090075Sobrien
3551169699Skan  return expand_builtin_memset (newarglist, const0_rtx, VOIDmode, exp);
355290075Sobrien}
355390075Sobrien
3554122190Skan/* Expand expression EXP, which is a call to the memcmp built-in function.
355590075Sobrien   ARGLIST is the argument list for this call.  Return 0 if we failed and the
355690075Sobrien   caller should emit a normal call, otherwise try to get the result in
355790075Sobrien   TARGET, if convenient (and in mode MODE, if that's convenient).  */
355890075Sobrien
355990075Sobrienstatic rtx
3560132727Skanexpand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
3561132727Skan		       enum machine_mode mode)
356290075Sobrien{
356390075Sobrien  if (!validate_arglist (arglist,
3564117404Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
356590075Sobrien    return 0;
3566169699Skan  else
356790075Sobrien    {
3568169699Skan      tree result = fold_builtin_memcmp (arglist);
3569169699Skan      if (result)
3570169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
357190075Sobrien    }
357290075Sobrien
3573169699Skan#if defined HAVE_cmpmemsi || defined HAVE_cmpstrnsi
357490075Sobrien  {
3575169699Skan    tree arg1 = TREE_VALUE (arglist);
3576169699Skan    tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
3577169699Skan    tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
357890075Sobrien    rtx arg1_rtx, arg2_rtx, arg3_rtx;
357990075Sobrien    rtx result;
358090075Sobrien    rtx insn;
358190075Sobrien
358290075Sobrien    int arg1_align
358390075Sobrien      = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
358490075Sobrien    int arg2_align
358590075Sobrien      = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
3586132727Skan    enum machine_mode insn_mode;
358790075Sobrien
3588132727Skan#ifdef HAVE_cmpmemsi
3589132727Skan    if (HAVE_cmpmemsi)
3590132727Skan      insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
3591132727Skan    else
3592132727Skan#endif
3593169699Skan#ifdef HAVE_cmpstrnsi
3594169699Skan    if (HAVE_cmpstrnsi)
3595169699Skan      insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
3596132727Skan    else
3597132727Skan#endif
3598169699Skan      return 0;
3599132727Skan
360090075Sobrien    /* If we don't have POINTER_TYPE, call the function.  */
360190075Sobrien    if (arg1_align == 0 || arg2_align == 0)
360290075Sobrien      return 0;
360390075Sobrien
360490075Sobrien    /* Make a place to write the result of the instruction.  */
360590075Sobrien    result = target;
360690075Sobrien    if (! (result != 0
3607169699Skan	   && REG_P (result) && GET_MODE (result) == insn_mode
360890075Sobrien	   && REGNO (result) >= FIRST_PSEUDO_REGISTER))
360990075Sobrien      result = gen_reg_rtx (insn_mode);
361090075Sobrien
3611169699Skan    arg1_rtx = get_memory_rtx (arg1, len);
3612169699Skan    arg2_rtx = get_memory_rtx (arg2, len);
3613169699Skan    arg3_rtx = expand_normal (len);
3614169699Skan
3615169699Skan    /* Set MEM_SIZE as appropriate.  */
3616169699Skan    if (GET_CODE (arg3_rtx) == CONST_INT)
3617169699Skan      {
3618169699Skan	set_mem_size (arg1_rtx, arg3_rtx);
3619169699Skan	set_mem_size (arg2_rtx, arg3_rtx);
3620169699Skan      }
3621169699Skan
3622132727Skan#ifdef HAVE_cmpmemsi
3623132727Skan    if (HAVE_cmpmemsi)
3624132727Skan      insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
3625132727Skan			   GEN_INT (MIN (arg1_align, arg2_align)));
362690075Sobrien    else
3627132727Skan#endif
3628169699Skan#ifdef HAVE_cmpstrnsi
3629169699Skan    if (HAVE_cmpstrnsi)
3630169699Skan      insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
3631169699Skan			    GEN_INT (MIN (arg1_align, arg2_align)));
3632132727Skan    else
3633132727Skan#endif
3634169699Skan      gcc_unreachable ();
363590075Sobrien
363690075Sobrien    if (insn)
363790075Sobrien      emit_insn (insn);
363890075Sobrien    else
363990075Sobrien      emit_library_call_value (memcmp_libfunc, result, LCT_PURE_MAKE_BLOCK,
364090075Sobrien			       TYPE_MODE (integer_type_node), 3,
364190075Sobrien			       XEXP (arg1_rtx, 0), Pmode,
364290075Sobrien			       XEXP (arg2_rtx, 0), Pmode,
364390075Sobrien			       convert_to_mode (TYPE_MODE (sizetype), arg3_rtx,
3644169699Skan						TYPE_UNSIGNED (sizetype)),
364590075Sobrien			       TYPE_MODE (sizetype));
364690075Sobrien
364790075Sobrien    /* Return the value in the proper mode for this function.  */
364890075Sobrien    mode = TYPE_MODE (TREE_TYPE (exp));
364990075Sobrien    if (GET_MODE (result) == mode)
365090075Sobrien      return result;
365190075Sobrien    else if (target != 0)
365290075Sobrien      {
365390075Sobrien	convert_move (target, result, 0);
365490075Sobrien	return target;
365590075Sobrien      }
365690075Sobrien    else
365790075Sobrien      return convert_to_mode (mode, result, 0);
365890075Sobrien  }
365990075Sobrien#endif
366090075Sobrien
366190075Sobrien  return 0;
366290075Sobrien}
366390075Sobrien
366490075Sobrien/* Expand expression EXP, which is a call to the strcmp builtin.  Return 0
366590075Sobrien   if we failed the caller should emit a normal call, otherwise try to get
366690075Sobrien   the result in TARGET, if convenient.  */
366790075Sobrien
366890075Sobrienstatic rtx
3669132727Skanexpand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
367090075Sobrien{
367190075Sobrien  tree arglist = TREE_OPERAND (exp, 1);
367290075Sobrien
367390075Sobrien  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
367490075Sobrien    return 0;
3675169699Skan  else
3676169699Skan    {
3677169699Skan      tree result = fold_builtin_strcmp (arglist);
3678169699Skan      if (result)
3679169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
3680169699Skan    }
368190075Sobrien
3682169699Skan#if defined HAVE_cmpstrsi || defined HAVE_cmpstrnsi
3683169699Skan  if (cmpstr_optab[SImode] != CODE_FOR_nothing
3684169699Skan      || cmpstrn_optab[SImode] != CODE_FOR_nothing)
3685169699Skan    {
3686169699Skan      rtx arg1_rtx, arg2_rtx;
3687169699Skan      rtx result, insn = NULL_RTX;
3688169699Skan      tree fndecl, fn;
368990075Sobrien
3690169699Skan      tree arg1 = TREE_VALUE (arglist);
3691169699Skan      tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
3692169699Skan      int arg1_align
3693169699Skan	= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
3694169699Skan      int arg2_align
3695169699Skan	= get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
3696132727Skan
3697169699Skan      /* If we don't have POINTER_TYPE, call the function.  */
3698169699Skan      if (arg1_align == 0 || arg2_align == 0)
3699169699Skan	return 0;
370090075Sobrien
3701169699Skan      /* Stabilize the arguments in case gen_cmpstr(n)si fail.  */
3702169699Skan      arg1 = builtin_save_expr (arg1);
3703169699Skan      arg2 = builtin_save_expr (arg2);
370490075Sobrien
3705169699Skan      arg1_rtx = get_memory_rtx (arg1, NULL);
3706169699Skan      arg2_rtx = get_memory_rtx (arg2, NULL);
370790075Sobrien
3708122190Skan#ifdef HAVE_cmpstrsi
3709169699Skan      /* Try to call cmpstrsi.  */
3710169699Skan      if (HAVE_cmpstrsi)
3711169699Skan	{
3712169699Skan	  enum machine_mode insn_mode
3713169699Skan	    = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
371490075Sobrien
3715169699Skan	  /* Make a place to write the result of the instruction.  */
3716169699Skan	  result = target;
3717169699Skan	  if (! (result != 0
3718169699Skan		 && REG_P (result) && GET_MODE (result) == insn_mode
3719169699Skan		 && REGNO (result) >= FIRST_PSEUDO_REGISTER))
3720169699Skan	    result = gen_reg_rtx (insn_mode);
372190075Sobrien
3722169699Skan	  insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx,
3723169699Skan			       GEN_INT (MIN (arg1_align, arg2_align)));
3724169699Skan	}
3725169699Skan#endif
3726169699Skan#ifdef HAVE_cmpstrnsi
3727169699Skan      /* Try to determine at least one length and call cmpstrnsi.  */
3728169699Skan      if (!insn && HAVE_cmpstrnsi)
3729169699Skan	{
3730169699Skan	  tree len;
3731169699Skan	  rtx arg3_rtx;
373290075Sobrien
3733169699Skan	  enum machine_mode insn_mode
3734169699Skan	    = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
3735169699Skan	  tree len1 = c_strlen (arg1, 1);
3736169699Skan	  tree len2 = c_strlen (arg2, 1);
373790075Sobrien
3738169699Skan	  if (len1)
3739169699Skan	    len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
3740169699Skan	  if (len2)
3741169699Skan	    len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
374290075Sobrien
3743169699Skan	  /* If we don't have a constant length for the first, use the length
3744169699Skan	     of the second, if we know it.  We don't require a constant for
3745169699Skan	     this case; some cost analysis could be done if both are available
3746169699Skan	     but neither is constant.  For now, assume they're equally cheap,
3747169699Skan	     unless one has side effects.  If both strings have constant lengths,
3748169699Skan	     use the smaller.  */
3749122190Skan
3750169699Skan	  if (!len1)
3751169699Skan	    len = len2;
3752169699Skan	  else if (!len2)
3753169699Skan	    len = len1;
3754169699Skan	  else if (TREE_SIDE_EFFECTS (len1))
3755169699Skan	    len = len2;
3756169699Skan	  else if (TREE_SIDE_EFFECTS (len2))
3757169699Skan	    len = len1;
3758169699Skan	  else if (TREE_CODE (len1) != INTEGER_CST)
3759169699Skan	    len = len2;
3760169699Skan	  else if (TREE_CODE (len2) != INTEGER_CST)
3761169699Skan	    len = len1;
3762169699Skan	  else if (tree_int_cst_lt (len1, len2))
3763169699Skan	    len = len1;
3764169699Skan	  else
3765169699Skan	    len = len2;
376690075Sobrien
3767169699Skan	  /* If both arguments have side effects, we cannot optimize.  */
3768169699Skan	  if (!len || TREE_SIDE_EFFECTS (len))
3769169699Skan	    goto do_libcall;
377090075Sobrien
3771169699Skan	  arg3_rtx = expand_normal (len);
377290075Sobrien
3773169699Skan	  /* Make a place to write the result of the instruction.  */
3774169699Skan	  result = target;
3775169699Skan	  if (! (result != 0
3776169699Skan		 && REG_P (result) && GET_MODE (result) == insn_mode
3777169699Skan		 && REGNO (result) >= FIRST_PSEUDO_REGISTER))
3778169699Skan	    result = gen_reg_rtx (insn_mode);
3779122190Skan
3780169699Skan	  insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
3781169699Skan				GEN_INT (MIN (arg1_align, arg2_align)));
3782169699Skan	}
3783169699Skan#endif
3784122190Skan
3785169699Skan      if (insn)
3786169699Skan	{
3787169699Skan	  emit_insn (insn);
3788132727Skan
3789169699Skan	  /* Return the value in the proper mode for this function.  */
3790169699Skan	  mode = TYPE_MODE (TREE_TYPE (exp));
3791169699Skan	  if (GET_MODE (result) == mode)
3792169699Skan	    return result;
3793169699Skan	  if (target == 0)
3794169699Skan	    return convert_to_mode (mode, result, 0);
3795169699Skan	  convert_move (target, result, 0);
3796169699Skan	  return target;
3797169699Skan	}
3798169699Skan
3799169699Skan      /* Expand the library call ourselves using a stabilized argument
3800169699Skan	 list to avoid re-evaluating the function's arguments twice.  */
3801169699Skan#ifdef HAVE_cmpstrnsi
3802169699Skan    do_libcall:
3803122190Skan#endif
3804169699Skan      arglist = build_tree_list (NULL_TREE, arg2);
3805169699Skan      arglist = tree_cons (NULL_TREE, arg1, arglist);
3806169699Skan      fndecl = get_callee_fndecl (exp);
3807169699Skan      fn = build_function_call_expr (fndecl, arglist);
3808169699Skan      if (TREE_CODE (fn) == CALL_EXPR)
3809169699Skan	CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
3810169699Skan      return expand_call (fn, target, target == const0_rtx);
3811169699Skan    }
3812169699Skan#endif
3813122190Skan  return 0;
381490075Sobrien}
381590075Sobrien
381690075Sobrien/* Expand expression EXP, which is a call to the strncmp builtin.  Return 0
381790075Sobrien   if we failed the caller should emit a normal call, otherwise try to get
381890075Sobrien   the result in TARGET, if convenient.  */
381990075Sobrien
382090075Sobrienstatic rtx
3821132727Skanexpand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
382290075Sobrien{
382390075Sobrien  tree arglist = TREE_OPERAND (exp, 1);
382490075Sobrien
382590075Sobrien  if (!validate_arglist (arglist,
382690075Sobrien			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
382790075Sobrien    return 0;
3828169699Skan  else
3829117404Skan    {
3830169699Skan      tree result = fold_builtin_strncmp (arglist);
3831169699Skan      if (result)
3832169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
3833117404Skan    }
383490075Sobrien
383590075Sobrien  /* If c_strlen can determine an expression for one of the string
3836169699Skan     lengths, and it doesn't have side effects, then emit cmpstrnsi
3837122190Skan     using length MIN(strlen(string)+1, arg3).  */
3838169699Skan#ifdef HAVE_cmpstrnsi
3839169699Skan  if (HAVE_cmpstrnsi)
3840132727Skan  {
3841169699Skan    tree arg1 = TREE_VALUE (arglist);
3842169699Skan    tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
3843169699Skan    tree arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
3844132727Skan    tree len, len1, len2;
3845132727Skan    rtx arg1_rtx, arg2_rtx, arg3_rtx;
3846132727Skan    rtx result, insn;
3847169699Skan    tree fndecl, fn;
384890075Sobrien
3849132727Skan    int arg1_align
3850132727Skan      = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
3851132727Skan    int arg2_align
3852132727Skan      = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
3853132727Skan    enum machine_mode insn_mode
3854169699Skan      = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
385590075Sobrien
3856132727Skan    len1 = c_strlen (arg1, 1);
3857132727Skan    len2 = c_strlen (arg2, 1);
385890075Sobrien
3859132727Skan    if (len1)
3860132727Skan      len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
3861132727Skan    if (len2)
3862132727Skan      len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
386390075Sobrien
3864132727Skan    /* If we don't have a constant length for the first, use the length
3865132727Skan       of the second, if we know it.  We don't require a constant for
3866132727Skan       this case; some cost analysis could be done if both are available
3867132727Skan       but neither is constant.  For now, assume they're equally cheap,
3868132727Skan       unless one has side effects.  If both strings have constant lengths,
3869132727Skan       use the smaller.  */
387090075Sobrien
3871132727Skan    if (!len1)
3872132727Skan      len = len2;
3873132727Skan    else if (!len2)
3874132727Skan      len = len1;
3875132727Skan    else if (TREE_SIDE_EFFECTS (len1))
3876132727Skan      len = len2;
3877132727Skan    else if (TREE_SIDE_EFFECTS (len2))
3878132727Skan      len = len1;
3879132727Skan    else if (TREE_CODE (len1) != INTEGER_CST)
3880132727Skan      len = len2;
3881132727Skan    else if (TREE_CODE (len2) != INTEGER_CST)
3882132727Skan      len = len1;
3883132727Skan    else if (tree_int_cst_lt (len1, len2))
3884132727Skan      len = len1;
3885132727Skan    else
3886132727Skan      len = len2;
388790075Sobrien
3888132727Skan    /* If both arguments have side effects, we cannot optimize.  */
3889132727Skan    if (!len || TREE_SIDE_EFFECTS (len))
3890132727Skan      return 0;
3891122190Skan
3892132727Skan    /* The actual new length parameter is MIN(len,arg3).  */
3893169699Skan    len = fold_build2 (MIN_EXPR, TREE_TYPE (len), len,
3894169699Skan		       fold_convert (TREE_TYPE (len), arg3));
3895122190Skan
3896132727Skan    /* If we don't have POINTER_TYPE, call the function.  */
3897132727Skan    if (arg1_align == 0 || arg2_align == 0)
3898132727Skan      return 0;
3899122190Skan
3900132727Skan    /* Make a place to write the result of the instruction.  */
3901132727Skan    result = target;
3902132727Skan    if (! (result != 0
3903169699Skan	   && REG_P (result) && GET_MODE (result) == insn_mode
3904132727Skan	   && REGNO (result) >= FIRST_PSEUDO_REGISTER))
3905132727Skan      result = gen_reg_rtx (insn_mode);
3906122190Skan
3907169699Skan    /* Stabilize the arguments in case gen_cmpstrnsi fails.  */
3908169699Skan    arg1 = builtin_save_expr (arg1);
3909169699Skan    arg2 = builtin_save_expr (arg2);
3910169699Skan    len = builtin_save_expr (len);
3911122190Skan
3912169699Skan    arg1_rtx = get_memory_rtx (arg1, len);
3913169699Skan    arg2_rtx = get_memory_rtx (arg2, len);
3914169699Skan    arg3_rtx = expand_normal (len);
3915169699Skan    insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
3916169699Skan			  GEN_INT (MIN (arg1_align, arg2_align)));
3917132727Skan    if (insn)
3918132727Skan      {
3919132727Skan	emit_insn (insn);
3920122190Skan
3921132727Skan	/* Return the value in the proper mode for this function.  */
3922132727Skan	mode = TYPE_MODE (TREE_TYPE (exp));
3923132727Skan	if (GET_MODE (result) == mode)
3924132727Skan	  return result;
3925132727Skan	if (target == 0)
3926132727Skan	  return convert_to_mode (mode, result, 0);
3927132727Skan	convert_move (target, result, 0);
3928132727Skan	return target;
3929132727Skan      }
3930132727Skan
3931132727Skan    /* Expand the library call ourselves using a stabilized argument
3932132727Skan       list to avoid re-evaluating the function's arguments twice.  */
3933132727Skan    arglist = build_tree_list (NULL_TREE, len);
3934132727Skan    arglist = tree_cons (NULL_TREE, arg2, arglist);
3935132727Skan    arglist = tree_cons (NULL_TREE, arg1, arglist);
3936132727Skan    fndecl = get_callee_fndecl (exp);
3937169699Skan    fn = build_function_call_expr (fndecl, arglist);
3938169699Skan    if (TREE_CODE (fn) == CALL_EXPR)
3939169699Skan      CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
3940169699Skan    return expand_call (fn, target, target == const0_rtx);
3941132727Skan  }
3942122190Skan#endif
3943122190Skan  return 0;
394490075Sobrien}
394590075Sobrien
394690075Sobrien/* Expand expression EXP, which is a call to the strcat builtin.
394790075Sobrien   Return 0 if we failed the caller should emit a normal call,
394890075Sobrien   otherwise try to get the result in TARGET, if convenient.  */
394990075Sobrien
395090075Sobrienstatic rtx
3951169699Skanexpand_builtin_strcat (tree fndecl, tree arglist, rtx target, enum machine_mode mode)
395290075Sobrien{
395390075Sobrien  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
395490075Sobrien    return 0;
395590075Sobrien  else
395690075Sobrien    {
395790075Sobrien      tree dst = TREE_VALUE (arglist),
3958169699Skan      src = TREE_VALUE (TREE_CHAIN (arglist));
395990075Sobrien      const char *p = c_getstr (src);
396090075Sobrien
3961169699Skan      /* If the string length is zero, return the dst parameter.  */
3962169699Skan      if (p && *p == '\0')
3963169699Skan	return expand_expr (dst, target, mode, EXPAND_NORMAL);
3964169699Skan
3965169699Skan      if (!optimize_size)
3966132727Skan	{
3967169699Skan	  /* See if we can store by pieces into (dst + strlen(dst)).  */
3968169699Skan	  tree newsrc, newdst,
3969169699Skan	    strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
3970169699Skan	  rtx insns;
3971169699Skan
3972169699Skan	  /* Stabilize the argument list.  */
3973169699Skan	  newsrc = builtin_save_expr (src);
3974169699Skan	  if (newsrc != src)
3975169699Skan	    arglist = build_tree_list (NULL_TREE, newsrc);
3976169699Skan	  else
3977169699Skan	    arglist = TREE_CHAIN (arglist); /* Reusing arglist if safe.  */
3978169699Skan
3979169699Skan	  dst = builtin_save_expr (dst);
3980169699Skan
3981169699Skan	  start_sequence ();
3982169699Skan
3983169699Skan	  /* Create strlen (dst).  */
3984169699Skan	  newdst =
3985169699Skan	    build_function_call_expr (strlen_fn,
3986169699Skan				      build_tree_list (NULL_TREE, dst));
3987169699Skan	  /* Create (dst + (cast) strlen (dst)).  */
3988169699Skan	  newdst = fold_convert (TREE_TYPE (dst), newdst);
3989169699Skan	  newdst = fold_build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst);
3990169699Skan
3991169699Skan	  newdst = builtin_save_expr (newdst);
3992169699Skan	  arglist = tree_cons (NULL_TREE, newdst, arglist);
3993169699Skan
3994169699Skan	  if (!expand_builtin_strcpy (fndecl, arglist, target, mode))
3995132727Skan	    {
3996169699Skan	      end_sequence (); /* Stop sequence.  */
3997169699Skan	      return 0;
3998169699Skan	    }
399990075Sobrien
4000169699Skan	  /* Output the entire sequence.  */
4001169699Skan	  insns = get_insns ();
4002169699Skan	  end_sequence ();
4003169699Skan	  emit_insn (insns);
4004132727Skan
4005169699Skan	  return expand_expr (dst, target, mode, EXPAND_NORMAL);
4006132727Skan	}
4007132727Skan
400890075Sobrien      return 0;
400990075Sobrien    }
401090075Sobrien}
401190075Sobrien
401290075Sobrien/* Expand expression EXP, which is a call to the strncat builtin.
401390075Sobrien   Return 0 if we failed the caller should emit a normal call,
401490075Sobrien   otherwise try to get the result in TARGET, if convenient.  */
401590075Sobrien
401690075Sobrienstatic rtx
4017132727Skanexpand_builtin_strncat (tree arglist, rtx target, enum machine_mode mode)
401890075Sobrien{
4019169699Skan  if (validate_arglist (arglist,
4020169699Skan			POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
402190075Sobrien    {
4022169699Skan      tree result = fold_builtin_strncat (arglist);
4023169699Skan      if (result)
4024169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
402590075Sobrien    }
4026169699Skan  return 0;
402790075Sobrien}
402890075Sobrien
402990075Sobrien/* Expand expression EXP, which is a call to the strspn builtin.
403090075Sobrien   Return 0 if we failed the caller should emit a normal call,
403190075Sobrien   otherwise try to get the result in TARGET, if convenient.  */
403290075Sobrien
403390075Sobrienstatic rtx
4034132727Skanexpand_builtin_strspn (tree arglist, rtx target, enum machine_mode mode)
403590075Sobrien{
4036169699Skan  if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
403790075Sobrien    {
4038169699Skan      tree result = fold_builtin_strspn (arglist);
4039169699Skan      if (result)
4040169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
404190075Sobrien    }
4042169699Skan  return 0;
404390075Sobrien}
404490075Sobrien
404590075Sobrien/* Expand expression EXP, which is a call to the strcspn builtin.
404690075Sobrien   Return 0 if we failed the caller should emit a normal call,
404790075Sobrien   otherwise try to get the result in TARGET, if convenient.  */
404890075Sobrien
404990075Sobrienstatic rtx
4050132727Skanexpand_builtin_strcspn (tree arglist, rtx target, enum machine_mode mode)
405190075Sobrien{
4052169699Skan  if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
405390075Sobrien    {
4054169699Skan      tree result = fold_builtin_strcspn (arglist);
4055169699Skan      if (result)
4056169699Skan	return expand_expr (result, target, mode, EXPAND_NORMAL);
405790075Sobrien    }
4058169699Skan  return 0;
405990075Sobrien}
406090075Sobrien
406190075Sobrien/* Expand a call to __builtin_saveregs, generating the result in TARGET,
406290075Sobrien   if that's convenient.  */
406390075Sobrien
406490075Sobrienrtx
4065132727Skanexpand_builtin_saveregs (void)
406690075Sobrien{
406790075Sobrien  rtx val, seq;
406890075Sobrien
406990075Sobrien  /* Don't do __builtin_saveregs more than once in a function.
407090075Sobrien     Save the result of the first call and reuse it.  */
407190075Sobrien  if (saveregs_value != 0)
407290075Sobrien    return saveregs_value;
407390075Sobrien
407490075Sobrien  /* When this function is called, it means that registers must be
407590075Sobrien     saved on entry to this function.  So we migrate the call to the
407690075Sobrien     first insn of this function.  */
407790075Sobrien
407890075Sobrien  start_sequence ();
407990075Sobrien
408090075Sobrien  /* Do whatever the machine needs done in this case.  */
4081132727Skan  val = targetm.calls.expand_builtin_saveregs ();
408290075Sobrien
408390075Sobrien  seq = get_insns ();
408490075Sobrien  end_sequence ();
408590075Sobrien
408690075Sobrien  saveregs_value = val;
408790075Sobrien
4088117404Skan  /* Put the insns after the NOTE that starts the function.  If this
4089117404Skan     is inside a start_sequence, make the outer-level insn chain current, so
409090075Sobrien     the code is placed at the start of the function.  */
409190075Sobrien  push_topmost_sequence ();
4092169699Skan  emit_insn_after (seq, entry_of_function ());
409390075Sobrien  pop_topmost_sequence ();
409490075Sobrien
409590075Sobrien  return val;
409690075Sobrien}
409790075Sobrien
409890075Sobrien/* __builtin_args_info (N) returns word N of the arg space info
409990075Sobrien   for the current function.  The number and meanings of words
410090075Sobrien   is controlled by the definition of CUMULATIVE_ARGS.  */
410190075Sobrien
410290075Sobrienstatic rtx
4103132727Skanexpand_builtin_args_info (tree arglist)
410490075Sobrien{
410590075Sobrien  int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int);
410690075Sobrien  int *word_ptr = (int *) &current_function_args_info;
410790075Sobrien
4108169699Skan  gcc_assert (sizeof (CUMULATIVE_ARGS) % sizeof (int) == 0);
410990075Sobrien
411090075Sobrien  if (arglist != 0)
411190075Sobrien    {
411290075Sobrien      if (!host_integerp (TREE_VALUE (arglist), 0))
4113169699Skan	error ("argument of %<__builtin_args_info%> must be constant");
411490075Sobrien      else
411590075Sobrien	{
411690075Sobrien	  HOST_WIDE_INT wordnum = tree_low_cst (TREE_VALUE (arglist), 0);
411790075Sobrien
411890075Sobrien	  if (wordnum < 0 || wordnum >= nwords)
4119169699Skan	    error ("argument of %<__builtin_args_info%> out of range");
412090075Sobrien	  else
412190075Sobrien	    return GEN_INT (word_ptr[wordnum]);
412290075Sobrien	}
412390075Sobrien    }
412490075Sobrien  else
4125169699Skan    error ("missing argument in %<__builtin_args_info%>");
412690075Sobrien
412790075Sobrien  return const0_rtx;
412890075Sobrien}
412990075Sobrien
4130169699Skan/* Expand a call to __builtin_next_arg.  */
413190075Sobrien
413290075Sobrienstatic rtx
4133169699Skanexpand_builtin_next_arg (void)
413490075Sobrien{
4135169699Skan  /* Checking arguments is already done in fold_builtin_next_arg
4136169699Skan     that must be called before this function.  */
413790075Sobrien  return expand_binop (Pmode, add_optab,
413890075Sobrien		       current_function_internal_arg_pointer,
413990075Sobrien		       current_function_arg_offset_rtx,
414090075Sobrien		       NULL_RTX, 0, OPTAB_LIB_WIDEN);
414190075Sobrien}
414290075Sobrien
414390075Sobrien/* Make it easier for the backends by protecting the valist argument
414490075Sobrien   from multiple evaluations.  */
414590075Sobrien
414690075Sobrienstatic tree
4147132727Skanstabilize_va_list (tree valist, int needs_lvalue)
414890075Sobrien{
414990075Sobrien  if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
415090075Sobrien    {
415190075Sobrien      if (TREE_SIDE_EFFECTS (valist))
415290075Sobrien	valist = save_expr (valist);
415390075Sobrien
415490075Sobrien      /* For this case, the backends will be expecting a pointer to
415590075Sobrien	 TREE_TYPE (va_list_type_node), but it's possible we've
415690075Sobrien	 actually been given an array (an actual va_list_type_node).
415790075Sobrien	 So fix it.  */
415890075Sobrien      if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
415990075Sobrien	{
416090075Sobrien	  tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
4161169699Skan	  valist = build_fold_addr_expr_with_type (valist, p1);
416290075Sobrien	}
416390075Sobrien    }
416490075Sobrien  else
416590075Sobrien    {
416690075Sobrien      tree pt;
416790075Sobrien
416890075Sobrien      if (! needs_lvalue)
416990075Sobrien	{
417090075Sobrien	  if (! TREE_SIDE_EFFECTS (valist))
417190075Sobrien	    return valist;
417290075Sobrien
417390075Sobrien	  pt = build_pointer_type (va_list_type_node);
4174169699Skan	  valist = fold_build1 (ADDR_EXPR, pt, valist);
417590075Sobrien	  TREE_SIDE_EFFECTS (valist) = 1;
417690075Sobrien	}
417790075Sobrien
417890075Sobrien      if (TREE_SIDE_EFFECTS (valist))
417990075Sobrien	valist = save_expr (valist);
4180169699Skan      valist = build_fold_indirect_ref (valist);
418190075Sobrien    }
418290075Sobrien
418390075Sobrien  return valist;
418490075Sobrien}
418590075Sobrien
4186132727Skan/* The "standard" definition of va_list is void*.  */
4187132727Skan
4188132727Skantree
4189132727Skanstd_build_builtin_va_list (void)
4190132727Skan{
4191132727Skan  return ptr_type_node;
4192132727Skan}
4193132727Skan
419490075Sobrien/* The "standard" implementation of va_start: just assign `nextarg' to
419590075Sobrien   the variable.  */
419690075Sobrien
419790075Sobrienvoid
4198132727Skanstd_expand_builtin_va_start (tree valist, rtx nextarg)
419990075Sobrien{
420090075Sobrien  tree t;
420190075Sobrien
4202169699Skan  t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
4203169699Skan	      make_tree (ptr_type_node, nextarg));
420490075Sobrien  TREE_SIDE_EFFECTS (t) = 1;
420590075Sobrien
420690075Sobrien  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
420790075Sobrien}
420890075Sobrien
4209117404Skan/* Expand ARGLIST, from a call to __builtin_va_start.  */
421090075Sobrien
421190075Sobrienstatic rtx
4212132727Skanexpand_builtin_va_start (tree arglist)
421390075Sobrien{
421490075Sobrien  rtx nextarg;
4215117404Skan  tree chain, valist;
421690075Sobrien
4217117404Skan  chain = TREE_CHAIN (arglist);
421890075Sobrien
4219169699Skan  if (!chain)
4220169699Skan    {
4221169699Skan      error ("too few arguments to function %<va_start%>");
4222169699Skan      return const0_rtx;
4223169699Skan    }
422490075Sobrien
4225169699Skan  if (fold_builtin_next_arg (chain))
4226169699Skan    return const0_rtx;
4227169699Skan
4228169699Skan  nextarg = expand_builtin_next_arg ();
422990075Sobrien  valist = stabilize_va_list (TREE_VALUE (arglist), 1);
423090075Sobrien
423190075Sobrien#ifdef EXPAND_BUILTIN_VA_START
4232117404Skan  EXPAND_BUILTIN_VA_START (valist, nextarg);
423390075Sobrien#else
4234117404Skan  std_expand_builtin_va_start (valist, nextarg);
423590075Sobrien#endif
423690075Sobrien
423790075Sobrien  return const0_rtx;
423890075Sobrien}
423990075Sobrien
424090075Sobrien/* The "standard" implementation of va_arg: read the value from the
424190075Sobrien   current (padded) address and increment by the (padded) size.  */
424290075Sobrien
4243169699Skantree
4244169699Skanstd_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
424590075Sobrien{
4246169699Skan  tree addr, t, type_size, rounded_size, valist_tmp;
4247169699Skan  unsigned HOST_WIDE_INT align, boundary;
4248169699Skan  bool indirect;
424990075Sobrien
4250169699Skan#ifdef ARGS_GROW_DOWNWARD
4251169699Skan  /* All of the alignment and movement below is for args-grow-up machines.
4252169699Skan     As of 2004, there are only 3 ARGS_GROW_DOWNWARD targets, and they all
4253169699Skan     implement their own specialized gimplify_va_arg_expr routines.  */
4254169699Skan  gcc_unreachable ();
4255169699Skan#endif
4256132727Skan
4257169699Skan  indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
4258169699Skan  if (indirect)
4259169699Skan    type = build_pointer_type (type);
4260169699Skan
4261169699Skan  align = PARM_BOUNDARY / BITS_PER_UNIT;
4262169699Skan  boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type) / BITS_PER_UNIT;
4263169699Skan
4264169699Skan  /* Hoist the valist value into a temporary for the moment.  */
4265169699Skan  valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
4266169699Skan
4267132727Skan  /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
4268132727Skan     requires greater alignment, we must perform dynamic alignment.  */
4269169699Skan  if (boundary > align
4270169699Skan      && !integer_zerop (TYPE_SIZE (type)))
4271169699Skan    {
4272169699Skan      t = fold_convert (TREE_TYPE (valist), size_int (boundary - 1));
4273169699Skan      t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
4274169699Skan		  build2 (PLUS_EXPR, TREE_TYPE (valist), valist_tmp, t));
4275169699Skan      gimplify_and_add (t, pre_p);
4276132727Skan
4277169699Skan      t = fold_convert (TREE_TYPE (valist), size_int (-boundary));
4278169699Skan      t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
4279169699Skan		  build2 (BIT_AND_EXPR, TREE_TYPE (valist), valist_tmp, t));
4280169699Skan      gimplify_and_add (t, pre_p);
4281132727Skan    }
428296263Sobrien  else
4283169699Skan    boundary = align;
428490075Sobrien
4285169699Skan  /* If the actual alignment is less than the alignment of the type,
4286169699Skan     adjust the type accordingly so that we don't assume strict alignment
4287169699Skan     when deferencing the pointer.  */
4288169699Skan  boundary *= BITS_PER_UNIT;
4289169699Skan  if (boundary < TYPE_ALIGN (type))
429090075Sobrien    {
4291169699Skan      type = build_variant_type_copy (type);
4292169699Skan      TYPE_ALIGN (type) = boundary;
429390075Sobrien    }
429490075Sobrien
4295169699Skan  /* Compute the rounded size of the type.  */
4296169699Skan  type_size = size_in_bytes (type);
4297169699Skan  rounded_size = round_up (type_size, align);
429890075Sobrien
4299169699Skan  /* Reduce rounded_size so it's sharable with the postqueue.  */
4300169699Skan  gimplify_expr (&rounded_size, pre_p, post_p, is_gimple_val, fb_rvalue);
4301169699Skan
4302169699Skan  /* Get AP.  */
4303169699Skan  addr = valist_tmp;
4304169699Skan  if (PAD_VARARGS_DOWN && !integer_zerop (rounded_size))
430596263Sobrien    {
4306169699Skan      /* Small args are padded downward.  */
4307169699Skan      t = fold_build2 (GT_EXPR, sizetype, rounded_size, size_int (align));
4308169699Skan      t = fold_build3 (COND_EXPR, sizetype, t, size_zero_node,
4309169699Skan		       size_binop (MINUS_EXPR, rounded_size, type_size));
4310169699Skan      t = fold_convert (TREE_TYPE (addr), t);
4311169699Skan      addr = fold_build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t);
431296263Sobrien    }
431390075Sobrien
4314169699Skan  /* Compute new value for AP.  */
4315169699Skan  t = fold_convert (TREE_TYPE (valist), rounded_size);
4316169699Skan  t = build2 (PLUS_EXPR, TREE_TYPE (valist), valist_tmp, t);
4317169699Skan  t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
4318169699Skan  gimplify_and_add (t, pre_p);
4319169699Skan
4320169699Skan  addr = fold_convert (build_pointer_type (type), addr);
4321169699Skan
4322169699Skan  if (indirect)
4323169699Skan    addr = build_va_arg_indirect_ref (addr);
4324169699Skan
4325169699Skan  return build_va_arg_indirect_ref (addr);
4326169699Skan}
4327169699Skan
4328169699Skan/* Build an indirect-ref expression over the given TREE, which represents a
4329169699Skan   piece of a va_arg() expansion.  */
4330169699Skantree
4331169699Skanbuild_va_arg_indirect_ref (tree addr)
4332169699Skan{
4333169699Skan  addr = build_fold_indirect_ref (addr);
4334169699Skan
4335169699Skan  if (flag_mudflap) /* Don't instrument va_arg INDIRECT_REF.  */
4336169699Skan    mf_mark (addr);
4337169699Skan
433890075Sobrien  return addr;
433990075Sobrien}
434090075Sobrien
4341169699Skan/* Return a dummy expression of type TYPE in order to keep going after an
4342169699Skan   error.  */
434390075Sobrien
4344169699Skanstatic tree
4345169699Skandummy_object (tree type)
434690075Sobrien{
4347169699Skan  tree t = build_int_cst (build_pointer_type (type), 0);
4348169699Skan  return build1 (INDIRECT_REF, type, t);
4349169699Skan}
4350169699Skan
4351169699Skan/* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a
4352169699Skan   builtin function, but a very special sort of operator.  */
4353169699Skan
4354169699Skanenum gimplify_status
4355169699Skangimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p)
4356169699Skan{
435790075Sobrien  tree promoted_type, want_va_type, have_va_type;
4358169699Skan  tree valist = TREE_OPERAND (*expr_p, 0);
4359169699Skan  tree type = TREE_TYPE (*expr_p);
4360169699Skan  tree t;
436190075Sobrien
436290075Sobrien  /* Verify that valist is of the proper type.  */
436390075Sobrien  want_va_type = va_list_type_node;
436490075Sobrien  have_va_type = TREE_TYPE (valist);
4365169699Skan
4366169699Skan  if (have_va_type == error_mark_node)
4367169699Skan    return GS_ERROR;
4368169699Skan
436990075Sobrien  if (TREE_CODE (want_va_type) == ARRAY_TYPE)
437090075Sobrien    {
437190075Sobrien      /* If va_list is an array type, the argument may have decayed
437290075Sobrien	 to a pointer type, e.g. by being passed to another function.
4373169699Skan	 In that case, unwrap both types so that we can compare the
437490075Sobrien	 underlying records.  */
437590075Sobrien      if (TREE_CODE (have_va_type) == ARRAY_TYPE
4376169699Skan	  || POINTER_TYPE_P (have_va_type))
437790075Sobrien	{
437890075Sobrien	  want_va_type = TREE_TYPE (want_va_type);
437990075Sobrien	  have_va_type = TREE_TYPE (have_va_type);
438090075Sobrien	}
438190075Sobrien    }
4382169699Skan
438390075Sobrien  if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type))
438490075Sobrien    {
4385169699Skan      error ("first argument to %<va_arg%> not of type %<va_list%>");
4386169699Skan      return GS_ERROR;
438790075Sobrien    }
438890075Sobrien
438990075Sobrien  /* Generate a diagnostic for requesting data of a type that cannot
439090075Sobrien     be passed through `...' due to type promotion at the call site.  */
4391169699Skan  else if ((promoted_type = lang_hooks.types.type_promotes_to (type))
4392117404Skan	   != type)
439390075Sobrien    {
439496263Sobrien      static bool gave_help;
439590075Sobrien
439696263Sobrien      /* Unfortunately, this is merely undefined, rather than a constraint
439796263Sobrien	 violation, so we cannot make this an error.  If this call is never
439896263Sobrien	 executed, the program is still strictly conforming.  */
4399169699Skan      warning (0, "%qT is promoted to %qT when passed through %<...%>",
4400169699Skan	       type, promoted_type);
440190075Sobrien      if (! gave_help)
440290075Sobrien	{
440396263Sobrien	  gave_help = true;
4404169699Skan	  warning (0, "(so you should pass %qT not %qT to %<va_arg%>)",
4405169699Skan		   promoted_type, type);
440690075Sobrien	}
440790075Sobrien
440896263Sobrien      /* We can, however, treat "undefined" any way we please.
440996263Sobrien	 Call abort to encourage the user to fix the program.  */
4410132727Skan      inform ("if this code is reached, the program will abort");
4411169699Skan      t = build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP],
4412169699Skan				    NULL);
4413169699Skan      append_to_statement_list (t, pre_p);
441496263Sobrien
441596263Sobrien      /* This is dead code, but go ahead and finish so that the
441696263Sobrien	 mode of the result comes out right.  */
4417169699Skan      *expr_p = dummy_object (type);
4418169699Skan      return GS_ALL_DONE;
441990075Sobrien    }
442090075Sobrien  else
442190075Sobrien    {
442290075Sobrien      /* Make it easier for the backends by protecting the valist argument
4423169699Skan	 from multiple evaluations.  */
4424169699Skan      if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
4425169699Skan	{
4426169699Skan	  /* For this case, the backends will be expecting a pointer to
4427169699Skan	     TREE_TYPE (va_list_type_node), but it's possible we've
4428169699Skan	     actually been given an array (an actual va_list_type_node).
4429169699Skan	     So fix it.  */
4430169699Skan	  if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
4431169699Skan	    {
4432169699Skan	      tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
4433169699Skan	      valist = build_fold_addr_expr_with_type (valist, p1);
4434169699Skan	    }
4435169699Skan	  gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue);
4436169699Skan	}
4437169699Skan      else
4438169699Skan	gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
443990075Sobrien
4440169699Skan      if (!targetm.gimplify_va_arg_expr)
4441169699Skan	/* FIXME:Once most targets are converted we should merely
4442169699Skan	   assert this is non-null.  */
4443169699Skan	return GS_ALL_DONE;
4444169699Skan
4445169699Skan      *expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
4446169699Skan      return GS_OK;
444790075Sobrien    }
444890075Sobrien}
444990075Sobrien
445090075Sobrien/* Expand ARGLIST, from a call to __builtin_va_end.  */
445190075Sobrien
445290075Sobrienstatic rtx
4453132727Skanexpand_builtin_va_end (tree arglist)
445490075Sobrien{
445590075Sobrien  tree valist = TREE_VALUE (arglist);
445690075Sobrien
445790075Sobrien  /* Evaluate for side effects, if needed.  I hate macros that don't
445890075Sobrien     do that.  */
445990075Sobrien  if (TREE_SIDE_EFFECTS (valist))
446090075Sobrien    expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL);
446190075Sobrien
446290075Sobrien  return const0_rtx;
446390075Sobrien}
446490075Sobrien
446590075Sobrien/* Expand ARGLIST, from a call to __builtin_va_copy.  We do this as a
446690075Sobrien   builtin rather than just as an assignment in stdarg.h because of the
446790075Sobrien   nastiness of array-type va_list types.  */
446890075Sobrien
446990075Sobrienstatic rtx
4470132727Skanexpand_builtin_va_copy (tree arglist)
447190075Sobrien{
447290075Sobrien  tree dst, src, t;
447390075Sobrien
447490075Sobrien  dst = TREE_VALUE (arglist);
447590075Sobrien  src = TREE_VALUE (TREE_CHAIN (arglist));
447690075Sobrien
447790075Sobrien  dst = stabilize_va_list (dst, 1);
447890075Sobrien  src = stabilize_va_list (src, 0);
447990075Sobrien
448090075Sobrien  if (TREE_CODE (va_list_type_node) != ARRAY_TYPE)
448190075Sobrien    {
4482169699Skan      t = build2 (MODIFY_EXPR, va_list_type_node, dst, src);
448390075Sobrien      TREE_SIDE_EFFECTS (t) = 1;
448490075Sobrien      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
448590075Sobrien    }
448690075Sobrien  else
448790075Sobrien    {
448890075Sobrien      rtx dstb, srcb, size;
448990075Sobrien
449090075Sobrien      /* Evaluate to pointers.  */
449190075Sobrien      dstb = expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL);
449290075Sobrien      srcb = expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL);
449390075Sobrien      size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX,
449490075Sobrien			  VOIDmode, EXPAND_NORMAL);
449590075Sobrien
4496132727Skan      dstb = convert_memory_address (Pmode, dstb);
4497132727Skan      srcb = convert_memory_address (Pmode, srcb);
449890075Sobrien
449990075Sobrien      /* "Dereference" to BLKmode memories.  */
450090075Sobrien      dstb = gen_rtx_MEM (BLKmode, dstb);
450190075Sobrien      set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst))));
450290075Sobrien      set_mem_align (dstb, TYPE_ALIGN (va_list_type_node));
450390075Sobrien      srcb = gen_rtx_MEM (BLKmode, srcb);
450490075Sobrien      set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src))));
450590075Sobrien      set_mem_align (srcb, TYPE_ALIGN (va_list_type_node));
450690075Sobrien
450790075Sobrien      /* Copy.  */
4508117404Skan      emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL);
450990075Sobrien    }
451090075Sobrien
451190075Sobrien  return const0_rtx;
451290075Sobrien}
451390075Sobrien
451490075Sobrien/* Expand a call to one of the builtin functions __builtin_frame_address or
451590075Sobrien   __builtin_return_address.  */
451690075Sobrien
451790075Sobrienstatic rtx
4518132727Skanexpand_builtin_frame_address (tree fndecl, tree arglist)
451990075Sobrien{
452090075Sobrien  /* The argument must be a nonnegative integer constant.
452190075Sobrien     It counts the number of frames to scan up the stack.
452290075Sobrien     The value is the return address saved in that frame.  */
452390075Sobrien  if (arglist == 0)
452490075Sobrien    /* Warning about missing arg was already issued.  */
452590075Sobrien    return const0_rtx;
452690075Sobrien  else if (! host_integerp (TREE_VALUE (arglist), 1))
452790075Sobrien    {
452890075Sobrien      if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
4529169699Skan	error ("invalid argument to %<__builtin_frame_address%>");
453090075Sobrien      else
4531169699Skan	error ("invalid argument to %<__builtin_return_address%>");
453290075Sobrien      return const0_rtx;
453390075Sobrien    }
453490075Sobrien  else
453590075Sobrien    {
453690075Sobrien      rtx tem
453790075Sobrien	= expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl),
4538169699Skan				      tree_low_cst (TREE_VALUE (arglist), 1));
453990075Sobrien
454090075Sobrien      /* Some ports cannot access arbitrary stack frames.  */
454190075Sobrien      if (tem == NULL)
454290075Sobrien	{
454390075Sobrien	  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
4544169699Skan	    warning (0, "unsupported argument to %<__builtin_frame_address%>");
454590075Sobrien	  else
4546169699Skan	    warning (0, "unsupported argument to %<__builtin_return_address%>");
454790075Sobrien	  return const0_rtx;
454890075Sobrien	}
454990075Sobrien
455090075Sobrien      /* For __builtin_frame_address, return what we've got.  */
455190075Sobrien      if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
455290075Sobrien	return tem;
455390075Sobrien
4554169699Skan      if (!REG_P (tem)
455590075Sobrien	  && ! CONSTANT_P (tem))
455690075Sobrien	tem = copy_to_mode_reg (Pmode, tem);
455790075Sobrien      return tem;
455890075Sobrien    }
455990075Sobrien}
456090075Sobrien
456190075Sobrien/* Expand a call to the alloca builtin, with arguments ARGLIST.  Return 0 if
456290075Sobrien   we failed and the caller should emit a normal call, otherwise try to get
456390075Sobrien   the result in TARGET, if convenient.  */
456490075Sobrien
456590075Sobrienstatic rtx
4566132727Skanexpand_builtin_alloca (tree arglist, rtx target)
456790075Sobrien{
456890075Sobrien  rtx op0;
456990075Sobrien  rtx result;
457090075Sobrien
4571169699Skan  /* In -fmudflap-instrumented code, alloca() and __builtin_alloca()
4572169699Skan     should always expand to function calls.  These can be intercepted
4573169699Skan     in libmudflap.  */
4574169699Skan  if (flag_mudflap)
4575169699Skan    return 0;
4576169699Skan
457790075Sobrien  if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
457890075Sobrien    return 0;
457990075Sobrien
458090075Sobrien  /* Compute the argument.  */
4581169699Skan  op0 = expand_normal (TREE_VALUE (arglist));
458290075Sobrien
458390075Sobrien  /* Allocate the desired space.  */
458490075Sobrien  result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
4585132727Skan  result = convert_memory_address (ptr_mode, result);
458690075Sobrien
458790075Sobrien  return result;
458890075Sobrien}
458990075Sobrien
4590259563Spfg/* Expand a call to a bswap builtin.  The arguments are in ARGLIST.  MODE
4591259563Spfg   is the mode to expand with.  */
4592259563Spfg
4593259563Spfgstatic rtx
4594259563Spfgexpand_builtin_bswap (tree arglist, rtx target, rtx subtarget)
4595259563Spfg{
4596259563Spfg  enum machine_mode mode;
4597259563Spfg  tree arg;
4598259563Spfg  rtx op0;
4599259563Spfg
4600259563Spfg  if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
4601259563Spfg    return 0;
4602259563Spfg
4603259563Spfg  arg = TREE_VALUE (arglist);
4604259563Spfg  mode = TYPE_MODE (TREE_TYPE (arg));
4605259563Spfg  op0 = expand_expr (arg, subtarget, VOIDmode, 0);
4606259563Spfg
4607259563Spfg  target = expand_unop (mode, bswap_optab, op0, target, 1);
4608259563Spfg
4609259563Spfg  gcc_assert (target);
4610259563Spfg
4611259563Spfg  return convert_to_mode (mode, target, 0);
4612259563Spfg}
4613259563Spfg
4614132727Skan/* Expand a call to a unary builtin.  The arguments are in ARGLIST.
461590075Sobrien   Return 0 if a normal call should be emitted rather than expanding the
461690075Sobrien   function in-line.  If convenient, the result should be placed in TARGET.
461790075Sobrien   SUBTARGET may be used as the target for computing one of EXP's operands.  */
461890075Sobrien
461990075Sobrienstatic rtx
4620132727Skanexpand_builtin_unop (enum machine_mode target_mode, tree arglist, rtx target,
4621132727Skan		     rtx subtarget, optab op_optab)
462290075Sobrien{
462390075Sobrien  rtx op0;
462490075Sobrien  if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
462590075Sobrien    return 0;
462690075Sobrien
462790075Sobrien  /* Compute the argument.  */
462890075Sobrien  op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
4629132727Skan  /* Compute op, into TARGET if possible.
463090075Sobrien     Set TARGET to wherever the result comes back.  */
463190075Sobrien  target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
4632132727Skan			op_optab, op0, target, 1);
4633169699Skan  gcc_assert (target);
4634132727Skan
4635132727Skan  return convert_to_mode (target_mode, target, 0);
463690075Sobrien}
463790075Sobrien
463890075Sobrien/* If the string passed to fputs is a constant and is one character
463990075Sobrien   long, we attempt to transform this call into __builtin_fputc().  */
464090075Sobrien
464190075Sobrienstatic rtx
4642132727Skanexpand_builtin_fputs (tree arglist, rtx target, bool unlocked)
464390075Sobrien{
464490075Sobrien  /* Verify the arguments in the original call.  */
4645169699Skan  if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
464690075Sobrien    {
4647169699Skan      tree result = fold_builtin_fputs (arglist, (target == const0_rtx),
4648169699Skan					unlocked, NULL_TREE);
4649169699Skan      if (result)
4650169699Skan	return expand_expr (result, target, VOIDmode, EXPAND_NORMAL);
465190075Sobrien    }
4652169699Skan  return 0;
465390075Sobrien}
465490075Sobrien
465590075Sobrien/* Expand a call to __builtin_expect.  We return our argument and emit a
465690075Sobrien   NOTE_INSN_EXPECTED_VALUE note.  This is the expansion of __builtin_expect in
465790075Sobrien   a non-jump context.  */
465890075Sobrien
465990075Sobrienstatic rtx
4660132727Skanexpand_builtin_expect (tree arglist, rtx target)
466190075Sobrien{
466290075Sobrien  tree exp, c;
466390075Sobrien  rtx note, rtx_c;
466490075Sobrien
466590075Sobrien  if (arglist == NULL_TREE
466690075Sobrien      || TREE_CHAIN (arglist) == NULL_TREE)
466790075Sobrien    return const0_rtx;
466890075Sobrien  exp = TREE_VALUE (arglist);
466990075Sobrien  c = TREE_VALUE (TREE_CHAIN (arglist));
467090075Sobrien
467190075Sobrien  if (TREE_CODE (c) != INTEGER_CST)
467290075Sobrien    {
4673169699Skan      error ("second argument to %<__builtin_expect%> must be a constant");
467490075Sobrien      c = integer_zero_node;
467590075Sobrien    }
467690075Sobrien
467790075Sobrien  target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL);
467890075Sobrien
467990075Sobrien  /* Don't bother with expected value notes for integral constants.  */
4680117404Skan  if (flag_guess_branch_prob && GET_CODE (target) != CONST_INT)
468190075Sobrien    {
468290075Sobrien      /* We do need to force this into a register so that we can be
468390075Sobrien	 moderately sure to be able to correctly interpret the branch
468490075Sobrien	 condition later.  */
468590075Sobrien      target = force_reg (GET_MODE (target), target);
468690075Sobrien
468790075Sobrien      rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL);
468890075Sobrien
4689132727Skan      note = emit_note (NOTE_INSN_EXPECTED_VALUE);
469090075Sobrien      NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c);
469190075Sobrien    }
469290075Sobrien
469390075Sobrien  return target;
469490075Sobrien}
469590075Sobrien
469690075Sobrien/* Like expand_builtin_expect, except do this in a jump context.  This is
469790075Sobrien   called from do_jump if the conditional is a __builtin_expect.  Return either
4698117404Skan   a list of insns to emit the jump or NULL if we cannot optimize
469990075Sobrien   __builtin_expect.  We need to optimize this at jump time so that machines
470090075Sobrien   like the PowerPC don't turn the test into a SCC operation, and then jump
470190075Sobrien   based on the test being 0/1.  */
470290075Sobrien
470390075Sobrienrtx
4704132727Skanexpand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label)
470590075Sobrien{
470690075Sobrien  tree arglist = TREE_OPERAND (exp, 1);
470790075Sobrien  tree arg0 = TREE_VALUE (arglist);
470890075Sobrien  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
470990075Sobrien  rtx ret = NULL_RTX;
471090075Sobrien
471190075Sobrien  /* Only handle __builtin_expect (test, 0) and
471290075Sobrien     __builtin_expect (test, 1).  */
471390075Sobrien  if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE
471490075Sobrien      && (integer_zerop (arg1) || integer_onep (arg1)))
471590075Sobrien    {
4716132727Skan      rtx insn, drop_through_label, temp;
471790075Sobrien
471890075Sobrien      /* Expand the jump insns.  */
471990075Sobrien      start_sequence ();
472090075Sobrien      do_jump (arg0, if_false_label, if_true_label);
4721117404Skan      ret = get_insns ();
4722132727Skan
4723132727Skan      drop_through_label = get_last_insn ();
4724169699Skan      if (drop_through_label && NOTE_P (drop_through_label))
4725132727Skan	drop_through_label = prev_nonnote_insn (drop_through_label);
4726169699Skan      if (drop_through_label && !LABEL_P (drop_through_label))
4727132727Skan	drop_through_label = NULL_RTX;
472890075Sobrien      end_sequence ();
472990075Sobrien
4730132727Skan      if (! if_true_label)
4731132727Skan	if_true_label = drop_through_label;
4732132727Skan      if (! if_false_label)
4733132727Skan	if_false_label = drop_through_label;
4734132727Skan
4735132727Skan      /* Go through and add the expect's to each of the conditional jumps.  */
4736117404Skan      insn = ret;
4737117404Skan      while (insn != NULL_RTX)
473890075Sobrien	{
4739117404Skan	  rtx next = NEXT_INSN (insn);
474090075Sobrien
4741169699Skan	  if (JUMP_P (insn) && any_condjump_p (insn))
474290075Sobrien	    {
4743132727Skan	      rtx ifelse = SET_SRC (pc_set (insn));
4744132727Skan	      rtx then_dest = XEXP (ifelse, 1);
4745132727Skan	      rtx else_dest = XEXP (ifelse, 2);
4746132727Skan	      int taken = -1;
474790075Sobrien
4748132727Skan	      /* First check if we recognize any of the labels.  */
4749132727Skan	      if (GET_CODE (then_dest) == LABEL_REF
4750132727Skan		  && XEXP (then_dest, 0) == if_true_label)
4751132727Skan		taken = 1;
4752132727Skan	      else if (GET_CODE (then_dest) == LABEL_REF
4753132727Skan		       && XEXP (then_dest, 0) == if_false_label)
4754132727Skan		taken = 0;
4755132727Skan	      else if (GET_CODE (else_dest) == LABEL_REF
4756132727Skan		       && XEXP (else_dest, 0) == if_false_label)
4757132727Skan		taken = 1;
4758132727Skan	      else if (GET_CODE (else_dest) == LABEL_REF
4759132727Skan		       && XEXP (else_dest, 0) == if_true_label)
4760132727Skan		taken = 0;
4761132727Skan	      /* Otherwise check where we drop through.  */
4762132727Skan	      else if (else_dest == pc_rtx)
4763132727Skan		{
4764169699Skan		  if (next && NOTE_P (next))
4765132727Skan		    next = next_nonnote_insn (next);
476690075Sobrien
4767169699Skan		  if (next && JUMP_P (next)
4768132727Skan		      && any_uncondjump_p (next))
4769132727Skan		    temp = XEXP (SET_SRC (pc_set (next)), 0);
4770132727Skan		  else
4771132727Skan		    temp = next;
4772132727Skan
4773132727Skan		  /* TEMP is either a CODE_LABEL, NULL_RTX or something
4774132727Skan		     else that can't possibly match either target label.  */
4775132727Skan		  if (temp == if_false_label)
4776132727Skan		    taken = 1;
4777132727Skan		  else if (temp == if_true_label)
4778132727Skan		    taken = 0;
477990075Sobrien		}
4780132727Skan	      else if (then_dest == pc_rtx)
478190075Sobrien		{
4782169699Skan		  if (next && NOTE_P (next))
4783132727Skan		    next = next_nonnote_insn (next);
4784132727Skan
4785169699Skan		  if (next && JUMP_P (next)
4786132727Skan		      && any_uncondjump_p (next))
4787132727Skan		    temp = XEXP (SET_SRC (pc_set (next)), 0);
4788132727Skan		  else
4789132727Skan		    temp = next;
4790132727Skan
4791132727Skan		  if (temp == if_false_label)
4792132727Skan		    taken = 0;
4793132727Skan		  else if (temp == if_true_label)
4794132727Skan		    taken = 1;
479590075Sobrien		}
4796132727Skan
4797132727Skan	      if (taken != -1)
479890075Sobrien		{
4799132727Skan		  /* If the test is expected to fail, reverse the
4800132727Skan		     probabilities.  */
4801132727Skan		  if (integer_zerop (arg1))
4802132727Skan		    taken = 1 - taken;
4803169699Skan		  predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken);
480490075Sobrien		}
480590075Sobrien	    }
4806117404Skan
4807117404Skan	  insn = next;
480890075Sobrien	}
480990075Sobrien    }
481090075Sobrien
481190075Sobrien  return ret;
481290075Sobrien}
481396263Sobrien
481496263Sobrienvoid
4815132727Skanexpand_builtin_trap (void)
481696263Sobrien{
481796263Sobrien#ifdef HAVE_trap
481896263Sobrien  if (HAVE_trap)
481996263Sobrien    emit_insn (gen_trap ());
482096263Sobrien  else
482196263Sobrien#endif
482296263Sobrien    emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0);
482396263Sobrien  emit_barrier ();
482496263Sobrien}
4825132727Skan
4826132727Skan/* Expand a call to fabs, fabsf or fabsl with arguments ARGLIST.
4827132727Skan   Return 0 if a normal call should be emitted rather than expanding
4828132727Skan   the function inline.  If convenient, the result should be placed
4829132727Skan   in TARGET.  SUBTARGET may be used as the target for computing
4830132727Skan   the operand.  */
4831132727Skan
4832132727Skanstatic rtx
4833132727Skanexpand_builtin_fabs (tree arglist, rtx target, rtx subtarget)
4834132727Skan{
4835132727Skan  enum machine_mode mode;
4836132727Skan  tree arg;
4837132727Skan  rtx op0;
4838132727Skan
4839132727Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
4840132727Skan    return 0;
4841132727Skan
4842132727Skan  arg = TREE_VALUE (arglist);
4843132727Skan  mode = TYPE_MODE (TREE_TYPE (arg));
4844132727Skan  op0 = expand_expr (arg, subtarget, VOIDmode, 0);
4845132727Skan  return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
4846132727Skan}
4847132727Skan
4848169699Skan/* Expand a call to copysign, copysignf, or copysignl with arguments ARGLIST.
4849169699Skan   Return NULL is a normal call should be emitted rather than expanding the
4850169699Skan   function inline.  If convenient, the result should be placed in TARGET.
4851169699Skan   SUBTARGET may be used as the target for computing the operand.  */
4852132727Skan
4853132727Skanstatic rtx
4854169699Skanexpand_builtin_copysign (tree arglist, rtx target, rtx subtarget)
4855132727Skan{
4856169699Skan  rtx op0, op1;
4857132727Skan  tree arg;
4858132727Skan
4859169699Skan  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
4860132727Skan    return 0;
4861169699Skan
4862132727Skan  arg = TREE_VALUE (arglist);
4863169699Skan  op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
4864132727Skan
4865169699Skan  arg = TREE_VALUE (TREE_CHAIN (arglist));
4866169699Skan  op1 = expand_normal (arg);
4867169699Skan
4868169699Skan  return expand_copysign (op0, op1, target);
4869132727Skan}
4870132727Skan
4871132727Skan/* Create a new constant string literal and return a char* pointer to it.
4872132727Skan   The STRING_CST value is the LEN characters at STR.  */
4873169699Skantree
4874132727Skanbuild_string_literal (int len, const char *str)
4875132727Skan{
4876132727Skan  tree t, elem, index, type;
4877132727Skan
4878132727Skan  t = build_string (len, str);
4879132727Skan  elem = build_type_variant (char_type_node, 1, 0);
4880169699Skan  index = build_index_type (build_int_cst (NULL_TREE, len - 1));
4881132727Skan  type = build_array_type (elem, index);
4882132727Skan  TREE_TYPE (t) = type;
4883132727Skan  TREE_CONSTANT (t) = 1;
4884169699Skan  TREE_INVARIANT (t) = 1;
4885132727Skan  TREE_READONLY (t) = 1;
4886132727Skan  TREE_STATIC (t) = 1;
4887132727Skan
4888132727Skan  type = build_pointer_type (type);
4889132727Skan  t = build1 (ADDR_EXPR, type, t);
4890132727Skan
4891132727Skan  type = build_pointer_type (elem);
4892132727Skan  t = build1 (NOP_EXPR, type, t);
4893132727Skan  return t;
4894132727Skan}
4895132727Skan
4896169699Skan/* Expand EXP, a call to printf or printf_unlocked.
4897132727Skan   Return 0 if a normal call should be emitted rather than transforming
4898132727Skan   the function inline.  If convenient, the result should be placed in
4899169699Skan   TARGET with mode MODE.  UNLOCKED indicates this is a printf_unlocked
4900132727Skan   call.  */
4901132727Skanstatic rtx
4902169699Skanexpand_builtin_printf (tree exp, rtx target, enum machine_mode mode,
4903132727Skan		       bool unlocked)
4904132727Skan{
4905169699Skan  tree arglist = TREE_OPERAND (exp, 1);
4906161660Skan  /* If we're using an unlocked function, assume the other unlocked
4907161660Skan     functions exist explicitly.  */
4908161660Skan  tree const fn_putchar = unlocked ? built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
4909161660Skan    : implicit_built_in_decls[BUILT_IN_PUTCHAR];
4910161660Skan  tree const fn_puts = unlocked ? built_in_decls[BUILT_IN_PUTS_UNLOCKED]
4911161660Skan    : implicit_built_in_decls[BUILT_IN_PUTS];
4912132727Skan  const char *fmt_str;
4913132727Skan  tree fn, fmt, arg;
4914132727Skan
4915132727Skan  /* If the return value is used, don't do the transformation.  */
4916132727Skan  if (target != const0_rtx)
4917132727Skan    return 0;
4918132727Skan
4919132727Skan  /* Verify the required arguments in the original call.  */
4920132727Skan  if (! arglist)
4921132727Skan    return 0;
4922132727Skan  fmt = TREE_VALUE (arglist);
4923169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
4924132727Skan    return 0;
4925132727Skan  arglist = TREE_CHAIN (arglist);
4926132727Skan
4927132727Skan  /* Check whether the format is a literal string constant.  */
4928132727Skan  fmt_str = c_getstr (fmt);
4929132727Skan  if (fmt_str == NULL)
4930132727Skan    return 0;
4931132727Skan
4932169699Skan  if (!init_target_chars())
4933169699Skan    return 0;
4934169699Skan
4935132727Skan  /* If the format specifier was "%s\n", call __builtin_puts(arg).  */
4936169699Skan  if (strcmp (fmt_str, target_percent_s_newline) == 0)
4937132727Skan    {
4938132727Skan      if (! arglist
4939169699Skan	  || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
4940132727Skan	  || TREE_CHAIN (arglist))
4941132727Skan	return 0;
4942132727Skan      fn = fn_puts;
4943132727Skan    }
4944132727Skan  /* If the format specifier was "%c", call __builtin_putchar(arg).  */
4945169699Skan  else if (strcmp (fmt_str, target_percent_c) == 0)
4946132727Skan    {
4947132727Skan      if (! arglist
4948132727Skan	  || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
4949132727Skan	  || TREE_CHAIN (arglist))
4950132727Skan	return 0;
4951132727Skan      fn = fn_putchar;
4952132727Skan    }
4953132727Skan  else
4954132727Skan    {
4955132727Skan      /* We can't handle anything else with % args or %% ... yet.  */
4956169699Skan      if (strchr (fmt_str, target_percent))
4957169699Skan	return 0;
4958132727Skan
4959132727Skan      if (arglist)
4960132727Skan	return 0;
4961132727Skan
4962132727Skan      /* If the format specifier was "", printf does nothing.  */
4963132727Skan      if (fmt_str[0] == '\0')
4964132727Skan	return const0_rtx;
4965132727Skan      /* If the format specifier has length of 1, call putchar.  */
4966132727Skan      if (fmt_str[1] == '\0')
4967132727Skan	{
4968132727Skan	  /* Given printf("c"), (where c is any one character,)
4969132727Skan	     convert "c"[0] to an int and pass that to the replacement
4970132727Skan	     function.  */
4971169699Skan	  arg = build_int_cst (NULL_TREE, fmt_str[0]);
4972132727Skan	  arglist = build_tree_list (NULL_TREE, arg);
4973132727Skan	  fn = fn_putchar;
4974132727Skan	}
4975132727Skan      else
4976132727Skan	{
4977132727Skan	  /* If the format specifier was "string\n", call puts("string").  */
4978132727Skan	  size_t len = strlen (fmt_str);
4979169699Skan	  if ((unsigned char)fmt_str[len - 1] == target_newline)
4980132727Skan	    {
4981132727Skan	      /* Create a NUL-terminated string that's one char shorter
4982132727Skan		 than the original, stripping off the trailing '\n'.  */
4983169699Skan	      char *newstr = alloca (len);
4984132727Skan	      memcpy (newstr, fmt_str, len - 1);
4985132727Skan	      newstr[len - 1] = 0;
4986132727Skan
4987132727Skan	      arg = build_string_literal (len, newstr);
4988132727Skan	      arglist = build_tree_list (NULL_TREE, arg);
4989132727Skan	      fn = fn_puts;
4990132727Skan	    }
4991132727Skan	  else
4992132727Skan	    /* We'd like to arrange to call fputs(string,stdout) here,
4993132727Skan	       but we need stdout and don't have a way to get it yet.  */
4994132727Skan	    return 0;
4995132727Skan	}
4996132727Skan    }
4997132727Skan
4998132727Skan  if (!fn)
4999132727Skan    return 0;
5000169699Skan  fn = build_function_call_expr (fn, arglist);
5001169699Skan  if (TREE_CODE (fn) == CALL_EXPR)
5002169699Skan    CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
5003169699Skan  return expand_expr (fn, target, mode, EXPAND_NORMAL);
5004132727Skan}
5005132727Skan
5006169699Skan/* Expand EXP, a call to fprintf or fprintf_unlocked.
5007132727Skan   Return 0 if a normal call should be emitted rather than transforming
5008132727Skan   the function inline.  If convenient, the result should be placed in
5009169699Skan   TARGET with mode MODE.  UNLOCKED indicates this is a fprintf_unlocked
5010132727Skan   call.  */
5011132727Skanstatic rtx
5012169699Skanexpand_builtin_fprintf (tree exp, rtx target, enum machine_mode mode,
5013169699Skan			bool unlocked)
5014132727Skan{
5015169699Skan  tree arglist = TREE_OPERAND (exp, 1);
5016161660Skan  /* If we're using an unlocked function, assume the other unlocked
5017161660Skan     functions exist explicitly.  */
5018161660Skan  tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
5019161660Skan    : implicit_built_in_decls[BUILT_IN_FPUTC];
5020161660Skan  tree const fn_fputs = unlocked ? built_in_decls[BUILT_IN_FPUTS_UNLOCKED]
5021161660Skan    : implicit_built_in_decls[BUILT_IN_FPUTS];
5022132727Skan  const char *fmt_str;
5023132727Skan  tree fn, fmt, fp, arg;
5024132727Skan
5025132727Skan  /* If the return value is used, don't do the transformation.  */
5026132727Skan  if (target != const0_rtx)
5027132727Skan    return 0;
5028132727Skan
5029132727Skan  /* Verify the required arguments in the original call.  */
5030132727Skan  if (! arglist)
5031132727Skan    return 0;
5032132727Skan  fp = TREE_VALUE (arglist);
5033169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fp)))
5034132727Skan    return 0;
5035132727Skan  arglist = TREE_CHAIN (arglist);
5036132727Skan  if (! arglist)
5037132727Skan    return 0;
5038132727Skan  fmt = TREE_VALUE (arglist);
5039169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
5040132727Skan    return 0;
5041132727Skan  arglist = TREE_CHAIN (arglist);
5042132727Skan
5043132727Skan  /* Check whether the format is a literal string constant.  */
5044132727Skan  fmt_str = c_getstr (fmt);
5045132727Skan  if (fmt_str == NULL)
5046132727Skan    return 0;
5047132727Skan
5048169699Skan  if (!init_target_chars())
5049169699Skan    return 0;
5050169699Skan
5051132727Skan  /* If the format specifier was "%s", call __builtin_fputs(arg,fp).  */
5052169699Skan  if (strcmp (fmt_str, target_percent_s) == 0)
5053132727Skan    {
5054132727Skan      if (! arglist
5055169699Skan	  || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
5056132727Skan	  || TREE_CHAIN (arglist))
5057132727Skan	return 0;
5058132727Skan      arg = TREE_VALUE (arglist);
5059132727Skan      arglist = build_tree_list (NULL_TREE, fp);
5060132727Skan      arglist = tree_cons (NULL_TREE, arg, arglist);
5061132727Skan      fn = fn_fputs;
5062132727Skan    }
5063132727Skan  /* If the format specifier was "%c", call __builtin_fputc(arg,fp).  */
5064169699Skan  else if (strcmp (fmt_str, target_percent_c) == 0)
5065132727Skan    {
5066132727Skan      if (! arglist
5067132727Skan	  || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
5068132727Skan	  || TREE_CHAIN (arglist))
5069132727Skan	return 0;
5070132727Skan      arg = TREE_VALUE (arglist);
5071132727Skan      arglist = build_tree_list (NULL_TREE, fp);
5072132727Skan      arglist = tree_cons (NULL_TREE, arg, arglist);
5073132727Skan      fn = fn_fputc;
5074132727Skan    }
5075132727Skan  else
5076132727Skan    {
5077132727Skan      /* We can't handle anything else with % args or %% ... yet.  */
5078169699Skan      if (strchr (fmt_str, target_percent))
5079169699Skan	return 0;
5080132727Skan
5081132727Skan      if (arglist)
5082132727Skan	return 0;
5083132727Skan
5084132727Skan      /* If the format specifier was "", fprintf does nothing.  */
5085132727Skan      if (fmt_str[0] == '\0')
5086132727Skan	{
5087132727Skan	  /* Evaluate and ignore FILE* argument for side-effects.  */
5088132727Skan	  expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL);
5089132727Skan	  return const0_rtx;
5090132727Skan	}
5091132727Skan
5092132727Skan      /* When "string" doesn't contain %, replace all cases of
5093132727Skan	 fprintf(stream,string) with fputs(string,stream).  The fputs
5094132727Skan	 builtin will take care of special cases like length == 1.  */
5095132727Skan      arglist = build_tree_list (NULL_TREE, fp);
5096132727Skan      arglist = tree_cons (NULL_TREE, fmt, arglist);
5097132727Skan      fn = fn_fputs;
5098132727Skan    }
5099132727Skan
5100132727Skan  if (!fn)
5101132727Skan    return 0;
5102169699Skan  fn = build_function_call_expr (fn, arglist);
5103169699Skan  if (TREE_CODE (fn) == CALL_EXPR)
5104169699Skan    CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
5105169699Skan  return expand_expr (fn, target, mode, EXPAND_NORMAL);
5106132727Skan}
5107132727Skan
5108132727Skan/* Expand a call to sprintf with argument list ARGLIST.  Return 0 if
5109132727Skan   a normal call should be emitted rather than expanding the function
5110132727Skan   inline.  If convenient, the result should be placed in TARGET with
5111132727Skan   mode MODE.  */
5112132727Skan
5113132727Skanstatic rtx
5114132727Skanexpand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
5115132727Skan{
5116132727Skan  tree orig_arglist, dest, fmt;
5117132727Skan  const char *fmt_str;
5118132727Skan
5119132727Skan  orig_arglist = arglist;
5120132727Skan
5121132727Skan  /* Verify the required arguments in the original call.  */
5122132727Skan  if (! arglist)
5123132727Skan    return 0;
5124132727Skan  dest = TREE_VALUE (arglist);
5125169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (dest)))
5126132727Skan    return 0;
5127132727Skan  arglist = TREE_CHAIN (arglist);
5128132727Skan  if (! arglist)
5129132727Skan    return 0;
5130132727Skan  fmt = TREE_VALUE (arglist);
5131169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
5132132727Skan    return 0;
5133132727Skan  arglist = TREE_CHAIN (arglist);
5134132727Skan
5135132727Skan  /* Check whether the format is a literal string constant.  */
5136132727Skan  fmt_str = c_getstr (fmt);
5137132727Skan  if (fmt_str == NULL)
5138132727Skan    return 0;
5139132727Skan
5140169699Skan  if (!init_target_chars())
5141169699Skan    return 0;
5142169699Skan
5143132727Skan  /* If the format doesn't contain % args or %%, use strcpy.  */
5144169699Skan  if (strchr (fmt_str, target_percent) == 0)
5145132727Skan    {
5146132727Skan      tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
5147132727Skan      tree exp;
5148132727Skan
5149132727Skan      if (arglist || ! fn)
5150132727Skan	return 0;
5151132727Skan      expand_expr (build_function_call_expr (fn, orig_arglist),
5152132727Skan		   const0_rtx, VOIDmode, EXPAND_NORMAL);
5153132727Skan      if (target == const0_rtx)
5154132727Skan	return const0_rtx;
5155169699Skan      exp = build_int_cst (NULL_TREE, strlen (fmt_str));
5156132727Skan      return expand_expr (exp, target, mode, EXPAND_NORMAL);
5157132727Skan    }
5158132727Skan  /* If the format is "%s", use strcpy if the result isn't used.  */
5159169699Skan  else if (strcmp (fmt_str, target_percent_s) == 0)
5160132727Skan    {
5161132727Skan      tree fn, arg, len;
5162132727Skan      fn = implicit_built_in_decls[BUILT_IN_STRCPY];
5163132727Skan
5164132727Skan      if (! fn)
5165132727Skan	return 0;
5166132727Skan
5167132727Skan      if (! arglist || TREE_CHAIN (arglist))
5168132727Skan	return 0;
5169132727Skan      arg = TREE_VALUE (arglist);
5170169699Skan      if (! POINTER_TYPE_P (TREE_TYPE (arg)))
5171132727Skan	return 0;
5172132727Skan
5173132727Skan      if (target != const0_rtx)
5174132727Skan	{
5175132727Skan	  len = c_strlen (arg, 1);
5176132727Skan	  if (! len || TREE_CODE (len) != INTEGER_CST)
5177132727Skan	    return 0;
5178132727Skan	}
5179132727Skan      else
5180132727Skan	len = NULL_TREE;
5181132727Skan
5182132727Skan      arglist = build_tree_list (NULL_TREE, arg);
5183132727Skan      arglist = tree_cons (NULL_TREE, dest, arglist);
5184132727Skan      expand_expr (build_function_call_expr (fn, arglist),
5185132727Skan		   const0_rtx, VOIDmode, EXPAND_NORMAL);
5186132727Skan
5187132727Skan      if (target == const0_rtx)
5188132727Skan	return const0_rtx;
5189132727Skan      return expand_expr (len, target, mode, EXPAND_NORMAL);
5190132727Skan    }
5191132727Skan
5192132727Skan  return 0;
5193132727Skan}
5194169699Skan
5195169699Skan/* Expand a call to either the entry or exit function profiler.  */
5196169699Skan
5197169699Skanstatic rtx
5198169699Skanexpand_builtin_profile_func (bool exitp)
5199169699Skan{
5200169699Skan  rtx this, which;
5201169699Skan
5202169699Skan  this = DECL_RTL (current_function_decl);
5203169699Skan  gcc_assert (MEM_P (this));
5204169699Skan  this = XEXP (this, 0);
5205169699Skan
5206169699Skan  if (exitp)
5207169699Skan    which = profile_function_exit_libfunc;
5208169699Skan  else
5209169699Skan    which = profile_function_entry_libfunc;
5210169699Skan
5211169699Skan  emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode,
5212169699Skan		     expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
5213169699Skan						 0),
5214169699Skan		     Pmode);
5215169699Skan
5216169699Skan  return const0_rtx;
5217169699Skan}
5218169699Skan
5219169699Skan/* Given a trampoline address, make sure it satisfies TRAMPOLINE_ALIGNMENT.  */
5220169699Skan
5221169699Skanstatic rtx
5222169699Skanround_trampoline_addr (rtx tramp)
5223169699Skan{
5224169699Skan  rtx temp, addend, mask;
5225169699Skan
5226169699Skan  /* If we don't need too much alignment, we'll have been guaranteed
5227169699Skan     proper alignment by get_trampoline_type.  */
5228169699Skan  if (TRAMPOLINE_ALIGNMENT <= STACK_BOUNDARY)
5229169699Skan    return tramp;
5230169699Skan
5231169699Skan  /* Round address up to desired boundary.  */
5232169699Skan  temp = gen_reg_rtx (Pmode);
5233169699Skan  addend = GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1);
5234169699Skan  mask = GEN_INT (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT);
5235169699Skan
5236169699Skan  temp  = expand_simple_binop (Pmode, PLUS, tramp, addend,
5237169699Skan			       temp, 0, OPTAB_LIB_WIDEN);
5238169699Skan  tramp = expand_simple_binop (Pmode, AND, temp, mask,
5239169699Skan			       temp, 0, OPTAB_LIB_WIDEN);
5240169699Skan
5241169699Skan  return tramp;
5242169699Skan}
5243169699Skan
5244169699Skanstatic rtx
5245169699Skanexpand_builtin_init_trampoline (tree arglist)
5246169699Skan{
5247169699Skan  tree t_tramp, t_func, t_chain;
5248169699Skan  rtx r_tramp, r_func, r_chain;
5249169699Skan#ifdef TRAMPOLINE_TEMPLATE
5250169699Skan  rtx blktramp;
5251169699Skan#endif
5252169699Skan
5253169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE,
5254169699Skan			 POINTER_TYPE, VOID_TYPE))
5255169699Skan    return NULL_RTX;
5256169699Skan
5257169699Skan  t_tramp = TREE_VALUE (arglist);
5258169699Skan  arglist = TREE_CHAIN (arglist);
5259169699Skan  t_func = TREE_VALUE (arglist);
5260169699Skan  arglist = TREE_CHAIN (arglist);
5261169699Skan  t_chain = TREE_VALUE (arglist);
5262169699Skan
5263169699Skan  r_tramp = expand_normal (t_tramp);
5264169699Skan  r_func = expand_normal (t_func);
5265169699Skan  r_chain = expand_normal (t_chain);
5266169699Skan
5267169699Skan  /* Generate insns to initialize the trampoline.  */
5268169699Skan  r_tramp = round_trampoline_addr (r_tramp);
5269169699Skan#ifdef TRAMPOLINE_TEMPLATE
5270169699Skan  blktramp = gen_rtx_MEM (BLKmode, r_tramp);
5271169699Skan  set_mem_align (blktramp, TRAMPOLINE_ALIGNMENT);
5272169699Skan  emit_block_move (blktramp, assemble_trampoline_template (),
5273169699Skan		   GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
5274169699Skan#endif
5275169699Skan  trampolines_created = 1;
5276169699Skan  INITIALIZE_TRAMPOLINE (r_tramp, r_func, r_chain);
5277169699Skan
5278169699Skan  return const0_rtx;
5279169699Skan}
5280169699Skan
5281169699Skanstatic rtx
5282169699Skanexpand_builtin_adjust_trampoline (tree arglist)
5283169699Skan{
5284169699Skan  rtx tramp;
5285169699Skan
5286169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
5287169699Skan    return NULL_RTX;
5288169699Skan
5289169699Skan  tramp = expand_normal (TREE_VALUE (arglist));
5290169699Skan  tramp = round_trampoline_addr (tramp);
5291169699Skan#ifdef TRAMPOLINE_ADJUST_ADDRESS
5292169699Skan  TRAMPOLINE_ADJUST_ADDRESS (tramp);
5293169699Skan#endif
5294169699Skan
5295169699Skan  return tramp;
5296169699Skan}
5297169699Skan
5298169699Skan/* Expand a call to the built-in signbit, signbitf or signbitl function.
5299169699Skan   Return NULL_RTX if a normal call should be emitted rather than expanding
5300169699Skan   the function in-line.  EXP is the expression that is a call to the builtin
5301169699Skan   function; if convenient, the result should be placed in TARGET.  */
5302169699Skan
5303169699Skanstatic rtx
5304169699Skanexpand_builtin_signbit (tree exp, rtx target)
5305169699Skan{
5306169699Skan  const struct real_format *fmt;
5307169699Skan  enum machine_mode fmode, imode, rmode;
5308169699Skan  HOST_WIDE_INT hi, lo;
5309169699Skan  tree arg, arglist;
5310169699Skan  int word, bitpos;
5311169699Skan  rtx temp;
5312169699Skan
5313169699Skan  arglist = TREE_OPERAND (exp, 1);
5314169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
5315169699Skan    return 0;
5316169699Skan
5317169699Skan  arg = TREE_VALUE (arglist);
5318169699Skan  fmode = TYPE_MODE (TREE_TYPE (arg));
5319169699Skan  rmode = TYPE_MODE (TREE_TYPE (exp));
5320169699Skan  fmt = REAL_MODE_FORMAT (fmode);
5321169699Skan
5322169699Skan  /* For floating point formats without a sign bit, implement signbit
5323169699Skan     as "ARG < 0.0".  */
5324169699Skan  bitpos = fmt->signbit_ro;
5325169699Skan  if (bitpos < 0)
5326169699Skan  {
5327169699Skan    /* But we can't do this if the format supports signed zero.  */
5328169699Skan    if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode))
5329169699Skan      return 0;
5330169699Skan
5331169699Skan    arg = fold_build2 (LT_EXPR, TREE_TYPE (exp), arg,
5332169699Skan		       build_real (TREE_TYPE (arg), dconst0));
5333169699Skan    return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
5334169699Skan  }
5335169699Skan
5336169699Skan  temp = expand_normal (arg);
5337169699Skan  if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD)
5338169699Skan    {
5339169699Skan      imode = int_mode_for_mode (fmode);
5340169699Skan      if (imode == BLKmode)
5341169699Skan	return 0;
5342169699Skan      temp = gen_lowpart (imode, temp);
5343169699Skan    }
5344169699Skan  else
5345169699Skan    {
5346169699Skan      imode = word_mode;
5347169699Skan      /* Handle targets with different FP word orders.  */
5348169699Skan      if (FLOAT_WORDS_BIG_ENDIAN)
5349169699Skan	word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD;
5350169699Skan      else
5351169699Skan	word = bitpos / BITS_PER_WORD;
5352169699Skan      temp = operand_subword_force (temp, word, fmode);
5353169699Skan      bitpos = bitpos % BITS_PER_WORD;
5354169699Skan    }
5355169699Skan
5356169699Skan  /* Force the intermediate word_mode (or narrower) result into a
5357169699Skan     register.  This avoids attempting to create paradoxical SUBREGs
5358169699Skan     of floating point modes below.  */
5359169699Skan  temp = force_reg (imode, temp);
5360169699Skan
5361169699Skan  /* If the bitpos is within the "result mode" lowpart, the operation
5362169699Skan     can be implement with a single bitwise AND.  Otherwise, we need
5363169699Skan     a right shift and an AND.  */
5364169699Skan
5365169699Skan  if (bitpos < GET_MODE_BITSIZE (rmode))
5366169699Skan    {
5367169699Skan      if (bitpos < HOST_BITS_PER_WIDE_INT)
5368169699Skan	{
5369169699Skan	  hi = 0;
5370169699Skan	  lo = (HOST_WIDE_INT) 1 << bitpos;
5371169699Skan	}
5372169699Skan      else
5373169699Skan	{
5374169699Skan	  hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
5375169699Skan	  lo = 0;
5376169699Skan	}
5377169699Skan
5378169699Skan      if (imode != rmode)
5379169699Skan	temp = gen_lowpart (rmode, temp);
5380169699Skan      temp = expand_binop (rmode, and_optab, temp,
5381169699Skan			   immed_double_const (lo, hi, rmode),
5382169699Skan			   NULL_RTX, 1, OPTAB_LIB_WIDEN);
5383169699Skan    }
5384169699Skan  else
5385169699Skan    {
5386169699Skan      /* Perform a logical right shift to place the signbit in the least
5387169699Skan	 significant bit, then truncate the result to the desired mode
5388169699Skan	 and mask just this bit.  */
5389169699Skan      temp = expand_shift (RSHIFT_EXPR, imode, temp,
5390169699Skan			   build_int_cst (NULL_TREE, bitpos), NULL_RTX, 1);
5391169699Skan      temp = gen_lowpart (rmode, temp);
5392169699Skan      temp = expand_binop (rmode, and_optab, temp, const1_rtx,
5393169699Skan			   NULL_RTX, 1, OPTAB_LIB_WIDEN);
5394169699Skan    }
5395169699Skan
5396169699Skan  return temp;
5397169699Skan}
5398169699Skan
5399169699Skan/* Expand fork or exec calls.  TARGET is the desired target of the
5400169699Skan   call.  ARGLIST is the list of arguments of the call.  FN is the
5401169699Skan   identificator of the actual function.  IGNORE is nonzero if the
5402169699Skan   value is to be ignored.  */
5403169699Skan
5404169699Skanstatic rtx
5405169699Skanexpand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore)
5406169699Skan{
5407169699Skan  tree id, decl;
5408169699Skan  tree call;
5409169699Skan
5410169699Skan  /* If we are not profiling, just call the function.  */
5411169699Skan  if (!profile_arc_flag)
5412169699Skan    return NULL_RTX;
5413169699Skan
5414169699Skan  /* Otherwise call the wrapper.  This should be equivalent for the rest of
5415169699Skan     compiler, so the code does not diverge, and the wrapper may run the
5416169699Skan     code necessary for keeping the profiling sane.  */
5417169699Skan
5418169699Skan  switch (DECL_FUNCTION_CODE (fn))
5419169699Skan    {
5420169699Skan    case BUILT_IN_FORK:
5421169699Skan      id = get_identifier ("__gcov_fork");
5422169699Skan      break;
5423169699Skan
5424169699Skan    case BUILT_IN_EXECL:
5425169699Skan      id = get_identifier ("__gcov_execl");
5426169699Skan      break;
5427169699Skan
5428169699Skan    case BUILT_IN_EXECV:
5429169699Skan      id = get_identifier ("__gcov_execv");
5430169699Skan      break;
5431169699Skan
5432169699Skan    case BUILT_IN_EXECLP:
5433169699Skan      id = get_identifier ("__gcov_execlp");
5434169699Skan      break;
5435169699Skan
5436169699Skan    case BUILT_IN_EXECLE:
5437169699Skan      id = get_identifier ("__gcov_execle");
5438169699Skan      break;
5439169699Skan
5440169699Skan    case BUILT_IN_EXECVP:
5441169699Skan      id = get_identifier ("__gcov_execvp");
5442169699Skan      break;
5443169699Skan
5444169699Skan    case BUILT_IN_EXECVE:
5445169699Skan      id = get_identifier ("__gcov_execve");
5446169699Skan      break;
5447169699Skan
5448169699Skan    default:
5449169699Skan      gcc_unreachable ();
5450169699Skan    }
5451169699Skan
5452169699Skan  decl = build_decl (FUNCTION_DECL, id, TREE_TYPE (fn));
5453169699Skan  DECL_EXTERNAL (decl) = 1;
5454169699Skan  TREE_PUBLIC (decl) = 1;
5455169699Skan  DECL_ARTIFICIAL (decl) = 1;
5456169699Skan  TREE_NOTHROW (decl) = 1;
5457169699Skan  DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
5458169699Skan  DECL_VISIBILITY_SPECIFIED (decl) = 1;
5459169699Skan  call = build_function_call_expr (decl, arglist);
5460169699Skan
5461169699Skan  return expand_call (call, target, ignore);
5462169699Skan}
5463169699Skan
546490075Sobrien
5465169699Skan/* Reconstitute a mode for a __sync intrinsic operation.  Since the type of
5466169699Skan   the pointer in these functions is void*, the tree optimizers may remove
5467169699Skan   casts.  The mode computed in expand_builtin isn't reliable either, due
5468169699Skan   to __sync_bool_compare_and_swap.
5469169699Skan
5470169699Skan   FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the
5471169699Skan   group of builtins.  This gives us log2 of the mode size.  */
5472169699Skan
5473169699Skanstatic inline enum machine_mode
5474169699Skanget_builtin_sync_mode (int fcode_diff)
5475169699Skan{
5476169699Skan  /* The size is not negotiable, so ask not to get BLKmode in return
5477169699Skan     if the target indicates that a smaller size would be better.  */
5478169699Skan  return mode_for_size (BITS_PER_UNIT << fcode_diff, MODE_INT, 0);
5479169699Skan}
5480169699Skan
5481169699Skan/* Expand the memory expression LOC and return the appropriate memory operand
5482169699Skan   for the builtin_sync operations.  */
5483169699Skan
5484169699Skanstatic rtx
5485169699Skanget_builtin_sync_mem (tree loc, enum machine_mode mode)
5486169699Skan{
5487169699Skan  rtx addr, mem;
5488169699Skan
5489169699Skan  addr = expand_expr (loc, NULL, Pmode, EXPAND_SUM);
5490169699Skan
5491169699Skan  /* Note that we explicitly do not want any alias information for this
5492169699Skan     memory, so that we kill all other live memories.  Otherwise we don't
5493169699Skan     satisfy the full barrier semantics of the intrinsic.  */
5494169699Skan  mem = validize_mem (gen_rtx_MEM (mode, addr));
5495169699Skan
5496169699Skan  set_mem_align (mem, get_pointer_alignment (loc, BIGGEST_ALIGNMENT));
5497169699Skan  set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER);
5498169699Skan  MEM_VOLATILE_P (mem) = 1;
5499169699Skan
5500169699Skan  return mem;
5501169699Skan}
5502169699Skan
5503169699Skan/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
5504169699Skan   ARGLIST is the operands list to the function.  CODE is the rtx code
5505169699Skan   that corresponds to the arithmetic or logical operation from the name;
5506169699Skan   an exception here is that NOT actually means NAND.  TARGET is an optional
5507169699Skan   place for us to store the results; AFTER is true if this is the
5508169699Skan   fetch_and_xxx form.  IGNORE is true if we don't actually care about
5509169699Skan   the result of the operation at all.  */
5510169699Skan
5511169699Skanstatic rtx
5512169699Skanexpand_builtin_sync_operation (enum machine_mode mode, tree arglist,
5513169699Skan			       enum rtx_code code, bool after,
5514169699Skan			       rtx target, bool ignore)
5515169699Skan{
5516169699Skan  rtx val, mem;
5517169699Skan  enum machine_mode old_mode;
5518169699Skan
5519169699Skan  /* Expand the operands.  */
5520169699Skan  mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
5521169699Skan
5522169699Skan  arglist = TREE_CHAIN (arglist);
5523169699Skan  val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
5524169699Skan  /* If VAL is promoted to a wider mode, convert it back to MODE.  Take care
5525169699Skan     of CONST_INTs, where we know the old_mode only from the call argument.  */
5526169699Skan  old_mode = GET_MODE (val);
5527169699Skan  if (old_mode == VOIDmode)
5528169699Skan    old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist)));
5529169699Skan  val = convert_modes (mode, old_mode, val, 1);
5530169699Skan
5531169699Skan  if (ignore)
5532169699Skan    return expand_sync_operation (mem, val, code);
5533169699Skan  else
5534169699Skan    return expand_sync_fetch_operation (mem, val, code, after, target);
5535169699Skan}
5536169699Skan
5537169699Skan/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
5538169699Skan   intrinsics.  ARGLIST is the operands list to the function.  IS_BOOL is
5539169699Skan   true if this is the boolean form.  TARGET is a place for us to store the
5540169699Skan   results; this is NOT optional if IS_BOOL is true.  */
5541169699Skan
5542169699Skanstatic rtx
5543169699Skanexpand_builtin_compare_and_swap (enum machine_mode mode, tree arglist,
5544169699Skan				 bool is_bool, rtx target)
5545169699Skan{
5546169699Skan  rtx old_val, new_val, mem;
5547169699Skan  enum machine_mode old_mode;
5548169699Skan
5549169699Skan  /* Expand the operands.  */
5550169699Skan  mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
5551169699Skan
5552169699Skan  arglist = TREE_CHAIN (arglist);
5553169699Skan  old_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
5554169699Skan  /* If VAL is promoted to a wider mode, convert it back to MODE.  Take care
5555169699Skan     of CONST_INTs, where we know the old_mode only from the call argument.  */
5556169699Skan  old_mode = GET_MODE (old_val);
5557169699Skan  if (old_mode == VOIDmode)
5558169699Skan    old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist)));
5559169699Skan  old_val = convert_modes (mode, old_mode, old_val, 1);
5560169699Skan
5561169699Skan  arglist = TREE_CHAIN (arglist);
5562169699Skan  new_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
5563169699Skan  /* If VAL is promoted to a wider mode, convert it back to MODE.  Take care
5564169699Skan     of CONST_INTs, where we know the old_mode only from the call argument.  */
5565169699Skan  old_mode = GET_MODE (new_val);
5566169699Skan  if (old_mode == VOIDmode)
5567169699Skan    old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist)));
5568169699Skan  new_val = convert_modes (mode, old_mode, new_val, 1);
5569169699Skan
5570169699Skan  if (is_bool)
5571169699Skan    return expand_bool_compare_and_swap (mem, old_val, new_val, target);
5572169699Skan  else
5573169699Skan    return expand_val_compare_and_swap (mem, old_val, new_val, target);
5574169699Skan}
5575169699Skan
5576169699Skan/* Expand the __sync_lock_test_and_set intrinsic.  Note that the most
5577169699Skan   general form is actually an atomic exchange, and some targets only
5578169699Skan   support a reduced form with the second argument being a constant 1.
5579169699Skan   ARGLIST is the operands list to the function; TARGET is an optional
5580169699Skan   place for us to store the results.  */
5581169699Skan
5582169699Skanstatic rtx
5583169699Skanexpand_builtin_lock_test_and_set (enum machine_mode mode, tree arglist,
5584169699Skan				  rtx target)
5585169699Skan{
5586169699Skan  rtx val, mem;
5587169699Skan  enum machine_mode old_mode;
5588169699Skan
5589169699Skan  /* Expand the operands.  */
5590169699Skan  mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
5591169699Skan
5592169699Skan  arglist = TREE_CHAIN (arglist);
5593169699Skan  val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
5594169699Skan  /* If VAL is promoted to a wider mode, convert it back to MODE.  Take care
5595169699Skan     of CONST_INTs, where we know the old_mode only from the call argument.  */
5596169699Skan  old_mode = GET_MODE (val);
5597169699Skan  if (old_mode == VOIDmode)
5598169699Skan    old_mode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist)));
5599169699Skan  val = convert_modes (mode, old_mode, val, 1);
5600169699Skan
5601169699Skan  return expand_sync_lock_test_and_set (mem, val, target);
5602169699Skan}
5603169699Skan
5604169699Skan/* Expand the __sync_synchronize intrinsic.  */
5605169699Skan
5606169699Skanstatic void
5607169699Skanexpand_builtin_synchronize (void)
5608169699Skan{
5609169699Skan  tree x;
5610169699Skan
5611169699Skan#ifdef HAVE_memory_barrier
5612169699Skan  if (HAVE_memory_barrier)
5613169699Skan    {
5614169699Skan      emit_insn (gen_memory_barrier ());
5615169699Skan      return;
5616169699Skan    }
5617169699Skan#endif
5618169699Skan
5619169699Skan  /* If no explicit memory barrier instruction is available, create an
5620169699Skan     empty asm stmt with a memory clobber.  */
5621169699Skan  x = build4 (ASM_EXPR, void_type_node, build_string (0, ""), NULL, NULL,
5622169699Skan	      tree_cons (NULL, build_string (6, "memory"), NULL));
5623169699Skan  ASM_VOLATILE_P (x) = 1;
5624169699Skan  expand_asm_expr (x);
5625169699Skan}
5626169699Skan
5627169699Skan/* Expand the __sync_lock_release intrinsic.  ARGLIST is the operands list
5628169699Skan   to the function.  */
5629169699Skan
5630169699Skanstatic void
5631169699Skanexpand_builtin_lock_release (enum machine_mode mode, tree arglist)
5632169699Skan{
5633169699Skan  enum insn_code icode;
5634169699Skan  rtx mem, insn;
5635169699Skan  rtx val = const0_rtx;
5636169699Skan
5637169699Skan  /* Expand the operands.  */
5638169699Skan  mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
5639169699Skan
5640169699Skan  /* If there is an explicit operation in the md file, use it.  */
5641169699Skan  icode = sync_lock_release[mode];
5642169699Skan  if (icode != CODE_FOR_nothing)
5643169699Skan    {
5644169699Skan      if (!insn_data[icode].operand[1].predicate (val, mode))
5645169699Skan	val = force_reg (mode, val);
5646169699Skan
5647169699Skan      insn = GEN_FCN (icode) (mem, val);
5648169699Skan      if (insn)
5649169699Skan	{
5650169699Skan	  emit_insn (insn);
5651169699Skan	  return;
5652169699Skan	}
5653169699Skan    }
5654169699Skan
5655169699Skan  /* Otherwise we can implement this operation by emitting a barrier
5656169699Skan     followed by a store of zero.  */
5657169699Skan  expand_builtin_synchronize ();
5658169699Skan  emit_move_insn (mem, val);
5659169699Skan}
5660169699Skan
566190075Sobrien/* Expand an expression EXP that calls a built-in function,
566290075Sobrien   with result going to TARGET if that's convenient
566390075Sobrien   (and in mode MODE if that's convenient).
566490075Sobrien   SUBTARGET may be used as the target for computing one of EXP's operands.
566590075Sobrien   IGNORE is nonzero if the value is to be ignored.  */
566690075Sobrien
566790075Sobrienrtx
5668132727Skanexpand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
5669132727Skan		int ignore)
567090075Sobrien{
5671132727Skan  tree fndecl = get_callee_fndecl (exp);
567290075Sobrien  tree arglist = TREE_OPERAND (exp, 1);
567390075Sobrien  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
5674132727Skan  enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
567590075Sobrien
567690075Sobrien  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
5677169699Skan    return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
567890075Sobrien
567990075Sobrien  /* When not optimizing, generate calls to library functions for a certain
568090075Sobrien     set of builtins.  */
5681132727Skan  if (!optimize
5682169699Skan      && !called_as_built_in (fndecl)
5683132727Skan      && DECL_ASSEMBLER_NAME_SET_P (fndecl)
5684132727Skan      && fcode != BUILT_IN_ALLOCA)
5685132727Skan    return expand_call (exp, target, ignore);
568690075Sobrien
5687132727Skan  /* The built-in function expanders test for target == const0_rtx
5688132727Skan     to determine whether the function's result will be ignored.  */
5689132727Skan  if (ignore)
5690132727Skan    target = const0_rtx;
569190075Sobrien
5692132727Skan  /* If the result of a pure or const built-in function is ignored, and
5693132727Skan     none of its arguments are volatile, we can avoid expanding the
5694132727Skan     built-in call and just evaluate the arguments for side-effects.  */
5695132727Skan  if (target == const0_rtx
5696132727Skan      && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl)))
5697132727Skan    {
5698132727Skan      bool volatilep = false;
5699132727Skan      tree arg;
5700132727Skan
5701132727Skan      for (arg = arglist; arg; arg = TREE_CHAIN (arg))
5702132727Skan	if (TREE_THIS_VOLATILE (TREE_VALUE (arg)))
5703132727Skan	  {
5704132727Skan	    volatilep = true;
5705132727Skan	    break;
5706132727Skan	  }
5707132727Skan
5708132727Skan      if (! volatilep)
5709132727Skan	{
5710132727Skan	  for (arg = arglist; arg; arg = TREE_CHAIN (arg))
5711132727Skan	    expand_expr (TREE_VALUE (arg), const0_rtx,
5712132727Skan			 VOIDmode, EXPAND_NORMAL);
5713132727Skan	  return const0_rtx;
5714132727Skan	}
5715132727Skan    }
5716132727Skan
571790075Sobrien  switch (fcode)
571890075Sobrien    {
5719169699Skan    CASE_FLT_FN (BUILT_IN_FABS):
5720132727Skan      target = expand_builtin_fabs (arglist, target, subtarget);
5721132727Skan      if (target)
5722169699Skan	return target;
5723132727Skan      break;
572490075Sobrien
5725169699Skan    CASE_FLT_FN (BUILT_IN_COPYSIGN):
5726169699Skan      target = expand_builtin_copysign (arglist, target, subtarget);
5727169699Skan      if (target)
5728169699Skan	return target;
5729132727Skan      break;
5730132727Skan
5731169699Skan      /* Just do a normal library call if we were unable to fold
5732169699Skan	 the values.  */
5733169699Skan    CASE_FLT_FN (BUILT_IN_CABS):
5734169699Skan      break;
573590075Sobrien
5736169699Skan    CASE_FLT_FN (BUILT_IN_EXP):
5737169699Skan    CASE_FLT_FN (BUILT_IN_EXP10):
5738169699Skan    CASE_FLT_FN (BUILT_IN_POW10):
5739169699Skan    CASE_FLT_FN (BUILT_IN_EXP2):
5740169699Skan    CASE_FLT_FN (BUILT_IN_EXPM1):
5741169699Skan    CASE_FLT_FN (BUILT_IN_LOGB):
5742169699Skan    CASE_FLT_FN (BUILT_IN_ILOGB):
5743169699Skan    CASE_FLT_FN (BUILT_IN_LOG):
5744169699Skan    CASE_FLT_FN (BUILT_IN_LOG10):
5745169699Skan    CASE_FLT_FN (BUILT_IN_LOG2):
5746169699Skan    CASE_FLT_FN (BUILT_IN_LOG1P):
5747169699Skan    CASE_FLT_FN (BUILT_IN_TAN):
5748169699Skan    CASE_FLT_FN (BUILT_IN_ASIN):
5749169699Skan    CASE_FLT_FN (BUILT_IN_ACOS):
5750169699Skan    CASE_FLT_FN (BUILT_IN_ATAN):
575190075Sobrien      /* Treat these like sqrt only if unsafe math optimizations are allowed,
575290075Sobrien	 because of possible accuracy problems.  */
575390075Sobrien      if (! flag_unsafe_math_optimizations)
575490075Sobrien	break;
5755169699Skan    CASE_FLT_FN (BUILT_IN_SQRT):
5756169699Skan    CASE_FLT_FN (BUILT_IN_FLOOR):
5757169699Skan    CASE_FLT_FN (BUILT_IN_CEIL):
5758169699Skan    CASE_FLT_FN (BUILT_IN_TRUNC):
5759169699Skan    CASE_FLT_FN (BUILT_IN_ROUND):
5760169699Skan    CASE_FLT_FN (BUILT_IN_NEARBYINT):
5761169699Skan    CASE_FLT_FN (BUILT_IN_RINT):
5762169699Skan    CASE_FLT_FN (BUILT_IN_LRINT):
5763169699Skan    CASE_FLT_FN (BUILT_IN_LLRINT):
576490075Sobrien      target = expand_builtin_mathfn (exp, target, subtarget);
576590075Sobrien      if (target)
576690075Sobrien	return target;
576790075Sobrien      break;
576890075Sobrien
5769169699Skan    CASE_FLT_FN (BUILT_IN_LCEIL):
5770169699Skan    CASE_FLT_FN (BUILT_IN_LLCEIL):
5771169699Skan    CASE_FLT_FN (BUILT_IN_LFLOOR):
5772169699Skan    CASE_FLT_FN (BUILT_IN_LLFLOOR):
5773169699Skan      target = expand_builtin_int_roundingfn (exp, target, subtarget);
5774169699Skan      if (target)
5775169699Skan	return target;
5776169699Skan      break;
5777169699Skan
5778169699Skan    CASE_FLT_FN (BUILT_IN_POW):
5779132727Skan      target = expand_builtin_pow (exp, target, subtarget);
5780132727Skan      if (target)
5781132727Skan	return target;
5782132727Skan      break;
5783132727Skan
5784169699Skan    CASE_FLT_FN (BUILT_IN_POWI):
5785169699Skan      target = expand_builtin_powi (exp, target, subtarget);
5786169699Skan      if (target)
5787169699Skan	return target;
5788169699Skan      break;
5789169699Skan
5790169699Skan    CASE_FLT_FN (BUILT_IN_ATAN2):
5791169699Skan    CASE_FLT_FN (BUILT_IN_LDEXP):
5792169699Skan    CASE_FLT_FN (BUILT_IN_FMOD):
5793169699Skan    CASE_FLT_FN (BUILT_IN_DREM):
5794132727Skan      if (! flag_unsafe_math_optimizations)
5795132727Skan	break;
5796132727Skan      target = expand_builtin_mathfn_2 (exp, target, subtarget);
5797132727Skan      if (target)
5798132727Skan	return target;
5799132727Skan      break;
5800132727Skan
5801169699Skan    CASE_FLT_FN (BUILT_IN_SIN):
5802169699Skan    CASE_FLT_FN (BUILT_IN_COS):
5803169699Skan      if (! flag_unsafe_math_optimizations)
5804169699Skan	break;
5805169699Skan      target = expand_builtin_mathfn_3 (exp, target, subtarget);
5806169699Skan      if (target)
5807169699Skan	return target;
5808169699Skan      break;
5809169699Skan
5810169699Skan    CASE_FLT_FN (BUILT_IN_SINCOS):
5811169699Skan      if (! flag_unsafe_math_optimizations)
5812169699Skan	break;
5813169699Skan      target = expand_builtin_sincos (exp);
5814169699Skan      if (target)
5815169699Skan	return target;
5816169699Skan      break;
5817169699Skan
581890075Sobrien    case BUILT_IN_APPLY_ARGS:
581990075Sobrien      return expand_builtin_apply_args ();
582090075Sobrien
582190075Sobrien      /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
582290075Sobrien	 FUNCTION with a copy of the parameters described by
582390075Sobrien	 ARGUMENTS, and ARGSIZE.  It returns a block of memory
582490075Sobrien	 allocated on the stack into which is stored all the registers
582590075Sobrien	 that might possibly be used for returning the result of a
582690075Sobrien	 function.  ARGUMENTS is the value returned by
582790075Sobrien	 __builtin_apply_args.  ARGSIZE is the number of bytes of
582890075Sobrien	 arguments that must be copied.  ??? How should this value be
582990075Sobrien	 computed?  We'll also need a safe worst case value for varargs
583090075Sobrien	 functions.  */
583190075Sobrien    case BUILT_IN_APPLY:
583290075Sobrien      if (!validate_arglist (arglist, POINTER_TYPE,
583390075Sobrien			     POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
583490075Sobrien	  && !validate_arglist (arglist, REFERENCE_TYPE,
583590075Sobrien				POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
583690075Sobrien	return const0_rtx;
583790075Sobrien      else
583890075Sobrien	{
583990075Sobrien	  int i;
584090075Sobrien	  tree t;
584190075Sobrien	  rtx ops[3];
584290075Sobrien
584390075Sobrien	  for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++)
5844169699Skan	    ops[i] = expand_normal (TREE_VALUE (t));
584590075Sobrien
584690075Sobrien	  return expand_builtin_apply (ops[0], ops[1], ops[2]);
584790075Sobrien	}
584890075Sobrien
584990075Sobrien      /* __builtin_return (RESULT) causes the function to return the
585090075Sobrien	 value described by RESULT.  RESULT is address of the block of
585190075Sobrien	 memory returned by __builtin_apply.  */
585290075Sobrien    case BUILT_IN_RETURN:
585390075Sobrien      if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
5854169699Skan	expand_builtin_return (expand_normal (TREE_VALUE (arglist)));
585590075Sobrien      return const0_rtx;
585690075Sobrien
585790075Sobrien    case BUILT_IN_SAVEREGS:
585890075Sobrien      return expand_builtin_saveregs ();
585990075Sobrien
586090075Sobrien    case BUILT_IN_ARGS_INFO:
5861132727Skan      return expand_builtin_args_info (arglist);
586290075Sobrien
586390075Sobrien      /* Return the address of the first anonymous stack arg.  */
586490075Sobrien    case BUILT_IN_NEXT_ARG:
5865169699Skan      if (fold_builtin_next_arg (arglist))
5866169699Skan	return const0_rtx;
5867169699Skan      return expand_builtin_next_arg ();
586890075Sobrien
586990075Sobrien    case BUILT_IN_CLASSIFY_TYPE:
587090075Sobrien      return expand_builtin_classify_type (arglist);
587190075Sobrien
587290075Sobrien    case BUILT_IN_CONSTANT_P:
5873169699Skan      return const0_rtx;
587490075Sobrien
587590075Sobrien    case BUILT_IN_FRAME_ADDRESS:
587690075Sobrien    case BUILT_IN_RETURN_ADDRESS:
5877132727Skan      return expand_builtin_frame_address (fndecl, arglist);
587890075Sobrien
587990075Sobrien    /* Returns the address of the area where the structure is returned.
588090075Sobrien       0 otherwise.  */
588190075Sobrien    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
588290075Sobrien      if (arglist != 0
5883117404Skan	  || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
5884169699Skan	  || !MEM_P (DECL_RTL (DECL_RESULT (current_function_decl))))
5885117404Skan	return const0_rtx;
588690075Sobrien      else
5887117404Skan	return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
588890075Sobrien
588990075Sobrien    case BUILT_IN_ALLOCA:
589090075Sobrien      target = expand_builtin_alloca (arglist, target);
589190075Sobrien      if (target)
589290075Sobrien	return target;
589390075Sobrien      break;
589490075Sobrien
5895169699Skan    case BUILT_IN_STACK_SAVE:
5896169699Skan      return expand_stack_save ();
5897169699Skan
5898169699Skan    case BUILT_IN_STACK_RESTORE:
5899169699Skan      expand_stack_restore (TREE_VALUE (arglist));
5900169699Skan      return const0_rtx;
5901169699Skan
5902259563Spfg    case BUILT_IN_BSWAP32:
5903259563Spfg    case BUILT_IN_BSWAP64:
5904259563Spfg      target = expand_builtin_bswap (arglist, target, subtarget);
5905259563Spfg
5906259563Spfg      if (target)
5907259563Spfg	return target;
5908259563Spfg      break;
5909259563Spfg
5910169699Skan    CASE_INT_FN (BUILT_IN_FFS):
5911169699Skan    case BUILT_IN_FFSIMAX:
5912132727Skan      target = expand_builtin_unop (target_mode, arglist, target,
5913132727Skan				    subtarget, ffs_optab);
591490075Sobrien      if (target)
591590075Sobrien	return target;
591690075Sobrien      break;
591790075Sobrien
5918169699Skan    CASE_INT_FN (BUILT_IN_CLZ):
5919169699Skan    case BUILT_IN_CLZIMAX:
5920132727Skan      target = expand_builtin_unop (target_mode, arglist, target,
5921132727Skan				    subtarget, clz_optab);
5922132727Skan      if (target)
5923132727Skan	return target;
5924132727Skan      break;
5925132727Skan
5926169699Skan    CASE_INT_FN (BUILT_IN_CTZ):
5927169699Skan    case BUILT_IN_CTZIMAX:
5928132727Skan      target = expand_builtin_unop (target_mode, arglist, target,
5929132727Skan				    subtarget, ctz_optab);
5930132727Skan      if (target)
5931132727Skan	return target;
5932132727Skan      break;
5933132727Skan
5934169699Skan    CASE_INT_FN (BUILT_IN_POPCOUNT):
5935169699Skan    case BUILT_IN_POPCOUNTIMAX:
5936132727Skan      target = expand_builtin_unop (target_mode, arglist, target,
5937132727Skan				    subtarget, popcount_optab);
5938132727Skan      if (target)
5939132727Skan	return target;
5940132727Skan      break;
5941132727Skan
5942169699Skan    CASE_INT_FN (BUILT_IN_PARITY):
5943169699Skan    case BUILT_IN_PARITYIMAX:
5944132727Skan      target = expand_builtin_unop (target_mode, arglist, target,
5945132727Skan				    subtarget, parity_optab);
5946132727Skan      if (target)
5947132727Skan	return target;
5948132727Skan      break;
5949132727Skan
595090075Sobrien    case BUILT_IN_STRLEN:
5951132727Skan      target = expand_builtin_strlen (arglist, target, target_mode);
595290075Sobrien      if (target)
595390075Sobrien	return target;
595490075Sobrien      break;
595590075Sobrien
595690075Sobrien    case BUILT_IN_STRCPY:
5957169699Skan      target = expand_builtin_strcpy (fndecl, arglist, target, mode);
595890075Sobrien      if (target)
595990075Sobrien	return target;
596090075Sobrien      break;
596190075Sobrien
596290075Sobrien    case BUILT_IN_STRNCPY:
5963169699Skan      target = expand_builtin_strncpy (exp, target, mode);
596490075Sobrien      if (target)
596590075Sobrien	return target;
596690075Sobrien      break;
596790075Sobrien
5968132727Skan    case BUILT_IN_STPCPY:
5969169699Skan      target = expand_builtin_stpcpy (exp, target, mode);
5970132727Skan      if (target)
5971132727Skan	return target;
5972132727Skan      break;
5973132727Skan
597490075Sobrien    case BUILT_IN_STRCAT:
5975169699Skan      target = expand_builtin_strcat (fndecl, arglist, target, mode);
597690075Sobrien      if (target)
597790075Sobrien	return target;
597890075Sobrien      break;
597990075Sobrien
598090075Sobrien    case BUILT_IN_STRNCAT:
598190075Sobrien      target = expand_builtin_strncat (arglist, target, mode);
598290075Sobrien      if (target)
598390075Sobrien	return target;
598490075Sobrien      break;
598590075Sobrien
598690075Sobrien    case BUILT_IN_STRSPN:
598790075Sobrien      target = expand_builtin_strspn (arglist, target, mode);
598890075Sobrien      if (target)
598990075Sobrien	return target;
599090075Sobrien      break;
599190075Sobrien
599290075Sobrien    case BUILT_IN_STRCSPN:
599390075Sobrien      target = expand_builtin_strcspn (arglist, target, mode);
599490075Sobrien      if (target)
599590075Sobrien	return target;
599690075Sobrien      break;
599790075Sobrien
599890075Sobrien    case BUILT_IN_STRSTR:
5999169699Skan      target = expand_builtin_strstr (arglist, TREE_TYPE (exp), target, mode);
600090075Sobrien      if (target)
600190075Sobrien	return target;
600290075Sobrien      break;
600390075Sobrien
600490075Sobrien    case BUILT_IN_STRPBRK:
6005169699Skan      target = expand_builtin_strpbrk (arglist, TREE_TYPE (exp), target, mode);
600690075Sobrien      if (target)
600790075Sobrien	return target;
600890075Sobrien      break;
600990075Sobrien
601090075Sobrien    case BUILT_IN_INDEX:
601190075Sobrien    case BUILT_IN_STRCHR:
6012169699Skan      target = expand_builtin_strchr (arglist, TREE_TYPE (exp), target, mode);
601390075Sobrien      if (target)
601490075Sobrien	return target;
601590075Sobrien      break;
601690075Sobrien
601790075Sobrien    case BUILT_IN_RINDEX:
601890075Sobrien    case BUILT_IN_STRRCHR:
6019169699Skan      target = expand_builtin_strrchr (arglist, TREE_TYPE (exp), target, mode);
602090075Sobrien      if (target)
602190075Sobrien	return target;
602290075Sobrien      break;
602390075Sobrien
602490075Sobrien    case BUILT_IN_MEMCPY:
6025169699Skan      target = expand_builtin_memcpy (exp, target, mode);
602690075Sobrien      if (target)
602790075Sobrien	return target;
602890075Sobrien      break;
602990075Sobrien
6030132727Skan    case BUILT_IN_MEMPCPY:
6031169699Skan      target = expand_builtin_mempcpy (arglist, TREE_TYPE (exp), target, mode, /*endp=*/ 1);
6032132727Skan      if (target)
6033132727Skan	return target;
6034132727Skan      break;
6035132727Skan
6036132727Skan    case BUILT_IN_MEMMOVE:
6037169699Skan      target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target,
6038169699Skan				       mode, exp);
6039132727Skan      if (target)
6040132727Skan	return target;
6041132727Skan      break;
6042132727Skan
6043132727Skan    case BUILT_IN_BCOPY:
6044169699Skan      target = expand_builtin_bcopy (exp);
6045132727Skan      if (target)
6046132727Skan	return target;
6047132727Skan      break;
6048132727Skan
604990075Sobrien    case BUILT_IN_MEMSET:
6050169699Skan      target = expand_builtin_memset (arglist, target, mode, exp);
605190075Sobrien      if (target)
605290075Sobrien	return target;
605390075Sobrien      break;
605490075Sobrien
605590075Sobrien    case BUILT_IN_BZERO:
6056169699Skan      target = expand_builtin_bzero (exp);
605790075Sobrien      if (target)
605890075Sobrien	return target;
605990075Sobrien      break;
606090075Sobrien
606190075Sobrien    case BUILT_IN_STRCMP:
606290075Sobrien      target = expand_builtin_strcmp (exp, target, mode);
606390075Sobrien      if (target)
606490075Sobrien	return target;
606590075Sobrien      break;
606690075Sobrien
606790075Sobrien    case BUILT_IN_STRNCMP:
606890075Sobrien      target = expand_builtin_strncmp (exp, target, mode);
606990075Sobrien      if (target)
607090075Sobrien	return target;
607190075Sobrien      break;
607290075Sobrien
607390075Sobrien    case BUILT_IN_BCMP:
607490075Sobrien    case BUILT_IN_MEMCMP:
607590075Sobrien      target = expand_builtin_memcmp (exp, arglist, target, mode);
607690075Sobrien      if (target)
607790075Sobrien	return target;
607890075Sobrien      break;
607990075Sobrien
608090075Sobrien    case BUILT_IN_SETJMP:
6081169699Skan      /* This should have been lowered to the builtins below.  */
6082169699Skan      gcc_unreachable ();
6083169699Skan
6084169699Skan    case BUILT_IN_SETJMP_SETUP:
6085169699Skan      /* __builtin_setjmp_setup is passed a pointer to an array of five words
6086169699Skan          and the receiver label.  */
6087169699Skan      if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
6088169699Skan	{
6089169699Skan	  rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
6090169699Skan				      VOIDmode, EXPAND_NORMAL);
6091169699Skan	  tree label = TREE_OPERAND (TREE_VALUE (TREE_CHAIN (arglist)), 0);
6092169699Skan	  rtx label_r = label_rtx (label);
6093169699Skan
6094169699Skan	  /* This is copied from the handling of non-local gotos.  */
6095169699Skan	  expand_builtin_setjmp_setup (buf_addr, label_r);
6096169699Skan	  nonlocal_goto_handler_labels
6097169699Skan	    = gen_rtx_EXPR_LIST (VOIDmode, label_r,
6098169699Skan				 nonlocal_goto_handler_labels);
6099169699Skan	  /* ??? Do not let expand_label treat us as such since we would
6100169699Skan	     not want to be both on the list of non-local labels and on
6101169699Skan	     the list of forced labels.  */
6102169699Skan	  FORCED_LABEL (label) = 0;
6103169699Skan	  return const0_rtx;
6104169699Skan	}
610590075Sobrien      break;
610690075Sobrien
6107169699Skan    case BUILT_IN_SETJMP_DISPATCHER:
6108169699Skan       /* __builtin_setjmp_dispatcher is passed the dispatcher label.  */
6109169699Skan      if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
6110169699Skan	{
6111169699Skan	  tree label = TREE_OPERAND (TREE_VALUE (arglist), 0);
6112169699Skan	  rtx label_r = label_rtx (label);
6113169699Skan
6114169699Skan	  /* Remove the dispatcher label from the list of non-local labels
6115169699Skan	     since the receiver labels have been added to it above.  */
6116169699Skan	  remove_node_from_expr_list (label_r, &nonlocal_goto_handler_labels);
6117169699Skan	  return const0_rtx;
6118169699Skan	}
6119169699Skan      break;
6120169699Skan
6121169699Skan    case BUILT_IN_SETJMP_RECEIVER:
6122169699Skan       /* __builtin_setjmp_receiver is passed the receiver label.  */
6123169699Skan      if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
6124169699Skan	{
6125169699Skan	  tree label = TREE_OPERAND (TREE_VALUE (arglist), 0);
6126169699Skan	  rtx label_r = label_rtx (label);
6127169699Skan
6128169699Skan	  expand_builtin_setjmp_receiver (label_r);
6129169699Skan	  return const0_rtx;
6130169699Skan	}
6131169699Skan      break;
6132169699Skan
613390075Sobrien      /* __builtin_longjmp is passed a pointer to an array of five words.
613490075Sobrien	 It's similar to the C library longjmp function but works with
613590075Sobrien	 __builtin_setjmp above.  */
613690075Sobrien    case BUILT_IN_LONGJMP:
6137169699Skan      if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
613890075Sobrien	{
613990075Sobrien	  rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
6140169699Skan				      VOIDmode, EXPAND_NORMAL);
6141169699Skan	  rtx value = expand_normal (TREE_VALUE (TREE_CHAIN (arglist)));
614290075Sobrien
614390075Sobrien	  if (value != const1_rtx)
614490075Sobrien	    {
6145169699Skan	      error ("%<__builtin_longjmp%> second argument must be 1");
614690075Sobrien	      return const0_rtx;
614790075Sobrien	    }
614890075Sobrien
614990075Sobrien	  expand_builtin_longjmp (buf_addr, value);
615090075Sobrien	  return const0_rtx;
615190075Sobrien	}
6152169699Skan      break;
615390075Sobrien
6154169699Skan    case BUILT_IN_NONLOCAL_GOTO:
6155169699Skan      target = expand_builtin_nonlocal_goto (arglist);
6156169699Skan      if (target)
6157169699Skan	return target;
6158169699Skan      break;
6159169699Skan
6160169699Skan      /* This updates the setjmp buffer that is its argument with the value
6161169699Skan	 of the current stack pointer.  */
6162169699Skan    case BUILT_IN_UPDATE_SETJMP_BUF:
6163169699Skan      if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
6164169699Skan	{
6165169699Skan	  rtx buf_addr
6166169699Skan	    = expand_normal (TREE_VALUE (arglist));
6167169699Skan
6168169699Skan	  expand_builtin_update_setjmp_buf (buf_addr);
6169169699Skan	  return const0_rtx;
6170169699Skan	}
6171169699Skan      break;
6172169699Skan
617390075Sobrien    case BUILT_IN_TRAP:
617496263Sobrien      expand_builtin_trap ();
617590075Sobrien      return const0_rtx;
617690075Sobrien
6177132727Skan    case BUILT_IN_PRINTF:
6178169699Skan      target = expand_builtin_printf (exp, target, mode, false);
6179132727Skan      if (target)
6180132727Skan	return target;
6181132727Skan      break;
6182132727Skan
6183132727Skan    case BUILT_IN_PRINTF_UNLOCKED:
6184169699Skan      target = expand_builtin_printf (exp, target, mode, true);
6185132727Skan      if (target)
6186132727Skan	return target;
6187132727Skan      break;
6188132727Skan
618990075Sobrien    case BUILT_IN_FPUTS:
6190132727Skan      target = expand_builtin_fputs (arglist, target, false);
619190075Sobrien      if (target)
619290075Sobrien	return target;
619390075Sobrien      break;
619490075Sobrien    case BUILT_IN_FPUTS_UNLOCKED:
6195132727Skan      target = expand_builtin_fputs (arglist, target, true);
619690075Sobrien      if (target)
619790075Sobrien	return target;
619890075Sobrien      break;
619990075Sobrien
6200132727Skan    case BUILT_IN_FPRINTF:
6201169699Skan      target = expand_builtin_fprintf (exp, target, mode, false);
6202132727Skan      if (target)
6203132727Skan	return target;
6204132727Skan      break;
6205132727Skan
6206132727Skan    case BUILT_IN_FPRINTF_UNLOCKED:
6207169699Skan      target = expand_builtin_fprintf (exp, target, mode, true);
6208132727Skan      if (target)
6209132727Skan	return target;
6210132727Skan      break;
6211132727Skan
6212132727Skan    case BUILT_IN_SPRINTF:
6213132727Skan      target = expand_builtin_sprintf (arglist, target, mode);
6214132727Skan      if (target)
6215132727Skan	return target;
6216132727Skan      break;
6217132727Skan
6218169699Skan    CASE_FLT_FN (BUILT_IN_SIGNBIT):
6219169699Skan      target = expand_builtin_signbit (exp, target);
6220169699Skan      if (target)
6221169699Skan	return target;
6222169699Skan      break;
6223169699Skan
622490075Sobrien      /* Various hooks for the DWARF 2 __throw routine.  */
622590075Sobrien    case BUILT_IN_UNWIND_INIT:
622690075Sobrien      expand_builtin_unwind_init ();
622790075Sobrien      return const0_rtx;
622890075Sobrien    case BUILT_IN_DWARF_CFA:
622990075Sobrien      return virtual_cfa_rtx;
623090075Sobrien#ifdef DWARF2_UNWIND_INFO
6231117404Skan    case BUILT_IN_DWARF_SP_COLUMN:
6232117404Skan      return expand_builtin_dwarf_sp_column ();
623390075Sobrien    case BUILT_IN_INIT_DWARF_REG_SIZES:
623490075Sobrien      expand_builtin_init_dwarf_reg_sizes (TREE_VALUE (arglist));
623590075Sobrien      return const0_rtx;
623690075Sobrien#endif
623790075Sobrien    case BUILT_IN_FROB_RETURN_ADDR:
623890075Sobrien      return expand_builtin_frob_return_addr (TREE_VALUE (arglist));
623990075Sobrien    case BUILT_IN_EXTRACT_RETURN_ADDR:
624090075Sobrien      return expand_builtin_extract_return_addr (TREE_VALUE (arglist));
624190075Sobrien    case BUILT_IN_EH_RETURN:
624290075Sobrien      expand_builtin_eh_return (TREE_VALUE (arglist),
624390075Sobrien				TREE_VALUE (TREE_CHAIN (arglist)));
624490075Sobrien      return const0_rtx;
624590075Sobrien#ifdef EH_RETURN_DATA_REGNO
624690075Sobrien    case BUILT_IN_EH_RETURN_DATA_REGNO:
624790075Sobrien      return expand_builtin_eh_return_data_regno (arglist);
624890075Sobrien#endif
6249132727Skan    case BUILT_IN_EXTEND_POINTER:
6250132727Skan      return expand_builtin_extend_pointer (TREE_VALUE (arglist));
6251132727Skan
6252117404Skan    case BUILT_IN_VA_START:
625390075Sobrien    case BUILT_IN_STDARG_START:
6254117404Skan      return expand_builtin_va_start (arglist);
625590075Sobrien    case BUILT_IN_VA_END:
625690075Sobrien      return expand_builtin_va_end (arglist);
625790075Sobrien    case BUILT_IN_VA_COPY:
625890075Sobrien      return expand_builtin_va_copy (arglist);
625990075Sobrien    case BUILT_IN_EXPECT:
626090075Sobrien      return expand_builtin_expect (arglist, target);
626190075Sobrien    case BUILT_IN_PREFETCH:
626290075Sobrien      expand_builtin_prefetch (arglist);
626390075Sobrien      return const0_rtx;
626490075Sobrien
6265169699Skan    case BUILT_IN_PROFILE_FUNC_ENTER:
6266169699Skan      return expand_builtin_profile_func (false);
6267169699Skan    case BUILT_IN_PROFILE_FUNC_EXIT:
6268169699Skan      return expand_builtin_profile_func (true);
626990075Sobrien
6270169699Skan    case BUILT_IN_INIT_TRAMPOLINE:
6271169699Skan      return expand_builtin_init_trampoline (arglist);
6272169699Skan    case BUILT_IN_ADJUST_TRAMPOLINE:
6273169699Skan      return expand_builtin_adjust_trampoline (arglist);
6274169699Skan
6275169699Skan    case BUILT_IN_FORK:
6276169699Skan    case BUILT_IN_EXECL:
6277169699Skan    case BUILT_IN_EXECV:
6278169699Skan    case BUILT_IN_EXECLP:
6279169699Skan    case BUILT_IN_EXECLE:
6280169699Skan    case BUILT_IN_EXECVP:
6281169699Skan    case BUILT_IN_EXECVE:
6282169699Skan      target = expand_builtin_fork_or_exec (fndecl, arglist, target, ignore);
6283169699Skan      if (target)
6284169699Skan	return target;
6285169699Skan      break;
6286169699Skan
6287169699Skan    case BUILT_IN_FETCH_AND_ADD_1:
6288169699Skan    case BUILT_IN_FETCH_AND_ADD_2:
6289169699Skan    case BUILT_IN_FETCH_AND_ADD_4:
6290169699Skan    case BUILT_IN_FETCH_AND_ADD_8:
6291169699Skan    case BUILT_IN_FETCH_AND_ADD_16:
6292169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_ADD_1);
6293169699Skan      target = expand_builtin_sync_operation (mode, arglist, PLUS,
6294169699Skan					      false, target, ignore);
6295169699Skan      if (target)
6296169699Skan	return target;
6297169699Skan      break;
6298169699Skan
6299169699Skan    case BUILT_IN_FETCH_AND_SUB_1:
6300169699Skan    case BUILT_IN_FETCH_AND_SUB_2:
6301169699Skan    case BUILT_IN_FETCH_AND_SUB_4:
6302169699Skan    case BUILT_IN_FETCH_AND_SUB_8:
6303169699Skan    case BUILT_IN_FETCH_AND_SUB_16:
6304169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_SUB_1);
6305169699Skan      target = expand_builtin_sync_operation (mode, arglist, MINUS,
6306169699Skan					      false, target, ignore);
6307169699Skan      if (target)
6308169699Skan	return target;
6309169699Skan      break;
6310169699Skan
6311169699Skan    case BUILT_IN_FETCH_AND_OR_1:
6312169699Skan    case BUILT_IN_FETCH_AND_OR_2:
6313169699Skan    case BUILT_IN_FETCH_AND_OR_4:
6314169699Skan    case BUILT_IN_FETCH_AND_OR_8:
6315169699Skan    case BUILT_IN_FETCH_AND_OR_16:
6316169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_OR_1);
6317169699Skan      target = expand_builtin_sync_operation (mode, arglist, IOR,
6318169699Skan					      false, target, ignore);
6319169699Skan      if (target)
6320169699Skan	return target;
6321169699Skan      break;
6322169699Skan
6323169699Skan    case BUILT_IN_FETCH_AND_AND_1:
6324169699Skan    case BUILT_IN_FETCH_AND_AND_2:
6325169699Skan    case BUILT_IN_FETCH_AND_AND_4:
6326169699Skan    case BUILT_IN_FETCH_AND_AND_8:
6327169699Skan    case BUILT_IN_FETCH_AND_AND_16:
6328169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_AND_1);
6329169699Skan      target = expand_builtin_sync_operation (mode, arglist, AND,
6330169699Skan					      false, target, ignore);
6331169699Skan      if (target)
6332169699Skan	return target;
6333169699Skan      break;
6334169699Skan
6335169699Skan    case BUILT_IN_FETCH_AND_XOR_1:
6336169699Skan    case BUILT_IN_FETCH_AND_XOR_2:
6337169699Skan    case BUILT_IN_FETCH_AND_XOR_4:
6338169699Skan    case BUILT_IN_FETCH_AND_XOR_8:
6339169699Skan    case BUILT_IN_FETCH_AND_XOR_16:
6340169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_XOR_1);
6341169699Skan      target = expand_builtin_sync_operation (mode, arglist, XOR,
6342169699Skan					      false, target, ignore);
6343169699Skan      if (target)
6344169699Skan	return target;
6345169699Skan      break;
6346169699Skan
6347169699Skan    case BUILT_IN_FETCH_AND_NAND_1:
6348169699Skan    case BUILT_IN_FETCH_AND_NAND_2:
6349169699Skan    case BUILT_IN_FETCH_AND_NAND_4:
6350169699Skan    case BUILT_IN_FETCH_AND_NAND_8:
6351169699Skan    case BUILT_IN_FETCH_AND_NAND_16:
6352169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_NAND_1);
6353169699Skan      target = expand_builtin_sync_operation (mode, arglist, NOT,
6354169699Skan					      false, target, ignore);
6355169699Skan      if (target)
6356169699Skan	return target;
6357169699Skan      break;
6358169699Skan
6359169699Skan    case BUILT_IN_ADD_AND_FETCH_1:
6360169699Skan    case BUILT_IN_ADD_AND_FETCH_2:
6361169699Skan    case BUILT_IN_ADD_AND_FETCH_4:
6362169699Skan    case BUILT_IN_ADD_AND_FETCH_8:
6363169699Skan    case BUILT_IN_ADD_AND_FETCH_16:
6364169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_ADD_AND_FETCH_1);
6365169699Skan      target = expand_builtin_sync_operation (mode, arglist, PLUS,
6366169699Skan					      true, target, ignore);
6367169699Skan      if (target)
6368169699Skan	return target;
6369169699Skan      break;
6370169699Skan
6371169699Skan    case BUILT_IN_SUB_AND_FETCH_1:
6372169699Skan    case BUILT_IN_SUB_AND_FETCH_2:
6373169699Skan    case BUILT_IN_SUB_AND_FETCH_4:
6374169699Skan    case BUILT_IN_SUB_AND_FETCH_8:
6375169699Skan    case BUILT_IN_SUB_AND_FETCH_16:
6376169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_SUB_AND_FETCH_1);
6377169699Skan      target = expand_builtin_sync_operation (mode, arglist, MINUS,
6378169699Skan					      true, target, ignore);
6379169699Skan      if (target)
6380169699Skan	return target;
6381169699Skan      break;
6382169699Skan
6383169699Skan    case BUILT_IN_OR_AND_FETCH_1:
6384169699Skan    case BUILT_IN_OR_AND_FETCH_2:
6385169699Skan    case BUILT_IN_OR_AND_FETCH_4:
6386169699Skan    case BUILT_IN_OR_AND_FETCH_8:
6387169699Skan    case BUILT_IN_OR_AND_FETCH_16:
6388169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_OR_AND_FETCH_1);
6389169699Skan      target = expand_builtin_sync_operation (mode, arglist, IOR,
6390169699Skan					      true, target, ignore);
6391169699Skan      if (target)
6392169699Skan	return target;
6393169699Skan      break;
6394169699Skan
6395169699Skan    case BUILT_IN_AND_AND_FETCH_1:
6396169699Skan    case BUILT_IN_AND_AND_FETCH_2:
6397169699Skan    case BUILT_IN_AND_AND_FETCH_4:
6398169699Skan    case BUILT_IN_AND_AND_FETCH_8:
6399169699Skan    case BUILT_IN_AND_AND_FETCH_16:
6400169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_AND_AND_FETCH_1);
6401169699Skan      target = expand_builtin_sync_operation (mode, arglist, AND,
6402169699Skan					      true, target, ignore);
6403169699Skan      if (target)
6404169699Skan	return target;
6405169699Skan      break;
6406169699Skan
6407169699Skan    case BUILT_IN_XOR_AND_FETCH_1:
6408169699Skan    case BUILT_IN_XOR_AND_FETCH_2:
6409169699Skan    case BUILT_IN_XOR_AND_FETCH_4:
6410169699Skan    case BUILT_IN_XOR_AND_FETCH_8:
6411169699Skan    case BUILT_IN_XOR_AND_FETCH_16:
6412169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_XOR_AND_FETCH_1);
6413169699Skan      target = expand_builtin_sync_operation (mode, arglist, XOR,
6414169699Skan					      true, target, ignore);
6415169699Skan      if (target)
6416169699Skan	return target;
6417169699Skan      break;
6418169699Skan
6419169699Skan    case BUILT_IN_NAND_AND_FETCH_1:
6420169699Skan    case BUILT_IN_NAND_AND_FETCH_2:
6421169699Skan    case BUILT_IN_NAND_AND_FETCH_4:
6422169699Skan    case BUILT_IN_NAND_AND_FETCH_8:
6423169699Skan    case BUILT_IN_NAND_AND_FETCH_16:
6424169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_NAND_AND_FETCH_1);
6425169699Skan      target = expand_builtin_sync_operation (mode, arglist, NOT,
6426169699Skan					      true, target, ignore);
6427169699Skan      if (target)
6428169699Skan	return target;
6429169699Skan      break;
6430169699Skan
6431169699Skan    case BUILT_IN_BOOL_COMPARE_AND_SWAP_1:
6432169699Skan    case BUILT_IN_BOOL_COMPARE_AND_SWAP_2:
6433169699Skan    case BUILT_IN_BOOL_COMPARE_AND_SWAP_4:
6434169699Skan    case BUILT_IN_BOOL_COMPARE_AND_SWAP_8:
6435169699Skan    case BUILT_IN_BOOL_COMPARE_AND_SWAP_16:
6436169699Skan      if (mode == VOIDmode)
6437169699Skan	mode = TYPE_MODE (boolean_type_node);
6438169699Skan      if (!target || !register_operand (target, mode))
6439169699Skan	target = gen_reg_rtx (mode);
6440169699Skan
6441169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_BOOL_COMPARE_AND_SWAP_1);
6442169699Skan      target = expand_builtin_compare_and_swap (mode, arglist, true, target);
6443169699Skan      if (target)
6444169699Skan	return target;
6445169699Skan      break;
6446169699Skan
6447169699Skan    case BUILT_IN_VAL_COMPARE_AND_SWAP_1:
6448169699Skan    case BUILT_IN_VAL_COMPARE_AND_SWAP_2:
6449169699Skan    case BUILT_IN_VAL_COMPARE_AND_SWAP_4:
6450169699Skan    case BUILT_IN_VAL_COMPARE_AND_SWAP_8:
6451169699Skan    case BUILT_IN_VAL_COMPARE_AND_SWAP_16:
6452169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_VAL_COMPARE_AND_SWAP_1);
6453169699Skan      target = expand_builtin_compare_and_swap (mode, arglist, false, target);
6454169699Skan      if (target)
6455169699Skan	return target;
6456169699Skan      break;
6457169699Skan
6458169699Skan    case BUILT_IN_LOCK_TEST_AND_SET_1:
6459169699Skan    case BUILT_IN_LOCK_TEST_AND_SET_2:
6460169699Skan    case BUILT_IN_LOCK_TEST_AND_SET_4:
6461169699Skan    case BUILT_IN_LOCK_TEST_AND_SET_8:
6462169699Skan    case BUILT_IN_LOCK_TEST_AND_SET_16:
6463169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_TEST_AND_SET_1);
6464169699Skan      target = expand_builtin_lock_test_and_set (mode, arglist, target);
6465169699Skan      if (target)
6466169699Skan	return target;
6467169699Skan      break;
6468169699Skan
6469169699Skan    case BUILT_IN_LOCK_RELEASE_1:
6470169699Skan    case BUILT_IN_LOCK_RELEASE_2:
6471169699Skan    case BUILT_IN_LOCK_RELEASE_4:
6472169699Skan    case BUILT_IN_LOCK_RELEASE_8:
6473169699Skan    case BUILT_IN_LOCK_RELEASE_16:
6474169699Skan      mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_RELEASE_1);
6475169699Skan      expand_builtin_lock_release (mode, arglist);
6476169699Skan      return const0_rtx;
6477169699Skan
6478169699Skan    case BUILT_IN_SYNCHRONIZE:
6479169699Skan      expand_builtin_synchronize ();
6480169699Skan      return const0_rtx;
6481169699Skan
6482169699Skan    case BUILT_IN_OBJECT_SIZE:
6483169699Skan      return expand_builtin_object_size (exp);
6484169699Skan
6485169699Skan    case BUILT_IN_MEMCPY_CHK:
6486169699Skan    case BUILT_IN_MEMPCPY_CHK:
6487169699Skan    case BUILT_IN_MEMMOVE_CHK:
6488169699Skan    case BUILT_IN_MEMSET_CHK:
6489169699Skan      target = expand_builtin_memory_chk (exp, target, mode, fcode);
6490169699Skan      if (target)
6491169699Skan	return target;
6492169699Skan      break;
6493169699Skan
6494169699Skan    case BUILT_IN_STRCPY_CHK:
6495169699Skan    case BUILT_IN_STPCPY_CHK:
6496169699Skan    case BUILT_IN_STRNCPY_CHK:
6497169699Skan    case BUILT_IN_STRCAT_CHK:
6498169699Skan    case BUILT_IN_SNPRINTF_CHK:
6499169699Skan    case BUILT_IN_VSNPRINTF_CHK:
6500169699Skan      maybe_emit_chk_warning (exp, fcode);
6501169699Skan      break;
6502169699Skan
6503169699Skan    case BUILT_IN_SPRINTF_CHK:
6504169699Skan    case BUILT_IN_VSPRINTF_CHK:
6505169699Skan      maybe_emit_sprintf_chk_warning (exp, fcode);
6506169699Skan      break;
6507169699Skan
6508117404Skan    default:	/* just do library call, if unknown builtin */
6509169699Skan      break;
651090075Sobrien    }
651190075Sobrien
651290075Sobrien  /* The switch statement above can drop through to cause the function
651390075Sobrien     to be called normally.  */
651490075Sobrien  return expand_call (exp, target, ignore);
651590075Sobrien}
651690075Sobrien
6517132727Skan/* Determine whether a tree node represents a call to a built-in
6518132727Skan   function.  If the tree T is a call to a built-in function with
6519132727Skan   the right number of arguments of the appropriate types, return
6520132727Skan   the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
6521132727Skan   Otherwise the return value is END_BUILTINS.  */
6522132727Skan
6523132727Skanenum built_in_function
6524132727Skanbuiltin_mathfn_code (tree t)
6525132727Skan{
6526132727Skan  tree fndecl, arglist, parmlist;
6527132727Skan  tree argtype, parmtype;
6528132727Skan
6529132727Skan  if (TREE_CODE (t) != CALL_EXPR
6530132727Skan      || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR)
6531132727Skan    return END_BUILTINS;
6532132727Skan
6533132727Skan  fndecl = get_callee_fndecl (t);
6534132727Skan  if (fndecl == NULL_TREE
6535132727Skan      || TREE_CODE (fndecl) != FUNCTION_DECL
6536132727Skan      || ! DECL_BUILT_IN (fndecl)
6537132727Skan      || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
6538132727Skan    return END_BUILTINS;
6539132727Skan
6540132727Skan  arglist = TREE_OPERAND (t, 1);
6541132727Skan  parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
6542132727Skan  for (; parmlist; parmlist = TREE_CHAIN (parmlist))
6543132727Skan    {
6544132727Skan      /* If a function doesn't take a variable number of arguments,
6545132727Skan	 the last element in the list will have type `void'.  */
6546132727Skan      parmtype = TREE_VALUE (parmlist);
6547132727Skan      if (VOID_TYPE_P (parmtype))
6548132727Skan	{
6549132727Skan	  if (arglist)
6550132727Skan	    return END_BUILTINS;
6551132727Skan	  return DECL_FUNCTION_CODE (fndecl);
6552132727Skan	}
6553132727Skan
6554132727Skan      if (! arglist)
6555132727Skan	return END_BUILTINS;
6556132727Skan
6557132727Skan      argtype = TREE_TYPE (TREE_VALUE (arglist));
6558132727Skan
6559132727Skan      if (SCALAR_FLOAT_TYPE_P (parmtype))
6560132727Skan	{
6561132727Skan	  if (! SCALAR_FLOAT_TYPE_P (argtype))
6562132727Skan	    return END_BUILTINS;
6563132727Skan	}
6564132727Skan      else if (COMPLEX_FLOAT_TYPE_P (parmtype))
6565132727Skan	{
6566132727Skan	  if (! COMPLEX_FLOAT_TYPE_P (argtype))
6567132727Skan	    return END_BUILTINS;
6568132727Skan	}
6569132727Skan      else if (POINTER_TYPE_P (parmtype))
6570132727Skan	{
6571132727Skan	  if (! POINTER_TYPE_P (argtype))
6572132727Skan	    return END_BUILTINS;
6573132727Skan	}
6574132727Skan      else if (INTEGRAL_TYPE_P (parmtype))
6575132727Skan	{
6576132727Skan	  if (! INTEGRAL_TYPE_P (argtype))
6577132727Skan	    return END_BUILTINS;
6578132727Skan	}
6579132727Skan      else
6580132727Skan	return END_BUILTINS;
6581132727Skan
6582132727Skan      arglist = TREE_CHAIN (arglist);
6583132727Skan    }
6584132727Skan
6585132727Skan  /* Variable-length argument list.  */
6586132727Skan  return DECL_FUNCTION_CODE (fndecl);
6587132727Skan}
6588132727Skan
658990075Sobrien/* Fold a call to __builtin_constant_p, if we know it will evaluate to a
659090075Sobrien   constant.  ARGLIST is the argument list of the call.  */
659190075Sobrien
659290075Sobrienstatic tree
6593132727Skanfold_builtin_constant_p (tree arglist)
659490075Sobrien{
659590075Sobrien  if (arglist == 0)
659690075Sobrien    return 0;
659790075Sobrien
659890075Sobrien  arglist = TREE_VALUE (arglist);
659990075Sobrien
660090075Sobrien  /* We return 1 for a numeric type that's known to be a constant
660190075Sobrien     value at compile-time or for an aggregate type that's a
660290075Sobrien     literal constant.  */
660390075Sobrien  STRIP_NOPS (arglist);
660490075Sobrien
660590075Sobrien  /* If we know this is a constant, emit the constant of one.  */
6606169699Skan  if (CONSTANT_CLASS_P (arglist)
660790075Sobrien      || (TREE_CODE (arglist) == CONSTRUCTOR
6608169699Skan	  && TREE_CONSTANT (arglist)))
660990075Sobrien    return integer_one_node;
6610169699Skan  if (TREE_CODE (arglist) == ADDR_EXPR)
6611169699Skan    {
6612169699Skan       tree op = TREE_OPERAND (arglist, 0);
6613169699Skan       if (TREE_CODE (op) == STRING_CST
6614169699Skan	   || (TREE_CODE (op) == ARRAY_REF
6615169699Skan	       && integer_zerop (TREE_OPERAND (op, 1))
6616169699Skan	       && TREE_CODE (TREE_OPERAND (op, 0)) == STRING_CST))
6617169699Skan	 return integer_one_node;
6618169699Skan    }
661990075Sobrien
6620132727Skan  /* If this expression has side effects, show we don't know it to be a
6621132727Skan     constant.  Likewise if it's a pointer or aggregate type since in
6622132727Skan     those case we only want literals, since those are only optimized
662390075Sobrien     when generating RTL, not later.
662490075Sobrien     And finally, if we are compiling an initializer, not code, we
662590075Sobrien     need to return a definite result now; there's not going to be any
662690075Sobrien     more optimization done.  */
6627132727Skan  if (TREE_SIDE_EFFECTS (arglist)
662890075Sobrien      || AGGREGATE_TYPE_P (TREE_TYPE (arglist))
662990075Sobrien      || POINTER_TYPE_P (TREE_TYPE (arglist))
6630169699Skan      || cfun == 0
6631169699Skan      || folding_initializer)
663290075Sobrien    return integer_zero_node;
663390075Sobrien
663490075Sobrien  return 0;
663590075Sobrien}
663690075Sobrien
6637169699Skan/* Fold a call to __builtin_expect, if we expect that a comparison against
6638169699Skan   the argument will fold to a constant.  In practice, this means a true
6639169699Skan   constant or the address of a non-weak symbol.  ARGLIST is the argument
6640169699Skan   list of the call.  */
6641169699Skan
6642169699Skanstatic tree
6643169699Skanfold_builtin_expect (tree arglist)
6644169699Skan{
6645169699Skan  tree arg, inner;
6646169699Skan
6647169699Skan  if (arglist == 0)
6648169699Skan    return 0;
6649169699Skan
6650169699Skan  arg = TREE_VALUE (arglist);
6651169699Skan
6652169699Skan  /* If the argument isn't invariant, then there's nothing we can do.  */
6653169699Skan  if (!TREE_INVARIANT (arg))
6654169699Skan    return 0;
6655169699Skan
6656169699Skan  /* If we're looking at an address of a weak decl, then do not fold.  */
6657169699Skan  inner = arg;
6658169699Skan  STRIP_NOPS (inner);
6659169699Skan  if (TREE_CODE (inner) == ADDR_EXPR)
6660169699Skan    {
6661169699Skan      do
6662169699Skan	{
6663169699Skan	  inner = TREE_OPERAND (inner, 0);
6664169699Skan	}
6665169699Skan      while (TREE_CODE (inner) == COMPONENT_REF
6666169699Skan	     || TREE_CODE (inner) == ARRAY_REF);
6667169699Skan      if (DECL_P (inner) && DECL_WEAK (inner))
6668169699Skan	return 0;
6669169699Skan    }
6670169699Skan
6671169699Skan  /* Otherwise, ARG already has the proper type for the return value.  */
6672169699Skan  return arg;
6673169699Skan}
6674169699Skan
667590075Sobrien/* Fold a call to __builtin_classify_type.  */
667690075Sobrien
667790075Sobrienstatic tree
6678132727Skanfold_builtin_classify_type (tree arglist)
667990075Sobrien{
668090075Sobrien  if (arglist == 0)
6681169699Skan    return build_int_cst (NULL_TREE, no_type_class);
668290075Sobrien
6683169699Skan  return build_int_cst (NULL_TREE,
6684169699Skan			type_to_class (TREE_TYPE (TREE_VALUE (arglist))));
668590075Sobrien}
668690075Sobrien
6687169699Skan/* Fold a call to __builtin_strlen.  */
6688169699Skan
6689169699Skanstatic tree
6690169699Skanfold_builtin_strlen (tree arglist)
6691169699Skan{
6692169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
6693169699Skan    return NULL_TREE;
6694169699Skan  else
6695169699Skan    {
6696169699Skan      tree len = c_strlen (TREE_VALUE (arglist), 0);
6697169699Skan
6698169699Skan      if (len)
6699169699Skan	{
6700169699Skan	  /* Convert from the internal "sizetype" type to "size_t".  */
6701169699Skan	  if (size_type_node)
6702169699Skan	    len = fold_convert (size_type_node, len);
6703169699Skan	  return len;
6704169699Skan	}
6705169699Skan
6706169699Skan      return NULL_TREE;
6707169699Skan    }
6708169699Skan}
6709169699Skan
6710117404Skan/* Fold a call to __builtin_inf or __builtin_huge_val.  */
6711117404Skan
6712117404Skanstatic tree
6713132727Skanfold_builtin_inf (tree type, int warn)
6714117404Skan{
6715117404Skan  REAL_VALUE_TYPE real;
6716117404Skan
6717169699Skan  /* __builtin_inff is intended to be usable to define INFINITY on all
6718169699Skan     targets.  If an infinity is not available, INFINITY expands "to a
6719169699Skan     positive constant of type float that overflows at translation
6720169699Skan     time", footnote "In this case, using INFINITY will violate the
6721169699Skan     constraint in 6.4.4 and thus require a diagnostic." (C99 7.12#4).
6722169699Skan     Thus we pedwarn to ensure this constraint violation is
6723169699Skan     diagnosed.  */
6724117404Skan  if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
6725169699Skan    pedwarn ("target format does not support infinity");
6726117404Skan
6727117404Skan  real_inf (&real);
6728117404Skan  return build_real (type, real);
6729117404Skan}
6730117404Skan
6731117404Skan/* Fold a call to __builtin_nan or __builtin_nans.  */
6732117404Skan
6733117404Skanstatic tree
6734132727Skanfold_builtin_nan (tree arglist, tree type, int quiet)
6735117404Skan{
6736117404Skan  REAL_VALUE_TYPE real;
6737117404Skan  const char *str;
6738117404Skan
6739117404Skan  if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
6740117404Skan    return 0;
6741117404Skan  str = c_getstr (TREE_VALUE (arglist));
6742117404Skan  if (!str)
6743117404Skan    return 0;
6744117404Skan
6745117404Skan  if (!real_nan (&real, str, quiet, TYPE_MODE (type)))
6746117404Skan    return 0;
6747117404Skan
6748117404Skan  return build_real (type, real);
6749117404Skan}
6750117404Skan
6751132727Skan/* Return true if the floating point expression T has an integer value.
6752132727Skan   We also allow +Inf, -Inf and NaN to be considered integer values.  */
6753132727Skan
6754132727Skanstatic bool
6755132727Skaninteger_valued_real_p (tree t)
6756132727Skan{
6757132727Skan  switch (TREE_CODE (t))
6758132727Skan    {
6759132727Skan    case FLOAT_EXPR:
6760132727Skan      return true;
6761132727Skan
6762132727Skan    case ABS_EXPR:
6763132727Skan    case SAVE_EXPR:
6764132727Skan    case NON_LVALUE_EXPR:
6765132727Skan      return integer_valued_real_p (TREE_OPERAND (t, 0));
6766132727Skan
6767132727Skan    case COMPOUND_EXPR:
6768132727Skan    case MODIFY_EXPR:
6769132727Skan    case BIND_EXPR:
6770132727Skan      return integer_valued_real_p (TREE_OPERAND (t, 1));
6771132727Skan
6772132727Skan    case PLUS_EXPR:
6773132727Skan    case MINUS_EXPR:
6774132727Skan    case MULT_EXPR:
6775132727Skan    case MIN_EXPR:
6776132727Skan    case MAX_EXPR:
6777132727Skan      return integer_valued_real_p (TREE_OPERAND (t, 0))
6778132727Skan	     && integer_valued_real_p (TREE_OPERAND (t, 1));
6779132727Skan
6780132727Skan    case COND_EXPR:
6781132727Skan      return integer_valued_real_p (TREE_OPERAND (t, 1))
6782132727Skan	     && integer_valued_real_p (TREE_OPERAND (t, 2));
6783132727Skan
6784132727Skan    case REAL_CST:
6785132727Skan      if (! TREE_CONSTANT_OVERFLOW (t))
6786132727Skan      {
6787169699Skan	REAL_VALUE_TYPE c, cint;
6788132727Skan
6789132727Skan	c = TREE_REAL_CST (t);
6790132727Skan	real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
6791132727Skan	return real_identical (&c, &cint);
6792132727Skan      }
6793169699Skan      break;
6794132727Skan
6795132727Skan    case NOP_EXPR:
6796132727Skan      {
6797132727Skan	tree type = TREE_TYPE (TREE_OPERAND (t, 0));
6798132727Skan	if (TREE_CODE (type) == INTEGER_TYPE)
6799132727Skan	  return true;
6800132727Skan	if (TREE_CODE (type) == REAL_TYPE)
6801132727Skan	  return integer_valued_real_p (TREE_OPERAND (t, 0));
6802132727Skan	break;
6803132727Skan      }
6804132727Skan
6805132727Skan    case CALL_EXPR:
6806132727Skan      switch (builtin_mathfn_code (t))
6807132727Skan	{
6808169699Skan	CASE_FLT_FN (BUILT_IN_CEIL):
6809169699Skan	CASE_FLT_FN (BUILT_IN_FLOOR):
6810169699Skan	CASE_FLT_FN (BUILT_IN_NEARBYINT):
6811169699Skan	CASE_FLT_FN (BUILT_IN_RINT):
6812169699Skan	CASE_FLT_FN (BUILT_IN_ROUND):
6813169699Skan	CASE_FLT_FN (BUILT_IN_TRUNC):
6814132727Skan	  return true;
6815132727Skan
6816132727Skan	default:
6817132727Skan	  break;
6818132727Skan	}
6819132727Skan      break;
6820132727Skan
6821132727Skan    default:
6822132727Skan      break;
6823132727Skan    }
6824132727Skan  return false;
6825132727Skan}
6826132727Skan
6827132727Skan/* EXP is assumed to be builtin call where truncation can be propagated
6828132727Skan   across (for instance floor((double)f) == (double)floorf (f).
6829132727Skan   Do the transformation.  */
6830132727Skan
6831132727Skanstatic tree
6832169699Skanfold_trunc_transparent_mathfn (tree fndecl, tree arglist)
6833132727Skan{
6834132727Skan  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
6835132727Skan  tree arg;
6836132727Skan
6837132727Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
6838132727Skan    return 0;
6839132727Skan
6840132727Skan  arg = TREE_VALUE (arglist);
6841132727Skan  /* Integer rounding functions are idempotent.  */
6842132727Skan  if (fcode == builtin_mathfn_code (arg))
6843132727Skan    return arg;
6844132727Skan
6845132727Skan  /* If argument is already integer valued, and we don't need to worry
6846132727Skan     about setting errno, there's no need to perform rounding.  */
6847132727Skan  if (! flag_errno_math && integer_valued_real_p (arg))
6848132727Skan    return arg;
6849132727Skan
6850132727Skan  if (optimize)
6851132727Skan    {
6852132727Skan      tree arg0 = strip_float_extensions (arg);
6853169699Skan      tree ftype = TREE_TYPE (TREE_TYPE (fndecl));
6854132727Skan      tree newtype = TREE_TYPE (arg0);
6855132727Skan      tree decl;
6856132727Skan
6857132727Skan      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
6858132727Skan	  && (decl = mathfn_built_in (newtype, fcode)))
6859132727Skan	{
6860132727Skan	  arglist =
6861169699Skan	    build_tree_list (NULL_TREE, fold_convert (newtype, arg0));
6862169699Skan	  return fold_convert (ftype,
6863169699Skan			       build_function_call_expr (decl, arglist));
6864132727Skan	}
6865132727Skan    }
6866132727Skan  return 0;
6867132727Skan}
6868132727Skan
6869169699Skan/* EXP is assumed to be builtin call which can narrow the FP type of
6870169699Skan   the argument, for instance lround((double)f) -> lroundf (f).  */
6871132727Skan
6872132727Skanstatic tree
6873169699Skanfold_fixed_mathfn (tree fndecl, tree arglist)
6874132727Skan{
6875169699Skan  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
6876132727Skan  tree arg;
6877132727Skan
6878169699Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
6879169699Skan    return 0;
6880169699Skan
6881169699Skan  arg = TREE_VALUE (arglist);
6882169699Skan
6883169699Skan  /* If argument is already integer valued, and we don't need to worry
6884169699Skan     about setting errno, there's no need to perform rounding.  */
6885169699Skan  if (! flag_errno_math && integer_valued_real_p (arg))
6886169699Skan    return fold_build1 (FIX_TRUNC_EXPR, TREE_TYPE (TREE_TYPE (fndecl)), arg);
6887169699Skan
6888169699Skan  if (optimize)
6889169699Skan    {
6890169699Skan      tree ftype = TREE_TYPE (arg);
6891169699Skan      tree arg0 = strip_float_extensions (arg);
6892169699Skan      tree newtype = TREE_TYPE (arg0);
6893169699Skan      tree decl;
6894169699Skan
6895169699Skan      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
6896169699Skan	  && (decl = mathfn_built_in (newtype, fcode)))
6897169699Skan	{
6898169699Skan	  arglist =
6899169699Skan	    build_tree_list (NULL_TREE, fold_convert (newtype, arg0));
6900169699Skan	  return build_function_call_expr (decl, arglist);
6901169699Skan	}
6902169699Skan    }
6903169699Skan
6904169699Skan  /* Canonicalize llround (x) to lround (x) on LP64 targets where
6905169699Skan     sizeof (long long) == sizeof (long).  */
6906169699Skan  if (TYPE_PRECISION (long_long_integer_type_node)
6907169699Skan      == TYPE_PRECISION (long_integer_type_node))
6908169699Skan    {
6909169699Skan      tree newfn = NULL_TREE;
6910169699Skan      switch (fcode)
6911169699Skan	{
6912169699Skan	CASE_FLT_FN (BUILT_IN_LLCEIL):
6913169699Skan	  newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL);
6914169699Skan	  break;
6915169699Skan
6916169699Skan	CASE_FLT_FN (BUILT_IN_LLFLOOR):
6917169699Skan	  newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR);
6918169699Skan	  break;
6919169699Skan
6920169699Skan	CASE_FLT_FN (BUILT_IN_LLROUND):
6921169699Skan	  newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND);
6922169699Skan	  break;
6923169699Skan
6924169699Skan	CASE_FLT_FN (BUILT_IN_LLRINT):
6925169699Skan	  newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT);
6926169699Skan	  break;
6927169699Skan
6928169699Skan	default:
6929169699Skan	  break;
6930169699Skan	}
6931169699Skan
6932169699Skan      if (newfn)
6933169699Skan	{
6934169699Skan	  tree newcall = build_function_call_expr (newfn, arglist);
6935169699Skan	  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), newcall);
6936169699Skan	}
6937169699Skan    }
6938169699Skan
6939169699Skan  return 0;
6940169699Skan}
6941169699Skan
6942169699Skan/* Fold function call to builtin cabs, cabsf or cabsl.  ARGLIST
6943169699Skan   is the argument list, TYPE is the return type and FNDECL is the
6944169699Skan   original function DECL.  Return NULL_TREE if no if no simplification
6945169699Skan   can be made.  */
6946169699Skan
6947169699Skanstatic tree
6948169699Skanfold_builtin_cabs (tree arglist, tree type, tree fndecl)
6949169699Skan{
6950169699Skan  tree arg;
6951169699Skan
6952132727Skan  if (!arglist || TREE_CHAIN (arglist))
6953132727Skan    return NULL_TREE;
6954132727Skan
6955132727Skan  arg = TREE_VALUE (arglist);
6956132727Skan  if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE
6957132727Skan      || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
6958132727Skan    return NULL_TREE;
6959132727Skan
6960132727Skan  /* Evaluate cabs of a constant at compile-time.  */
6961132727Skan  if (flag_unsafe_math_optimizations
6962132727Skan      && TREE_CODE (arg) == COMPLEX_CST
6963132727Skan      && TREE_CODE (TREE_REALPART (arg)) == REAL_CST
6964132727Skan      && TREE_CODE (TREE_IMAGPART (arg)) == REAL_CST
6965132727Skan      && ! TREE_CONSTANT_OVERFLOW (TREE_REALPART (arg))
6966132727Skan      && ! TREE_CONSTANT_OVERFLOW (TREE_IMAGPART (arg)))
6967132727Skan    {
6968132727Skan      REAL_VALUE_TYPE r, i;
6969132727Skan
6970132727Skan      r = TREE_REAL_CST (TREE_REALPART (arg));
6971132727Skan      i = TREE_REAL_CST (TREE_IMAGPART (arg));
6972132727Skan
6973132727Skan      real_arithmetic (&r, MULT_EXPR, &r, &r);
6974132727Skan      real_arithmetic (&i, MULT_EXPR, &i, &i);
6975132727Skan      real_arithmetic (&r, PLUS_EXPR, &r, &i);
6976132727Skan      if (real_sqrt (&r, TYPE_MODE (type), &r)
6977132727Skan	  || ! flag_trapping_math)
6978132727Skan	return build_real (type, r);
6979132727Skan    }
6980132727Skan
6981132727Skan  /* If either part is zero, cabs is fabs of the other.  */
6982132727Skan  if (TREE_CODE (arg) == COMPLEX_EXPR
6983132727Skan      && real_zerop (TREE_OPERAND (arg, 0)))
6984169699Skan    return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1));
6985132727Skan  if (TREE_CODE (arg) == COMPLEX_EXPR
6986132727Skan      && real_zerop (TREE_OPERAND (arg, 1)))
6987169699Skan    return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0));
6988132727Skan
6989169699Skan  /* Optimize cabs(-z) and cabs(conj(z)) as cabs(z).  */
6990169699Skan  if (TREE_CODE (arg) == NEGATE_EXPR
6991169699Skan      || TREE_CODE (arg) == CONJ_EXPR)
6992132727Skan    {
6993169699Skan      tree arglist = build_tree_list (NULL_TREE, TREE_OPERAND (arg, 0));
6994169699Skan      return build_function_call_expr (fndecl, arglist);
6995169699Skan    }
6996132727Skan
6997169699Skan  /* Don't do this when optimizing for size.  */
6998169699Skan  if (flag_unsafe_math_optimizations
6999169699Skan      && optimize && !optimize_size)
7000169699Skan    {
7001169699Skan      tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
7002132727Skan
7003132727Skan      if (sqrtfn != NULL_TREE)
7004132727Skan	{
7005132727Skan	  tree rpart, ipart, result, arglist;
7006132727Skan
7007169699Skan	  arg = builtin_save_expr (arg);
7008132727Skan
7009169699Skan	  rpart = fold_build1 (REALPART_EXPR, type, arg);
7010169699Skan	  ipart = fold_build1 (IMAGPART_EXPR, type, arg);
7011132727Skan
7012169699Skan	  rpart = builtin_save_expr (rpart);
7013169699Skan	  ipart = builtin_save_expr (ipart);
7014132727Skan
7015169699Skan	  result = fold_build2 (PLUS_EXPR, type,
7016169699Skan				fold_build2 (MULT_EXPR, type,
7017169699Skan					     rpart, rpart),
7018169699Skan				fold_build2 (MULT_EXPR, type,
7019169699Skan					     ipart, ipart));
7020132727Skan
7021132727Skan	  arglist = build_tree_list (NULL_TREE, result);
7022132727Skan	  return build_function_call_expr (sqrtfn, arglist);
7023132727Skan	}
7024132727Skan    }
7025132727Skan
7026132727Skan  return NULL_TREE;
7027132727Skan}
7028132727Skan
7029169699Skan/* Fold a builtin function call to sqrt, sqrtf, or sqrtl.  Return
7030169699Skan   NULL_TREE if no simplification can be made.  */
7031169699Skan
7032169699Skanstatic tree
7033169699Skanfold_builtin_sqrt (tree arglist, tree type)
7034169699Skan{
7035169699Skan
7036169699Skan  enum built_in_function fcode;
7037169699Skan  tree arg = TREE_VALUE (arglist);
7038169699Skan
7039169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7040169699Skan    return NULL_TREE;
7041169699Skan
7042169699Skan  /* Optimize sqrt of constant value.  */
7043169699Skan  if (TREE_CODE (arg) == REAL_CST
7044169699Skan      && ! TREE_CONSTANT_OVERFLOW (arg))
7045169699Skan    {
7046169699Skan      REAL_VALUE_TYPE r, x;
7047169699Skan
7048169699Skan      x = TREE_REAL_CST (arg);
7049169699Skan      if (real_sqrt (&r, TYPE_MODE (type), &x)
7050169699Skan	  || (!flag_trapping_math && !flag_errno_math))
7051169699Skan	return build_real (type, r);
7052169699Skan    }
7053169699Skan
7054169699Skan  /* Optimize sqrt(expN(x)) = expN(x*0.5).  */
7055169699Skan  fcode = builtin_mathfn_code (arg);
7056169699Skan  if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
7057169699Skan    {
7058169699Skan      tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
7059169699Skan      arg = fold_build2 (MULT_EXPR, type,
7060169699Skan			 TREE_VALUE (TREE_OPERAND (arg, 1)),
7061169699Skan			 build_real (type, dconsthalf));
7062169699Skan      arglist = build_tree_list (NULL_TREE, arg);
7063169699Skan      return build_function_call_expr (expfn, arglist);
7064169699Skan    }
7065169699Skan
7066169699Skan  /* Optimize sqrt(Nroot(x)) -> pow(x,1/(2*N)).  */
7067169699Skan  if (flag_unsafe_math_optimizations && BUILTIN_ROOT_P (fcode))
7068169699Skan    {
7069169699Skan      tree powfn = mathfn_built_in (type, BUILT_IN_POW);
7070169699Skan
7071169699Skan      if (powfn)
7072169699Skan	{
7073169699Skan	  tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
7074169699Skan	  tree tree_root;
7075169699Skan	  /* The inner root was either sqrt or cbrt.  */
7076169699Skan	  REAL_VALUE_TYPE dconstroot =
7077169699Skan	    BUILTIN_SQRT_P (fcode) ? dconsthalf : dconstthird;
7078169699Skan
7079169699Skan	  /* Adjust for the outer root.  */
7080169699Skan	  SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
7081169699Skan	  dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
7082169699Skan	  tree_root = build_real (type, dconstroot);
7083169699Skan	  arglist = tree_cons (NULL_TREE, arg0,
7084169699Skan			       build_tree_list (NULL_TREE, tree_root));
7085169699Skan	  return build_function_call_expr (powfn, arglist);
7086169699Skan	}
7087169699Skan    }
7088169699Skan
7089169699Skan  /* Optimize sqrt(pow(x,y)) = pow(|x|,y*0.5).  */
7090169699Skan  if (flag_unsafe_math_optimizations
7091169699Skan      && (fcode == BUILT_IN_POW
7092169699Skan	  || fcode == BUILT_IN_POWF
7093169699Skan	  || fcode == BUILT_IN_POWL))
7094169699Skan    {
7095169699Skan      tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
7096169699Skan      tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
7097169699Skan      tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
7098169699Skan      tree narg1;
7099169699Skan      if (!tree_expr_nonnegative_p (arg0))
7100169699Skan	arg0 = build1 (ABS_EXPR, type, arg0);
7101169699Skan      narg1 = fold_build2 (MULT_EXPR, type, arg1,
7102169699Skan			   build_real (type, dconsthalf));
7103169699Skan      arglist = tree_cons (NULL_TREE, arg0,
7104169699Skan			   build_tree_list (NULL_TREE, narg1));
7105169699Skan      return build_function_call_expr (powfn, arglist);
7106169699Skan    }
7107169699Skan
7108169699Skan  return NULL_TREE;
7109169699Skan}
7110169699Skan
7111169699Skan/* Fold a builtin function call to cbrt, cbrtf, or cbrtl.  Return
7112169699Skan   NULL_TREE if no simplification can be made.  */
7113169699Skanstatic tree
7114169699Skanfold_builtin_cbrt (tree arglist, tree type)
7115169699Skan{
7116169699Skan  tree arg = TREE_VALUE (arglist);
7117169699Skan  const enum built_in_function fcode = builtin_mathfn_code (arg);
7118169699Skan
7119169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7120169699Skan    return NULL_TREE;
7121169699Skan
7122169699Skan  /* Optimize cbrt of constant value.  */
7123169699Skan  if (real_zerop (arg) || real_onep (arg) || real_minus_onep (arg))
7124169699Skan    return arg;
7125169699Skan
7126169699Skan  if (flag_unsafe_math_optimizations)
7127169699Skan    {
7128169699Skan      /* Optimize cbrt(expN(x)) -> expN(x/3).  */
7129169699Skan      if (BUILTIN_EXPONENT_P (fcode))
7130169699Skan	{
7131169699Skan	  tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
7132169699Skan	  const REAL_VALUE_TYPE third_trunc =
7133169699Skan	    real_value_truncate (TYPE_MODE (type), dconstthird);
7134169699Skan	  arg = fold_build2 (MULT_EXPR, type,
7135169699Skan			     TREE_VALUE (TREE_OPERAND (arg, 1)),
7136169699Skan			     build_real (type, third_trunc));
7137169699Skan	  arglist = build_tree_list (NULL_TREE, arg);
7138169699Skan	  return build_function_call_expr (expfn, arglist);
7139169699Skan	}
7140169699Skan
7141169699Skan      /* Optimize cbrt(sqrt(x)) -> pow(x,1/6).  */
7142169699Skan      if (BUILTIN_SQRT_P (fcode))
7143169699Skan	{
7144169699Skan	  tree powfn = mathfn_built_in (type, BUILT_IN_POW);
7145169699Skan
7146169699Skan	  if (powfn)
7147169699Skan	    {
7148169699Skan	      tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
7149169699Skan	      tree tree_root;
7150169699Skan	      REAL_VALUE_TYPE dconstroot = dconstthird;
7151169699Skan
7152169699Skan	      SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
7153169699Skan	      dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
7154169699Skan	      tree_root = build_real (type, dconstroot);
7155169699Skan	      arglist = tree_cons (NULL_TREE, arg0,
7156169699Skan				   build_tree_list (NULL_TREE, tree_root));
7157169699Skan	      return build_function_call_expr (powfn, arglist);
7158169699Skan	    }
7159169699Skan	}
7160169699Skan
7161169699Skan      /* Optimize cbrt(cbrt(x)) -> pow(x,1/9) iff x is nonnegative.  */
7162169699Skan      if (BUILTIN_CBRT_P (fcode))
7163169699Skan	{
7164169699Skan	  tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
7165169699Skan	  if (tree_expr_nonnegative_p (arg0))
7166169699Skan	    {
7167169699Skan	      tree powfn = mathfn_built_in (type, BUILT_IN_POW);
7168169699Skan
7169169699Skan	      if (powfn)
7170169699Skan		{
7171169699Skan		  tree tree_root;
7172169699Skan		  REAL_VALUE_TYPE dconstroot;
7173169699Skan
7174169699Skan		  real_arithmetic (&dconstroot, MULT_EXPR, &dconstthird, &dconstthird);
7175169699Skan		  dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
7176169699Skan		  tree_root = build_real (type, dconstroot);
7177169699Skan		  arglist = tree_cons (NULL_TREE, arg0,
7178169699Skan				       build_tree_list (NULL_TREE, tree_root));
7179169699Skan		  return build_function_call_expr (powfn, arglist);
7180169699Skan		}
7181169699Skan	    }
7182169699Skan	}
7183169699Skan
7184169699Skan      /* Optimize cbrt(pow(x,y)) -> pow(x,y/3) iff x is nonnegative.  */
7185169699Skan      if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF
7186169699Skan	  || fcode == BUILT_IN_POWL)
7187169699Skan	{
7188169699Skan	  tree arg00 = TREE_VALUE (TREE_OPERAND (arg, 1));
7189169699Skan	  tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
7190169699Skan	  if (tree_expr_nonnegative_p (arg00))
7191169699Skan	    {
7192169699Skan	      tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
7193169699Skan	      const REAL_VALUE_TYPE dconstroot
7194169699Skan		= real_value_truncate (TYPE_MODE (type), dconstthird);
7195169699Skan	      tree narg01 = fold_build2 (MULT_EXPR, type, arg01,
7196169699Skan					 build_real (type, dconstroot));
7197169699Skan	      arglist = tree_cons (NULL_TREE, arg00,
7198169699Skan				   build_tree_list (NULL_TREE, narg01));
7199169699Skan	      return build_function_call_expr (powfn, arglist);
7200169699Skan	    }
7201169699Skan	}
7202169699Skan    }
7203169699Skan  return NULL_TREE;
7204169699Skan}
7205169699Skan
7206169699Skan/* Fold function call to builtin sin, sinf, or sinl.  Return
7207169699Skan   NULL_TREE if no simplification can be made.  */
7208169699Skanstatic tree
7209169699Skanfold_builtin_sin (tree arglist)
7210169699Skan{
7211169699Skan  tree arg = TREE_VALUE (arglist);
7212169699Skan
7213169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7214169699Skan    return NULL_TREE;
7215169699Skan
7216169699Skan  /* Optimize sin (0.0) = 0.0.  */
7217169699Skan  if (real_zerop (arg))
7218169699Skan    return arg;
7219169699Skan
7220169699Skan  return NULL_TREE;
7221169699Skan}
7222169699Skan
7223169699Skan/* Fold function call to builtin cos, cosf, or cosl.  Return
7224169699Skan   NULL_TREE if no simplification can be made.  */
7225169699Skanstatic tree
7226169699Skanfold_builtin_cos (tree arglist, tree type, tree fndecl)
7227169699Skan{
7228169699Skan  tree arg = TREE_VALUE (arglist);
7229169699Skan
7230169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7231169699Skan    return NULL_TREE;
7232169699Skan
7233169699Skan  /* Optimize cos (0.0) = 1.0.  */
7234169699Skan  if (real_zerop (arg))
7235169699Skan    return build_real (type, dconst1);
7236169699Skan
7237169699Skan  /* Optimize cos(-x) into cos (x).  */
7238169699Skan  if (TREE_CODE (arg) == NEGATE_EXPR)
7239169699Skan    {
7240169699Skan      tree args = build_tree_list (NULL_TREE,
7241169699Skan				   TREE_OPERAND (arg, 0));
7242169699Skan      return build_function_call_expr (fndecl, args);
7243169699Skan    }
7244169699Skan
7245169699Skan  return NULL_TREE;
7246169699Skan}
7247169699Skan
7248169699Skan/* Fold function call to builtin tan, tanf, or tanl.  Return
7249169699Skan   NULL_TREE if no simplification can be made.  */
7250169699Skanstatic tree
7251169699Skanfold_builtin_tan (tree arglist)
7252169699Skan{
7253169699Skan  enum built_in_function fcode;
7254169699Skan  tree arg = TREE_VALUE (arglist);
7255169699Skan
7256169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7257169699Skan    return NULL_TREE;
7258169699Skan
7259169699Skan  /* Optimize tan(0.0) = 0.0.  */
7260169699Skan  if (real_zerop (arg))
7261169699Skan    return arg;
7262169699Skan
7263169699Skan  /* Optimize tan(atan(x)) = x.  */
7264169699Skan  fcode = builtin_mathfn_code (arg);
7265169699Skan  if (flag_unsafe_math_optimizations
7266169699Skan      && (fcode == BUILT_IN_ATAN
7267169699Skan	  || fcode == BUILT_IN_ATANF
7268169699Skan	  || fcode == BUILT_IN_ATANL))
7269169699Skan    return TREE_VALUE (TREE_OPERAND (arg, 1));
7270169699Skan
7271169699Skan  return NULL_TREE;
7272169699Skan}
7273169699Skan
7274169699Skan/* Fold function call to builtin atan, atanf, or atanl.  Return
7275169699Skan   NULL_TREE if no simplification can be made.  */
7276169699Skan
7277169699Skanstatic tree
7278169699Skanfold_builtin_atan (tree arglist, tree type)
7279169699Skan{
7280169699Skan
7281169699Skan  tree arg = TREE_VALUE (arglist);
7282169699Skan
7283169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7284169699Skan    return NULL_TREE;
7285169699Skan
7286169699Skan  /* Optimize atan(0.0) = 0.0.  */
7287169699Skan  if (real_zerop (arg))
7288169699Skan    return arg;
7289169699Skan
7290169699Skan  /* Optimize atan(1.0) = pi/4.  */
7291169699Skan  if (real_onep (arg))
7292169699Skan    {
7293169699Skan      REAL_VALUE_TYPE cst;
7294169699Skan
7295169699Skan      real_convert (&cst, TYPE_MODE (type), &dconstpi);
7296169699Skan      SET_REAL_EXP (&cst, REAL_EXP (&cst) - 2);
7297169699Skan      return build_real (type, cst);
7298169699Skan    }
7299169699Skan
7300169699Skan  return NULL_TREE;
7301169699Skan}
7302169699Skan
7303132727Skan/* Fold function call to builtin trunc, truncf or truncl.  Return
7304132727Skan   NULL_TREE if no simplification can be made.  */
7305132727Skan
7306132727Skanstatic tree
7307169699Skanfold_builtin_trunc (tree fndecl, tree arglist)
7308132727Skan{
7309132727Skan  tree arg;
7310132727Skan
7311132727Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7312132727Skan    return 0;
7313132727Skan
7314132727Skan  /* Optimize trunc of constant value.  */
7315132727Skan  arg = TREE_VALUE (arglist);
7316132727Skan  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7317132727Skan    {
7318132727Skan      REAL_VALUE_TYPE r, x;
7319169699Skan      tree type = TREE_TYPE (TREE_TYPE (fndecl));
7320132727Skan
7321132727Skan      x = TREE_REAL_CST (arg);
7322132727Skan      real_trunc (&r, TYPE_MODE (type), &x);
7323132727Skan      return build_real (type, r);
7324132727Skan    }
7325132727Skan
7326169699Skan  return fold_trunc_transparent_mathfn (fndecl, arglist);
7327132727Skan}
7328132727Skan
7329132727Skan/* Fold function call to builtin floor, floorf or floorl.  Return
7330132727Skan   NULL_TREE if no simplification can be made.  */
7331132727Skan
7332132727Skanstatic tree
7333169699Skanfold_builtin_floor (tree fndecl, tree arglist)
7334132727Skan{
7335132727Skan  tree arg;
7336132727Skan
7337132727Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7338132727Skan    return 0;
7339132727Skan
7340132727Skan  /* Optimize floor of constant value.  */
7341132727Skan  arg = TREE_VALUE (arglist);
7342132727Skan  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7343132727Skan    {
7344132727Skan      REAL_VALUE_TYPE x;
7345132727Skan
7346132727Skan      x = TREE_REAL_CST (arg);
7347132727Skan      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
7348132727Skan	{
7349169699Skan	  tree type = TREE_TYPE (TREE_TYPE (fndecl));
7350132727Skan	  REAL_VALUE_TYPE r;
7351132727Skan
7352132727Skan	  real_floor (&r, TYPE_MODE (type), &x);
7353132727Skan	  return build_real (type, r);
7354132727Skan	}
7355132727Skan    }
7356132727Skan
7357169699Skan  return fold_trunc_transparent_mathfn (fndecl, arglist);
7358132727Skan}
7359132727Skan
7360132727Skan/* Fold function call to builtin ceil, ceilf or ceill.  Return
7361132727Skan   NULL_TREE if no simplification can be made.  */
7362132727Skan
7363132727Skanstatic tree
7364169699Skanfold_builtin_ceil (tree fndecl, tree arglist)
7365132727Skan{
7366132727Skan  tree arg;
7367132727Skan
7368132727Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7369132727Skan    return 0;
7370132727Skan
7371132727Skan  /* Optimize ceil of constant value.  */
7372132727Skan  arg = TREE_VALUE (arglist);
7373132727Skan  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7374132727Skan    {
7375132727Skan      REAL_VALUE_TYPE x;
7376132727Skan
7377132727Skan      x = TREE_REAL_CST (arg);
7378132727Skan      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
7379132727Skan	{
7380169699Skan	  tree type = TREE_TYPE (TREE_TYPE (fndecl));
7381132727Skan	  REAL_VALUE_TYPE r;
7382132727Skan
7383132727Skan	  real_ceil (&r, TYPE_MODE (type), &x);
7384132727Skan	  return build_real (type, r);
7385132727Skan	}
7386132727Skan    }
7387132727Skan
7388169699Skan  return fold_trunc_transparent_mathfn (fndecl, arglist);
7389132727Skan}
7390132727Skan
7391169699Skan/* Fold function call to builtin round, roundf or roundl.  Return
7392169699Skan   NULL_TREE if no simplification can be made.  */
7393169699Skan
7394169699Skanstatic tree
7395169699Skanfold_builtin_round (tree fndecl, tree arglist)
7396169699Skan{
7397169699Skan  tree arg;
7398169699Skan
7399169699Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7400169699Skan    return 0;
7401169699Skan
7402169699Skan  /* Optimize round of constant value.  */
7403169699Skan  arg = TREE_VALUE (arglist);
7404169699Skan  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7405169699Skan    {
7406169699Skan      REAL_VALUE_TYPE x;
7407169699Skan
7408169699Skan      x = TREE_REAL_CST (arg);
7409169699Skan      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
7410169699Skan	{
7411169699Skan	  tree type = TREE_TYPE (TREE_TYPE (fndecl));
7412169699Skan	  REAL_VALUE_TYPE r;
7413169699Skan
7414169699Skan	  real_round (&r, TYPE_MODE (type), &x);
7415169699Skan	  return build_real (type, r);
7416169699Skan	}
7417169699Skan    }
7418169699Skan
7419169699Skan  return fold_trunc_transparent_mathfn (fndecl, arglist);
7420169699Skan}
7421169699Skan
7422169699Skan/* Fold function call to builtin lround, lroundf or lroundl (or the
7423169699Skan   corresponding long long versions) and other rounding functions.
7424169699Skan   Return NULL_TREE if no simplification can be made.  */
7425169699Skan
7426169699Skanstatic tree
7427169699Skanfold_builtin_int_roundingfn (tree fndecl, tree arglist)
7428169699Skan{
7429169699Skan  tree arg;
7430169699Skan
7431169699Skan  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7432169699Skan    return 0;
7433169699Skan
7434169699Skan  /* Optimize lround of constant value.  */
7435169699Skan  arg = TREE_VALUE (arglist);
7436169699Skan  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7437169699Skan    {
7438169699Skan      const REAL_VALUE_TYPE x = TREE_REAL_CST (arg);
7439169699Skan
7440169699Skan      if (! REAL_VALUE_ISNAN (x) && ! REAL_VALUE_ISINF (x))
7441169699Skan	{
7442169699Skan	  tree itype = TREE_TYPE (TREE_TYPE (fndecl));
7443169699Skan	  tree ftype = TREE_TYPE (arg), result;
7444169699Skan	  HOST_WIDE_INT hi, lo;
7445169699Skan	  REAL_VALUE_TYPE r;
7446169699Skan
7447169699Skan	  switch (DECL_FUNCTION_CODE (fndecl))
7448169699Skan	    {
7449169699Skan	    CASE_FLT_FN (BUILT_IN_LFLOOR):
7450169699Skan	    CASE_FLT_FN (BUILT_IN_LLFLOOR):
7451169699Skan	      real_floor (&r, TYPE_MODE (ftype), &x);
7452169699Skan	      break;
7453169699Skan
7454169699Skan	    CASE_FLT_FN (BUILT_IN_LCEIL):
7455169699Skan	    CASE_FLT_FN (BUILT_IN_LLCEIL):
7456169699Skan	      real_ceil (&r, TYPE_MODE (ftype), &x);
7457169699Skan	      break;
7458169699Skan
7459169699Skan	    CASE_FLT_FN (BUILT_IN_LROUND):
7460169699Skan	    CASE_FLT_FN (BUILT_IN_LLROUND):
7461169699Skan	      real_round (&r, TYPE_MODE (ftype), &x);
7462169699Skan	      break;
7463169699Skan
7464169699Skan	    default:
7465169699Skan	      gcc_unreachable ();
7466169699Skan	    }
7467169699Skan
7468169699Skan	  REAL_VALUE_TO_INT (&lo, &hi, r);
7469169699Skan	  result = build_int_cst_wide (NULL_TREE, lo, hi);
7470169699Skan	  if (int_fits_type_p (result, itype))
7471169699Skan	    return fold_convert (itype, result);
7472169699Skan	}
7473169699Skan    }
7474169699Skan
7475169699Skan  return fold_fixed_mathfn (fndecl, arglist);
7476169699Skan}
7477169699Skan
7478132727Skan/* Fold function call to builtin ffs, clz, ctz, popcount and parity
7479132727Skan   and their long and long long variants (i.e. ffsl and ffsll).
7480132727Skan   Return NULL_TREE if no simplification can be made.  */
7481132727Skan
7482132727Skanstatic tree
7483169699Skanfold_builtin_bitop (tree fndecl, tree arglist)
7484132727Skan{
7485132727Skan  tree arg;
7486132727Skan
7487132727Skan  if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
7488132727Skan    return NULL_TREE;
7489132727Skan
7490132727Skan  /* Optimize for constant argument.  */
7491132727Skan  arg = TREE_VALUE (arglist);
7492132727Skan  if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7493132727Skan    {
7494132727Skan      HOST_WIDE_INT hi, width, result;
7495132727Skan      unsigned HOST_WIDE_INT lo;
7496169699Skan      tree type;
7497132727Skan
7498132727Skan      type = TREE_TYPE (arg);
7499132727Skan      width = TYPE_PRECISION (type);
7500132727Skan      lo = TREE_INT_CST_LOW (arg);
7501132727Skan
7502132727Skan      /* Clear all the bits that are beyond the type's precision.  */
7503132727Skan      if (width > HOST_BITS_PER_WIDE_INT)
7504132727Skan	{
7505132727Skan	  hi = TREE_INT_CST_HIGH (arg);
7506132727Skan	  if (width < 2 * HOST_BITS_PER_WIDE_INT)
7507132727Skan	    hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT));
7508132727Skan	}
7509132727Skan      else
7510132727Skan	{
7511132727Skan	  hi = 0;
7512132727Skan	  if (width < HOST_BITS_PER_WIDE_INT)
7513132727Skan	    lo &= ~((unsigned HOST_WIDE_INT) (-1) << width);
7514132727Skan	}
7515132727Skan
7516132727Skan      switch (DECL_FUNCTION_CODE (fndecl))
7517132727Skan	{
7518169699Skan	CASE_INT_FN (BUILT_IN_FFS):
7519132727Skan	  if (lo != 0)
7520132727Skan	    result = exact_log2 (lo & -lo) + 1;
7521132727Skan	  else if (hi != 0)
7522132727Skan	    result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1;
7523132727Skan	  else
7524132727Skan	    result = 0;
7525132727Skan	  break;
7526132727Skan
7527169699Skan	CASE_INT_FN (BUILT_IN_CLZ):
7528132727Skan	  if (hi != 0)
7529132727Skan	    result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT;
7530132727Skan	  else if (lo != 0)
7531132727Skan	    result = width - floor_log2 (lo) - 1;
7532132727Skan	  else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
7533132727Skan	    result = width;
7534132727Skan	  break;
7535132727Skan
7536169699Skan	CASE_INT_FN (BUILT_IN_CTZ):
7537132727Skan	  if (lo != 0)
7538132727Skan	    result = exact_log2 (lo & -lo);
7539132727Skan	  else if (hi != 0)
7540132727Skan	    result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi);
7541132727Skan	  else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
7542132727Skan	    result = width;
7543132727Skan	  break;
7544132727Skan
7545169699Skan	CASE_INT_FN (BUILT_IN_POPCOUNT):
7546132727Skan	  result = 0;
7547132727Skan	  while (lo)
7548132727Skan	    result++, lo &= lo - 1;
7549132727Skan	  while (hi)
7550132727Skan	    result++, hi &= hi - 1;
7551132727Skan	  break;
7552132727Skan
7553169699Skan	CASE_INT_FN (BUILT_IN_PARITY):
7554132727Skan	  result = 0;
7555132727Skan	  while (lo)
7556132727Skan	    result++, lo &= lo - 1;
7557132727Skan	  while (hi)
7558132727Skan	    result++, hi &= hi - 1;
7559132727Skan	  result &= 1;
7560132727Skan	  break;
7561132727Skan
7562132727Skan	default:
7563169699Skan	  gcc_unreachable ();
7564132727Skan	}
7565132727Skan
7566169699Skan      return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result);
7567132727Skan    }
7568132727Skan
7569132727Skan  return NULL_TREE;
7570132727Skan}
7571132727Skan
7572259563Spfg/* Fold function call to builtin_bswap and the long and long long
7573259563Spfg   variants.  Return NULL_TREE if no simplification can be made.  */
7574259563Spfgstatic tree
7575259563Spfgfold_builtin_bswap (tree fndecl, tree arglist)
7576259563Spfg{
7577259563Spfg  tree arg;
7578259563Spfg
7579259563Spfg  if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
7580259563Spfg    return 0;
7581259563Spfg
7582259563Spfg  /* Optimize constant value.  */
7583259563Spfg  arg = TREE_VALUE (arglist);
7584259563Spfg  if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg))
7585259563Spfg    {
7586259563Spfg      HOST_WIDE_INT hi, width, r_hi = 0;
7587259563Spfg      unsigned HOST_WIDE_INT lo, r_lo = 0;
7588259563Spfg      tree type;
7589259563Spfg
7590259563Spfg      type = TREE_TYPE (arg);
7591259563Spfg      width = TYPE_PRECISION (type);
7592259563Spfg      lo = TREE_INT_CST_LOW (arg);
7593259563Spfg      hi = TREE_INT_CST_HIGH (arg);
7594259563Spfg
7595259563Spfg      switch (DECL_FUNCTION_CODE (fndecl))
7596259563Spfg	{
7597259563Spfg	  case BUILT_IN_BSWAP32:
7598259563Spfg	  case BUILT_IN_BSWAP64:
7599259563Spfg	    {
7600259563Spfg	      int s;
7601259563Spfg
7602259563Spfg	      for (s = 0; s < width; s += 8)
7603259563Spfg		{
7604259563Spfg		  int d = width - s - 8;
7605259563Spfg		  unsigned HOST_WIDE_INT byte;
7606259563Spfg
7607259563Spfg		  if (s < HOST_BITS_PER_WIDE_INT)
7608259563Spfg		    byte = (lo >> s) & 0xff;
7609259563Spfg		  else
7610259563Spfg		    byte = (hi >> (s - HOST_BITS_PER_WIDE_INT)) & 0xff;
7611259563Spfg
7612259563Spfg		  if (d < HOST_BITS_PER_WIDE_INT)
7613259563Spfg		    r_lo |= byte << d;
7614259563Spfg		  else
7615259563Spfg		    r_hi |= byte << (d - HOST_BITS_PER_WIDE_INT);
7616259563Spfg		}
7617259563Spfg	    }
7618259563Spfg
7619259563Spfg	    break;
7620259563Spfg
7621259563Spfg	default:
7622259563Spfg	  gcc_unreachable ();
7623259563Spfg	}
7624259563Spfg
7625259563Spfg      if (width < HOST_BITS_PER_WIDE_INT)
7626259563Spfg	return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), r_lo);
7627259563Spfg      else
7628259563Spfg	return build_int_cst_wide (TREE_TYPE (TREE_TYPE (fndecl)), r_lo, r_hi);
7629259563Spfg    }
7630259563Spfg
7631259563Spfg  return NULL_TREE;
7632259563Spfg}
7633132727Skan/* Return true if EXPR is the real constant contained in VALUE.  */
7634132727Skan
7635132727Skanstatic bool
7636132727Skanreal_dconstp (tree expr, const REAL_VALUE_TYPE *value)
7637132727Skan{
7638132727Skan  STRIP_NOPS (expr);
7639132727Skan
7640132727Skan  return ((TREE_CODE (expr) == REAL_CST
7641169699Skan	   && ! TREE_CONSTANT_OVERFLOW (expr)
7642169699Skan	   && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value))
7643169699Skan	  || (TREE_CODE (expr) == COMPLEX_CST
7644169699Skan	      && real_dconstp (TREE_REALPART (expr), value)
7645169699Skan	      && real_zerop (TREE_IMAGPART (expr))));
7646132727Skan}
7647132727Skan
7648132727Skan/* A subroutine of fold_builtin to fold the various logarithmic
7649169699Skan   functions.  EXP is the CALL_EXPR of a call to a builtin logN
7650169699Skan   function.  VALUE is the base of the logN function.  */
7651132727Skan
7652132727Skanstatic tree
7653169699Skanfold_builtin_logarithm (tree fndecl, tree arglist,
7654169699Skan			const REAL_VALUE_TYPE *value)
7655132727Skan{
7656132727Skan  if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7657132727Skan    {
7658132727Skan      tree type = TREE_TYPE (TREE_TYPE (fndecl));
7659132727Skan      tree arg = TREE_VALUE (arglist);
7660132727Skan      const enum built_in_function fcode = builtin_mathfn_code (arg);
7661169699Skan
7662169699Skan      /* Optimize logN(1.0) = 0.0.  */
7663132727Skan      if (real_onep (arg))
7664132727Skan	return build_real (type, dconst0);
7665132727Skan
7666132727Skan      /* Optimize logN(N) = 1.0.  If N can't be truncated to MODE
7667169699Skan	 exactly, then only do this if flag_unsafe_math_optimizations.  */
7668132727Skan      if (exact_real_truncate (TYPE_MODE (type), value)
7669132727Skan	  || flag_unsafe_math_optimizations)
7670169699Skan	{
7671132727Skan	  const REAL_VALUE_TYPE value_truncate =
7672132727Skan	    real_value_truncate (TYPE_MODE (type), *value);
7673132727Skan	  if (real_dconstp (arg, &value_truncate))
7674132727Skan	    return build_real (type, dconst1);
7675132727Skan	}
7676169699Skan
7677132727Skan      /* Special case, optimize logN(expN(x)) = x.  */
7678132727Skan      if (flag_unsafe_math_optimizations
7679132727Skan	  && ((value == &dconste
7680132727Skan	       && (fcode == BUILT_IN_EXP
7681132727Skan		   || fcode == BUILT_IN_EXPF
7682132727Skan		   || fcode == BUILT_IN_EXPL))
7683132727Skan	      || (value == &dconst2
7684132727Skan		  && (fcode == BUILT_IN_EXP2
7685132727Skan		      || fcode == BUILT_IN_EXP2F
7686132727Skan		      || fcode == BUILT_IN_EXP2L))
7687169699Skan	      || (value == &dconst10 && (BUILTIN_EXP10_P (fcode)))))
7688169699Skan	return fold_convert (type, TREE_VALUE (TREE_OPERAND (arg, 1)));
7689132727Skan
7690169699Skan      /* Optimize logN(func()) for various exponential functions.  We
7691169699Skan	 want to determine the value "x" and the power "exponent" in
7692169699Skan	 order to transform logN(x**exponent) into exponent*logN(x).  */
7693132727Skan      if (flag_unsafe_math_optimizations)
7694169699Skan	{
7695132727Skan	  tree exponent = 0, x = 0;
7696169699Skan
7697132727Skan	  switch (fcode)
7698132727Skan	  {
7699169699Skan	  CASE_FLT_FN (BUILT_IN_EXP):
7700132727Skan	    /* Prepare to do logN(exp(exponent) -> exponent*logN(e).  */
7701132727Skan	    x = build_real (type,
7702132727Skan			    real_value_truncate (TYPE_MODE (type), dconste));
7703132727Skan	    exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
7704132727Skan	    break;
7705169699Skan	  CASE_FLT_FN (BUILT_IN_EXP2):
7706132727Skan	    /* Prepare to do logN(exp2(exponent) -> exponent*logN(2).  */
7707132727Skan	    x = build_real (type, dconst2);
7708132727Skan	    exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
7709132727Skan	    break;
7710169699Skan	  CASE_FLT_FN (BUILT_IN_EXP10):
7711169699Skan	  CASE_FLT_FN (BUILT_IN_POW10):
7712132727Skan	    /* Prepare to do logN(exp10(exponent) -> exponent*logN(10).  */
7713132727Skan	    x = build_real (type, dconst10);
7714132727Skan	    exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
7715132727Skan	    break;
7716169699Skan	  CASE_FLT_FN (BUILT_IN_SQRT):
7717132727Skan	    /* Prepare to do logN(sqrt(x) -> 0.5*logN(x).  */
7718132727Skan	    x = TREE_VALUE (TREE_OPERAND (arg, 1));
7719132727Skan	    exponent = build_real (type, dconsthalf);
7720132727Skan	    break;
7721169699Skan	  CASE_FLT_FN (BUILT_IN_CBRT):
7722132727Skan	    /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x).  */
7723132727Skan	    x = TREE_VALUE (TREE_OPERAND (arg, 1));
7724132727Skan	    exponent = build_real (type, real_value_truncate (TYPE_MODE (type),
7725132727Skan							      dconstthird));
7726132727Skan	    break;
7727169699Skan	  CASE_FLT_FN (BUILT_IN_POW):
7728132727Skan	    /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x).  */
7729132727Skan	    x = TREE_VALUE (TREE_OPERAND (arg, 1));
7730132727Skan	    exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
7731132727Skan	    break;
7732132727Skan	  default:
7733132727Skan	    break;
7734132727Skan	  }
7735132727Skan
7736132727Skan	  /* Now perform the optimization.  */
7737132727Skan	  if (x && exponent)
7738132727Skan	    {
7739132727Skan	      tree logfn;
7740132727Skan	      arglist = build_tree_list (NULL_TREE, x);
7741132727Skan	      logfn = build_function_call_expr (fndecl, arglist);
7742169699Skan	      return fold_build2 (MULT_EXPR, type, exponent, logfn);
7743132727Skan	    }
7744132727Skan	}
7745132727Skan    }
7746132727Skan
7747132727Skan  return 0;
7748132727Skan}
7749169699Skan
7750169699Skan/* Fold a builtin function call to pow, powf, or powl.  Return
7751169699Skan   NULL_TREE if no simplification can be made.  */
7752169699Skanstatic tree
7753169699Skanfold_builtin_pow (tree fndecl, tree arglist, tree type)
7754169699Skan{
7755169699Skan  tree arg0 = TREE_VALUE (arglist);
7756169699Skan  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
7757169699Skan
7758169699Skan  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
7759169699Skan    return NULL_TREE;
7760169699Skan
7761169699Skan  /* Optimize pow(1.0,y) = 1.0.  */
7762169699Skan  if (real_onep (arg0))
7763169699Skan    return omit_one_operand (type, build_real (type, dconst1), arg1);
7764169699Skan
7765169699Skan  if (TREE_CODE (arg1) == REAL_CST
7766169699Skan      && ! TREE_CONSTANT_OVERFLOW (arg1))
7767169699Skan    {
7768169699Skan      REAL_VALUE_TYPE cint;
7769169699Skan      REAL_VALUE_TYPE c;
7770169699Skan      HOST_WIDE_INT n;
7771169699Skan
7772169699Skan      c = TREE_REAL_CST (arg1);
7773169699Skan
7774169699Skan      /* Optimize pow(x,0.0) = 1.0.  */
7775169699Skan      if (REAL_VALUES_EQUAL (c, dconst0))
7776169699Skan	return omit_one_operand (type, build_real (type, dconst1),
7777169699Skan				 arg0);
7778169699Skan
7779169699Skan      /* Optimize pow(x,1.0) = x.  */
7780169699Skan      if (REAL_VALUES_EQUAL (c, dconst1))
7781169699Skan	return arg0;
7782169699Skan
7783169699Skan      /* Optimize pow(x,-1.0) = 1.0/x.  */
7784169699Skan      if (REAL_VALUES_EQUAL (c, dconstm1))
7785169699Skan	return fold_build2 (RDIV_EXPR, type,
7786169699Skan			    build_real (type, dconst1), arg0);
7787169699Skan
7788169699Skan      /* Optimize pow(x,0.5) = sqrt(x).  */
7789169699Skan      if (flag_unsafe_math_optimizations
7790169699Skan	  && REAL_VALUES_EQUAL (c, dconsthalf))
7791169699Skan	{
7792169699Skan	  tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
7793169699Skan
7794169699Skan	  if (sqrtfn != NULL_TREE)
7795169699Skan	    {
7796169699Skan	      tree arglist = build_tree_list (NULL_TREE, arg0);
7797169699Skan	      return build_function_call_expr (sqrtfn, arglist);
7798169699Skan	    }
7799169699Skan	}
7800169699Skan
7801169699Skan      /* Check for an integer exponent.  */
7802169699Skan      n = real_to_integer (&c);
7803169699Skan      real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
7804169699Skan      if (real_identical (&c, &cint))
7805169699Skan	{
7806169699Skan	  /* Attempt to evaluate pow at compile-time.  */
7807169699Skan	  if (TREE_CODE (arg0) == REAL_CST
7808169699Skan	      && ! TREE_CONSTANT_OVERFLOW (arg0))
7809169699Skan	    {
7810169699Skan	      REAL_VALUE_TYPE x;
7811169699Skan	      bool inexact;
7812169699Skan
7813169699Skan	      x = TREE_REAL_CST (arg0);
7814169699Skan	      inexact = real_powi (&x, TYPE_MODE (type), &x, n);
7815169699Skan	      if (flag_unsafe_math_optimizations || !inexact)
7816169699Skan		return build_real (type, x);
7817169699Skan	    }
7818169699Skan
7819169699Skan	  /* Strip sign ops from even integer powers.  */
7820169699Skan	  if ((n & 1) == 0 && flag_unsafe_math_optimizations)
7821169699Skan	    {
7822169699Skan	      tree narg0 = fold_strip_sign_ops (arg0);
7823169699Skan	      if (narg0)
7824169699Skan		{
7825169699Skan		  arglist = build_tree_list (NULL_TREE, arg1);
7826169699Skan		  arglist = tree_cons (NULL_TREE, narg0, arglist);
7827169699Skan		  return build_function_call_expr (fndecl, arglist);
7828169699Skan		}
7829169699Skan	    }
7830169699Skan	}
7831169699Skan    }
7832169699Skan
7833169699Skan  if (flag_unsafe_math_optimizations)
7834169699Skan    {
7835169699Skan      const enum built_in_function fcode = builtin_mathfn_code (arg0);
7836169699Skan
7837169699Skan      /* Optimize pow(expN(x),y) = expN(x*y).  */
7838169699Skan      if (BUILTIN_EXPONENT_P (fcode))
7839169699Skan	{
7840169699Skan	  tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
7841169699Skan	  tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
7842169699Skan	  arg = fold_build2 (MULT_EXPR, type, arg, arg1);
7843169699Skan	  arglist = build_tree_list (NULL_TREE, arg);
7844169699Skan	  return build_function_call_expr (expfn, arglist);
7845169699Skan	}
7846169699Skan
7847169699Skan      /* Optimize pow(sqrt(x),y) = pow(x,y*0.5).  */
7848169699Skan      if (BUILTIN_SQRT_P (fcode))
7849169699Skan	{
7850169699Skan	  tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
7851169699Skan	  tree narg1 = fold_build2 (MULT_EXPR, type, arg1,
7852169699Skan				    build_real (type, dconsthalf));
7853169699Skan
7854169699Skan	  arglist = tree_cons (NULL_TREE, narg0,
7855169699Skan			       build_tree_list (NULL_TREE, narg1));
7856169699Skan	  return build_function_call_expr (fndecl, arglist);
7857169699Skan	}
7858169699Skan
7859169699Skan      /* Optimize pow(cbrt(x),y) = pow(x,y/3) iff x is nonnegative.  */
7860169699Skan      if (BUILTIN_CBRT_P (fcode))
7861169699Skan	{
7862169699Skan	  tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
7863169699Skan	  if (tree_expr_nonnegative_p (arg))
7864169699Skan	    {
7865169699Skan	      const REAL_VALUE_TYPE dconstroot
7866169699Skan		= real_value_truncate (TYPE_MODE (type), dconstthird);
7867169699Skan	      tree narg1 = fold_build2 (MULT_EXPR, type, arg1,
7868169699Skan					build_real (type, dconstroot));
7869169699Skan	      arglist = tree_cons (NULL_TREE, arg,
7870169699Skan				   build_tree_list (NULL_TREE, narg1));
7871169699Skan	      return build_function_call_expr (fndecl, arglist);
7872169699Skan	    }
7873169699Skan	}
7874169699Skan
7875169699Skan      /* Optimize pow(pow(x,y),z) = pow(x,y*z).  */
7876169699Skan      if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF
7877169699Skan	   || fcode == BUILT_IN_POWL)
7878169699Skan	{
7879169699Skan	  tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1));
7880169699Skan	  tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1)));
7881169699Skan	  tree narg1 = fold_build2 (MULT_EXPR, type, arg01, arg1);
7882169699Skan	  arglist = tree_cons (NULL_TREE, arg00,
7883169699Skan			       build_tree_list (NULL_TREE, narg1));
7884169699Skan	  return build_function_call_expr (fndecl, arglist);
7885169699Skan	}
7886169699Skan    }
7887169699Skan
7888169699Skan  return NULL_TREE;
7889169699Skan}
7890169699Skan
7891169699Skan/* Fold a builtin function call to powi, powif, or powil.  Return
7892169699Skan   NULL_TREE if no simplification can be made.  */
7893169699Skanstatic tree
7894169699Skanfold_builtin_powi (tree fndecl ATTRIBUTE_UNUSED, tree arglist, tree type)
7895169699Skan{
7896169699Skan  tree arg0 = TREE_VALUE (arglist);
7897169699Skan  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
7898169699Skan
7899169699Skan  if (!validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE))
7900169699Skan    return NULL_TREE;
7901169699Skan
7902169699Skan  /* Optimize pow(1.0,y) = 1.0.  */
7903169699Skan  if (real_onep (arg0))
7904169699Skan    return omit_one_operand (type, build_real (type, dconst1), arg1);
7905169699Skan
7906169699Skan  if (host_integerp (arg1, 0))
7907169699Skan    {
7908169699Skan      HOST_WIDE_INT c = TREE_INT_CST_LOW (arg1);
7909169699Skan
7910169699Skan      /* Evaluate powi at compile-time.  */
7911169699Skan      if (TREE_CODE (arg0) == REAL_CST
7912169699Skan	  && ! TREE_CONSTANT_OVERFLOW (arg0))
7913169699Skan	{
7914169699Skan	  REAL_VALUE_TYPE x;
7915169699Skan	  x = TREE_REAL_CST (arg0);
7916169699Skan	  real_powi (&x, TYPE_MODE (type), &x, c);
7917169699Skan	  return build_real (type, x);
7918169699Skan	}
7919169699Skan
7920169699Skan      /* Optimize pow(x,0) = 1.0.  */
7921169699Skan      if (c == 0)
7922169699Skan	return omit_one_operand (type, build_real (type, dconst1),
7923169699Skan				 arg0);
7924169699Skan
7925169699Skan      /* Optimize pow(x,1) = x.  */
7926169699Skan      if (c == 1)
7927169699Skan	return arg0;
7928169699Skan
7929169699Skan      /* Optimize pow(x,-1) = 1.0/x.  */
7930169699Skan      if (c == -1)
7931169699Skan	return fold_build2 (RDIV_EXPR, type,
7932169699Skan			   build_real (type, dconst1), arg0);
7933169699Skan    }
7934169699Skan
7935169699Skan  return NULL_TREE;
7936169699Skan}
7937169699Skan
7938132727Skan/* A subroutine of fold_builtin to fold the various exponent
7939132727Skan   functions.  EXP is the CALL_EXPR of a call to a builtin function.
7940132727Skan   VALUE is the value which will be raised to a power.  */
7941132727Skan
7942132727Skanstatic tree
7943169699Skanfold_builtin_exponent (tree fndecl, tree arglist,
7944169699Skan		       const REAL_VALUE_TYPE *value)
7945132727Skan{
7946132727Skan  if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
7947132727Skan    {
7948132727Skan      tree type = TREE_TYPE (TREE_TYPE (fndecl));
7949132727Skan      tree arg = TREE_VALUE (arglist);
7950132727Skan
7951132727Skan      /* Optimize exp*(0.0) = 1.0.  */
7952132727Skan      if (real_zerop (arg))
7953132727Skan	return build_real (type, dconst1);
7954132727Skan
7955132727Skan      /* Optimize expN(1.0) = N.  */
7956132727Skan      if (real_onep (arg))
7957169699Skan	{
7958132727Skan	  REAL_VALUE_TYPE cst;
7959132727Skan
7960132727Skan	  real_convert (&cst, TYPE_MODE (type), value);
7961132727Skan	  return build_real (type, cst);
7962132727Skan	}
7963132727Skan
7964132727Skan      /* Attempt to evaluate expN(integer) at compile-time.  */
7965132727Skan      if (flag_unsafe_math_optimizations
7966132727Skan	  && TREE_CODE (arg) == REAL_CST
7967132727Skan	  && ! TREE_CONSTANT_OVERFLOW (arg))
7968169699Skan	{
7969132727Skan	  REAL_VALUE_TYPE cint;
7970132727Skan	  REAL_VALUE_TYPE c;
7971132727Skan	  HOST_WIDE_INT n;
7972132727Skan
7973132727Skan	  c = TREE_REAL_CST (arg);
7974132727Skan	  n = real_to_integer (&c);
7975132727Skan	  real_from_integer (&cint, VOIDmode, n,
7976132727Skan			     n < 0 ? -1 : 0, 0);
7977132727Skan	  if (real_identical (&c, &cint))
7978132727Skan	    {
7979132727Skan	      REAL_VALUE_TYPE x;
7980132727Skan
7981132727Skan	      real_powi (&x, TYPE_MODE (type), value, n);
7982132727Skan	      return build_real (type, x);
7983132727Skan	    }
7984132727Skan	}
7985132727Skan
7986132727Skan      /* Optimize expN(logN(x)) = x.  */
7987132727Skan      if (flag_unsafe_math_optimizations)
7988169699Skan	{
7989132727Skan	  const enum built_in_function fcode = builtin_mathfn_code (arg);
7990132727Skan
7991132727Skan	  if ((value == &dconste
7992132727Skan	       && (fcode == BUILT_IN_LOG
7993132727Skan		   || fcode == BUILT_IN_LOGF
7994132727Skan		   || fcode == BUILT_IN_LOGL))
7995132727Skan	      || (value == &dconst2
7996132727Skan		  && (fcode == BUILT_IN_LOG2
7997132727Skan		      || fcode == BUILT_IN_LOG2F
7998132727Skan		      || fcode == BUILT_IN_LOG2L))
7999132727Skan	      || (value == &dconst10
8000132727Skan		  && (fcode == BUILT_IN_LOG10
8001132727Skan		      || fcode == BUILT_IN_LOG10F
8002132727Skan		      || fcode == BUILT_IN_LOG10L)))
8003169699Skan	    return fold_convert (type, TREE_VALUE (TREE_OPERAND (arg, 1)));
8004132727Skan	}
8005132727Skan    }
8006132727Skan
8007132727Skan  return 0;
8008132727Skan}
8009132727Skan
8010169699Skan/* Return true if VAR is a VAR_DECL or a component thereof.  */
8011169699Skan
8012169699Skanstatic bool
8013169699Skanvar_decl_component_p (tree var)
8014169699Skan{
8015169699Skan  tree inner = var;
8016169699Skan  while (handled_component_p (inner))
8017169699Skan    inner = TREE_OPERAND (inner, 0);
8018169699Skan  return SSA_VAR_P (inner);
8019169699Skan}
8020169699Skan
8021169699Skan/* Fold function call to builtin memset.  Return
8022132727Skan   NULL_TREE if no simplification can be made.  */
8023132727Skan
8024132727Skanstatic tree
8025169699Skanfold_builtin_memset (tree arglist, tree type, bool ignore)
8026132727Skan{
8027169699Skan  tree dest, c, len, var, ret;
8028169699Skan  unsigned HOST_WIDE_INT length, cval;
8029132727Skan
8030132727Skan  if (!validate_arglist (arglist,
8031169699Skan			 POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
8032132727Skan    return 0;
8033132727Skan
8034132727Skan  dest = TREE_VALUE (arglist);
8035169699Skan  c = TREE_VALUE (TREE_CHAIN (arglist));
8036132727Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
8037132727Skan
8038169699Skan  if (! host_integerp (len, 1))
8039169699Skan    return 0;
8040169699Skan
8041132727Skan  /* If the LEN parameter is zero, return DEST.  */
8042132727Skan  if (integer_zerop (len))
8043169699Skan    return omit_one_operand (type, dest, c);
8044132727Skan
8045169699Skan  if (! host_integerp (c, 1) || TREE_SIDE_EFFECTS (dest))
8046169699Skan    return 0;
8047132727Skan
8048169699Skan  var = dest;
8049169699Skan  STRIP_NOPS (var);
8050169699Skan  if (TREE_CODE (var) != ADDR_EXPR)
8051169699Skan    return 0;
8052169699Skan
8053169699Skan  var = TREE_OPERAND (var, 0);
8054169699Skan  if (TREE_THIS_VOLATILE (var))
8055169699Skan    return 0;
8056169699Skan
8057169699Skan  if (!INTEGRAL_TYPE_P (TREE_TYPE (var))
8058169699Skan      && !POINTER_TYPE_P (TREE_TYPE (var)))
8059169699Skan    return 0;
8060169699Skan
8061169699Skan  if (! var_decl_component_p (var))
8062169699Skan    return 0;
8063169699Skan
8064169699Skan  length = tree_low_cst (len, 1);
8065169699Skan  if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (var))) != length
8066169699Skan      || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
8067169699Skan	 < (int) length)
8068169699Skan    return 0;
8069169699Skan
8070169699Skan  if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
8071169699Skan    return 0;
8072169699Skan
8073169699Skan  if (integer_zerop (c))
8074169699Skan    cval = 0;
8075169699Skan  else
8076169699Skan    {
8077169699Skan      if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
8078169699Skan	return 0;
8079169699Skan
8080169699Skan      cval = tree_low_cst (c, 1);
8081169699Skan      cval &= 0xff;
8082169699Skan      cval |= cval << 8;
8083169699Skan      cval |= cval << 16;
8084169699Skan      cval |= (cval << 31) << 1;
8085169699Skan    }
8086169699Skan
8087169699Skan  ret = build_int_cst_type (TREE_TYPE (var), cval);
8088169699Skan  ret = build2 (MODIFY_EXPR, TREE_TYPE (var), var, ret);
8089169699Skan  if (ignore)
8090169699Skan    return ret;
8091169699Skan
8092169699Skan  return omit_one_operand (type, dest, ret);
8093132727Skan}
8094132727Skan
8095169699Skan/* Fold function call to builtin memset.  Return
8096132727Skan   NULL_TREE if no simplification can be made.  */
8097132727Skan
8098132727Skanstatic tree
8099169699Skanfold_builtin_bzero (tree arglist, bool ignore)
8100132727Skan{
8101169699Skan  tree dest, size, newarglist;
8102132727Skan
8103169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
8104132727Skan    return 0;
8105132727Skan
8106169699Skan  if (!ignore)
8107169699Skan    return 0;
8108169699Skan
8109132727Skan  dest = TREE_VALUE (arglist);
8110169699Skan  size = TREE_VALUE (TREE_CHAIN (arglist));
8111169699Skan
8112169699Skan  /* New argument list transforming bzero(ptr x, int y) to
8113169699Skan     memset(ptr x, int 0, size_t y).   This is done this way
8114169699Skan     so that if it isn't expanded inline, we fallback to
8115169699Skan     calling bzero instead of memset.  */
8116169699Skan
8117169699Skan  newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size));
8118169699Skan  newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
8119169699Skan  newarglist = tree_cons (NULL_TREE, dest, newarglist);
8120169699Skan  return fold_builtin_memset (newarglist, void_type_node, ignore);
8121169699Skan}
8122169699Skan
8123169699Skan/* Fold function call to builtin mem{{,p}cpy,move}.  Return
8124169699Skan   NULL_TREE if no simplification can be made.
8125169699Skan   If ENDP is 0, return DEST (like memcpy).
8126169699Skan   If ENDP is 1, return DEST+LEN (like mempcpy).
8127169699Skan   If ENDP is 2, return DEST+LEN-1 (like stpcpy).
8128169699Skan   If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
8129169699Skan   (memmove).   */
8130169699Skan
8131169699Skanstatic tree
8132169699Skanfold_builtin_memory_op (tree arglist, tree type, bool ignore, int endp)
8133169699Skan{
8134169699Skan  tree dest, src, len, destvar, srcvar, expr;
8135169699Skan  unsigned HOST_WIDE_INT length;
8136169699Skan
8137169699Skan  if (! validate_arglist (arglist,
8138169699Skan			  POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
8139169699Skan    return 0;
8140169699Skan
8141169699Skan  dest = TREE_VALUE (arglist);
8142132727Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
8143132727Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
8144132727Skan
8145132727Skan  /* If the LEN parameter is zero, return DEST.  */
8146132727Skan  if (integer_zerop (len))
8147169699Skan    return omit_one_operand (type, dest, src);
8148132727Skan
8149169699Skan  /* If SRC and DEST are the same (and not volatile), return
8150169699Skan     DEST{,+LEN,+LEN-1}.  */
8151132727Skan  if (operand_equal_p (src, dest, 0))
8152169699Skan    expr = len;
8153169699Skan  else
8154132727Skan    {
8155169699Skan      if (! host_integerp (len, 1))
8156169699Skan	return 0;
8157169699Skan
8158169699Skan      if (TREE_SIDE_EFFECTS (dest) || TREE_SIDE_EFFECTS (src))
8159169699Skan	return 0;
8160169699Skan
8161169699Skan      destvar = dest;
8162169699Skan      STRIP_NOPS (destvar);
8163169699Skan      if (TREE_CODE (destvar) != ADDR_EXPR)
8164169699Skan	return 0;
8165169699Skan
8166169699Skan      destvar = TREE_OPERAND (destvar, 0);
8167169699Skan      if (TREE_THIS_VOLATILE (destvar))
8168169699Skan	return 0;
8169169699Skan
8170169699Skan      if (!INTEGRAL_TYPE_P (TREE_TYPE (destvar))
8171169699Skan	  && !POINTER_TYPE_P (TREE_TYPE (destvar))
8172169699Skan	  && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (destvar)))
8173169699Skan	return 0;
8174169699Skan
8175169699Skan      if (! var_decl_component_p (destvar))
8176169699Skan	return 0;
8177169699Skan
8178169699Skan      srcvar = src;
8179169699Skan      STRIP_NOPS (srcvar);
8180169699Skan      if (TREE_CODE (srcvar) != ADDR_EXPR)
8181169699Skan	return 0;
8182169699Skan
8183169699Skan      srcvar = TREE_OPERAND (srcvar, 0);
8184169699Skan      if (TREE_THIS_VOLATILE (srcvar))
8185169699Skan	return 0;
8186169699Skan
8187169699Skan      if (!INTEGRAL_TYPE_P (TREE_TYPE (srcvar))
8188169699Skan	  && !POINTER_TYPE_P (TREE_TYPE (srcvar))
8189169699Skan	  && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (srcvar)))
8190169699Skan	return 0;
8191169699Skan
8192169699Skan      if (! var_decl_component_p (srcvar))
8193169699Skan	return 0;
8194169699Skan
8195169699Skan      length = tree_low_cst (len, 1);
8196169699Skan      if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (destvar))) != length
8197169699Skan	  || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
8198169699Skan	     < (int) length
8199169699Skan	  || GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (srcvar))) != length
8200169699Skan	  || get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
8201169699Skan	     < (int) length)
8202169699Skan	return 0;
8203169699Skan
8204169699Skan      if ((INTEGRAL_TYPE_P (TREE_TYPE (srcvar))
8205169699Skan	   || POINTER_TYPE_P (TREE_TYPE (srcvar)))
8206169699Skan	  && (INTEGRAL_TYPE_P (TREE_TYPE (destvar))
8207169699Skan	      || POINTER_TYPE_P (TREE_TYPE (destvar))))
8208169699Skan	expr = fold_convert (TREE_TYPE (destvar), srcvar);
8209169699Skan      else
8210169699Skan	expr = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (destvar), srcvar);
8211169699Skan      expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, expr);
8212132727Skan    }
8213132727Skan
8214169699Skan  if (ignore)
8215169699Skan    return expr;
8216169699Skan
8217169699Skan  if (endp == 0 || endp == 3)
8218169699Skan    return omit_one_operand (type, dest, expr);
8219169699Skan
8220169699Skan  if (expr == len)
8221169699Skan    expr = 0;
8222169699Skan
8223169699Skan  if (endp == 2)
8224169699Skan    len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len,
8225169699Skan		       ssize_int (1));
8226169699Skan
8227169699Skan  len = fold_convert (TREE_TYPE (dest), len);
8228169699Skan  dest = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len);
8229169699Skan  dest = fold_convert (type, dest);
8230169699Skan  if (expr)
8231169699Skan    dest = omit_one_operand (type, dest, expr);
8232169699Skan  return dest;
8233132727Skan}
8234132727Skan
8235169699Skan/* Fold function call to builtin bcopy.  Return NULL_TREE if no
8236169699Skan   simplification can be made.  */
8237132727Skan
8238132727Skanstatic tree
8239169699Skanfold_builtin_bcopy (tree arglist, bool ignore)
8240132727Skan{
8241169699Skan  tree src, dest, size, newarglist;
8242132727Skan
8243132727Skan  if (!validate_arglist (arglist,
8244132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
8245132727Skan    return 0;
8246132727Skan
8247169699Skan  if (! ignore)
8248169699Skan    return 0;
8249132727Skan
8250169699Skan  src = TREE_VALUE (arglist);
8251169699Skan  dest = TREE_VALUE (TREE_CHAIN (arglist));
8252169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
8253132727Skan
8254169699Skan  /* New argument list transforming bcopy(ptr x, ptr y, int z) to
8255169699Skan     memmove(ptr y, ptr x, size_t z).   This is done this way
8256169699Skan     so that if it isn't expanded inline, we fallback to
8257169699Skan     calling bcopy instead of memmove.  */
8258132727Skan
8259169699Skan  newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size));
8260169699Skan  newarglist = tree_cons (NULL_TREE, src, newarglist);
8261169699Skan  newarglist = tree_cons (NULL_TREE, dest, newarglist);
8262169699Skan
8263169699Skan  return fold_builtin_memory_op (newarglist, void_type_node, true, /*endp=*/3);
8264132727Skan}
8265132727Skan
8266169699Skan/* Fold function call to builtin strcpy.  If LEN is not NULL, it represents
8267169699Skan   the length of the string to be copied.  Return NULL_TREE if no
8268169699Skan   simplification can be made.  */
8269132727Skan
8270169699Skantree
8271169699Skanfold_builtin_strcpy (tree fndecl, tree arglist, tree len)
8272132727Skan{
8273169699Skan  tree dest, src, fn;
8274132727Skan
8275132727Skan  if (!validate_arglist (arglist,
8276132727Skan			 POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
8277132727Skan    return 0;
8278132727Skan
8279132727Skan  dest = TREE_VALUE (arglist);
8280132727Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
8281132727Skan
8282132727Skan  /* If SRC and DEST are the same (and not volatile), return DEST.  */
8283132727Skan  if (operand_equal_p (src, dest, 0))
8284169699Skan    return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest);
8285132727Skan
8286169699Skan  if (optimize_size)
8287169699Skan    return 0;
8288169699Skan
8289169699Skan  fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
8290169699Skan  if (!fn)
8291169699Skan    return 0;
8292169699Skan
8293169699Skan  if (!len)
8294169699Skan    {
8295169699Skan      len = c_strlen (src, 1);
8296169699Skan      if (! len || TREE_SIDE_EFFECTS (len))
8297169699Skan	return 0;
8298169699Skan    }
8299169699Skan
8300169699Skan  len = size_binop (PLUS_EXPR, len, ssize_int (1));
8301169699Skan  arglist = build_tree_list (NULL_TREE, len);
8302169699Skan  arglist = tree_cons (NULL_TREE, src, arglist);
8303169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
8304169699Skan  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
8305169699Skan		       build_function_call_expr (fn, arglist));
8306132727Skan}
8307132727Skan
8308169699Skan/* Fold function call to builtin strncpy.  If SLEN is not NULL, it represents
8309169699Skan   the length of the source string.  Return NULL_TREE if no simplification
8310169699Skan   can be made.  */
8311132727Skan
8312169699Skantree
8313169699Skanfold_builtin_strncpy (tree fndecl, tree arglist, tree slen)
8314132727Skan{
8315169699Skan  tree dest, src, len, fn;
8316132727Skan
8317132727Skan  if (!validate_arglist (arglist,
8318132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
8319132727Skan    return 0;
8320132727Skan
8321132727Skan  dest = TREE_VALUE (arglist);
8322132727Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
8323132727Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
8324132727Skan
8325132727Skan  /* If the LEN parameter is zero, return DEST.  */
8326132727Skan  if (integer_zerop (len))
8327169699Skan    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
8328132727Skan
8329169699Skan  /* We can't compare slen with len as constants below if len is not a
8330169699Skan     constant.  */
8331169699Skan  if (len == 0 || TREE_CODE (len) != INTEGER_CST)
8332169699Skan    return 0;
8333169699Skan
8334169699Skan  if (!slen)
8335169699Skan    slen = c_strlen (src, 1);
8336169699Skan
8337169699Skan  /* Now, we must be passed a constant src ptr parameter.  */
8338169699Skan  if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
8339169699Skan    return 0;
8340169699Skan
8341169699Skan  slen = size_binop (PLUS_EXPR, slen, ssize_int (1));
8342169699Skan
8343169699Skan  /* We do not support simplification of this case, though we do
8344169699Skan     support it when expanding trees into RTL.  */
8345169699Skan  /* FIXME: generate a call to __builtin_memset.  */
8346169699Skan  if (tree_int_cst_lt (slen, len))
8347169699Skan    return 0;
8348169699Skan
8349169699Skan  /* OK transform into builtin memcpy.  */
8350169699Skan  fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
8351169699Skan  if (!fn)
8352169699Skan    return 0;
8353169699Skan  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
8354169699Skan		       build_function_call_expr (fn, arglist));
8355132727Skan}
8356132727Skan
8357132727Skan/* Fold function call to builtin memcmp.  Return
8358132727Skan   NULL_TREE if no simplification can be made.  */
8359132727Skan
8360132727Skanstatic tree
8361169699Skanfold_builtin_memcmp (tree arglist)
8362132727Skan{
8363132727Skan  tree arg1, arg2, len;
8364169699Skan  const char *p1, *p2;
8365132727Skan
8366132727Skan  if (!validate_arglist (arglist,
8367132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
8368132727Skan    return 0;
8369132727Skan
8370132727Skan  arg1 = TREE_VALUE (arglist);
8371132727Skan  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
8372132727Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
8373132727Skan
8374132727Skan  /* If the LEN parameter is zero, return zero.  */
8375132727Skan  if (integer_zerop (len))
8376169699Skan    return omit_two_operands (integer_type_node, integer_zero_node,
8377169699Skan			      arg1, arg2);
8378132727Skan
8379132727Skan  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
8380132727Skan  if (operand_equal_p (arg1, arg2, 0))
8381169699Skan    return omit_one_operand (integer_type_node, integer_zero_node, len);
8382132727Skan
8383169699Skan  p1 = c_getstr (arg1);
8384169699Skan  p2 = c_getstr (arg2);
8385169699Skan
8386169699Skan  /* If all arguments are constant, and the value of len is not greater
8387169699Skan     than the lengths of arg1 and arg2, evaluate at compile-time.  */
8388169699Skan  if (host_integerp (len, 1) && p1 && p2
8389169699Skan      && compare_tree_int (len, strlen (p1) + 1) <= 0
8390169699Skan      && compare_tree_int (len, strlen (p2) + 1) <= 0)
8391169699Skan    {
8392169699Skan      const int r = memcmp (p1, p2, tree_low_cst (len, 1));
8393169699Skan
8394169699Skan      if (r > 0)
8395169699Skan	return integer_one_node;
8396169699Skan      else if (r < 0)
8397169699Skan	return integer_minus_one_node;
8398169699Skan      else
8399169699Skan	return integer_zero_node;
8400169699Skan    }
8401169699Skan
8402169699Skan  /* If len parameter is one, return an expression corresponding to
8403169699Skan     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
8404169699Skan  if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
8405169699Skan    {
8406169699Skan      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
8407169699Skan      tree cst_uchar_ptr_node
8408169699Skan	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
8409169699Skan
8410169699Skan      tree ind1 = fold_convert (integer_type_node,
8411169699Skan				build1 (INDIRECT_REF, cst_uchar_node,
8412169699Skan					fold_convert (cst_uchar_ptr_node,
8413169699Skan						      arg1)));
8414169699Skan      tree ind2 = fold_convert (integer_type_node,
8415169699Skan				build1 (INDIRECT_REF, cst_uchar_node,
8416169699Skan					fold_convert (cst_uchar_ptr_node,
8417169699Skan						      arg2)));
8418169699Skan      return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2);
8419169699Skan    }
8420169699Skan
8421132727Skan  return 0;
8422132727Skan}
8423132727Skan
8424132727Skan/* Fold function call to builtin strcmp.  Return
8425132727Skan   NULL_TREE if no simplification can be made.  */
8426132727Skan
8427132727Skanstatic tree
8428169699Skanfold_builtin_strcmp (tree arglist)
8429132727Skan{
8430132727Skan  tree arg1, arg2;
8431132727Skan  const char *p1, *p2;
8432132727Skan
8433169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
8434132727Skan    return 0;
8435132727Skan
8436132727Skan  arg1 = TREE_VALUE (arglist);
8437132727Skan  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
8438132727Skan
8439132727Skan  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
8440132727Skan  if (operand_equal_p (arg1, arg2, 0))
8441169699Skan    return integer_zero_node;
8442132727Skan
8443132727Skan  p1 = c_getstr (arg1);
8444132727Skan  p2 = c_getstr (arg2);
8445132727Skan
8446132727Skan  if (p1 && p2)
8447132727Skan    {
8448132727Skan      const int i = strcmp (p1, p2);
8449132727Skan      if (i < 0)
8450169699Skan	return integer_minus_one_node;
8451132727Skan      else if (i > 0)
8452169699Skan	return integer_one_node;
8453132727Skan      else
8454169699Skan	return integer_zero_node;
8455132727Skan    }
8456132727Skan
8457169699Skan  /* If the second arg is "", return *(const unsigned char*)arg1.  */
8458169699Skan  if (p2 && *p2 == '\0')
8459169699Skan    {
8460169699Skan      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
8461169699Skan      tree cst_uchar_ptr_node
8462169699Skan	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
8463169699Skan
8464169699Skan      return fold_convert (integer_type_node,
8465169699Skan			   build1 (INDIRECT_REF, cst_uchar_node,
8466169699Skan				   fold_convert (cst_uchar_ptr_node,
8467169699Skan						 arg1)));
8468169699Skan    }
8469169699Skan
8470169699Skan  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
8471169699Skan  if (p1 && *p1 == '\0')
8472169699Skan    {
8473169699Skan      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
8474169699Skan      tree cst_uchar_ptr_node
8475169699Skan	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
8476169699Skan
8477169699Skan      tree temp = fold_convert (integer_type_node,
8478169699Skan				build1 (INDIRECT_REF, cst_uchar_node,
8479169699Skan					fold_convert (cst_uchar_ptr_node,
8480169699Skan						      arg2)));
8481169699Skan      return fold_build1 (NEGATE_EXPR, integer_type_node, temp);
8482169699Skan    }
8483169699Skan
8484132727Skan  return 0;
8485132727Skan}
8486132727Skan
8487132727Skan/* Fold function call to builtin strncmp.  Return
8488132727Skan   NULL_TREE if no simplification can be made.  */
8489132727Skan
8490132727Skanstatic tree
8491169699Skanfold_builtin_strncmp (tree arglist)
8492132727Skan{
8493132727Skan  tree arg1, arg2, len;
8494132727Skan  const char *p1, *p2;
8495132727Skan
8496132727Skan  if (!validate_arglist (arglist,
8497132727Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
8498132727Skan    return 0;
8499132727Skan
8500132727Skan  arg1 = TREE_VALUE (arglist);
8501132727Skan  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
8502132727Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
8503132727Skan
8504132727Skan  /* If the LEN parameter is zero, return zero.  */
8505132727Skan  if (integer_zerop (len))
8506169699Skan    return omit_two_operands (integer_type_node, integer_zero_node,
8507169699Skan			      arg1, arg2);
8508132727Skan
8509132727Skan  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
8510132727Skan  if (operand_equal_p (arg1, arg2, 0))
8511169699Skan    return omit_one_operand (integer_type_node, integer_zero_node, len);
8512132727Skan
8513132727Skan  p1 = c_getstr (arg1);
8514132727Skan  p2 = c_getstr (arg2);
8515132727Skan
8516132727Skan  if (host_integerp (len, 1) && p1 && p2)
8517132727Skan    {
8518132727Skan      const int i = strncmp (p1, p2, tree_low_cst (len, 1));
8519169699Skan      if (i > 0)
8520169699Skan	return integer_one_node;
8521169699Skan      else if (i < 0)
8522169699Skan	return integer_minus_one_node;
8523132727Skan      else
8524169699Skan	return integer_zero_node;
8525132727Skan    }
8526132727Skan
8527169699Skan  /* If the second arg is "", and the length is greater than zero,
8528169699Skan     return *(const unsigned char*)arg1.  */
8529169699Skan  if (p2 && *p2 == '\0'
8530169699Skan      && TREE_CODE (len) == INTEGER_CST
8531169699Skan      && tree_int_cst_sgn (len) == 1)
8532169699Skan    {
8533169699Skan      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
8534169699Skan      tree cst_uchar_ptr_node
8535169699Skan	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
8536169699Skan
8537169699Skan      return fold_convert (integer_type_node,
8538169699Skan			   build1 (INDIRECT_REF, cst_uchar_node,
8539169699Skan				   fold_convert (cst_uchar_ptr_node,
8540169699Skan						 arg1)));
8541169699Skan    }
8542169699Skan
8543169699Skan  /* If the first arg is "", and the length is greater than zero,
8544169699Skan     return -*(const unsigned char*)arg2.  */
8545169699Skan  if (p1 && *p1 == '\0'
8546169699Skan      && TREE_CODE (len) == INTEGER_CST
8547169699Skan      && tree_int_cst_sgn (len) == 1)
8548169699Skan    {
8549169699Skan      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
8550169699Skan      tree cst_uchar_ptr_node
8551169699Skan	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
8552169699Skan
8553169699Skan      tree temp = fold_convert (integer_type_node,
8554169699Skan				build1 (INDIRECT_REF, cst_uchar_node,
8555169699Skan					fold_convert (cst_uchar_ptr_node,
8556169699Skan						      arg2)));
8557169699Skan      return fold_build1 (NEGATE_EXPR, integer_type_node, temp);
8558169699Skan    }
8559169699Skan
8560169699Skan  /* If len parameter is one, return an expression corresponding to
8561169699Skan     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
8562169699Skan  if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
8563169699Skan    {
8564169699Skan      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
8565169699Skan      tree cst_uchar_ptr_node
8566169699Skan	= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
8567169699Skan
8568169699Skan      tree ind1 = fold_convert (integer_type_node,
8569169699Skan				build1 (INDIRECT_REF, cst_uchar_node,
8570169699Skan					fold_convert (cst_uchar_ptr_node,
8571169699Skan						      arg1)));
8572169699Skan      tree ind2 = fold_convert (integer_type_node,
8573169699Skan				build1 (INDIRECT_REF, cst_uchar_node,
8574169699Skan					fold_convert (cst_uchar_ptr_node,
8575169699Skan						      arg2)));
8576169699Skan      return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2);
8577169699Skan    }
8578169699Skan
8579132727Skan  return 0;
8580132727Skan}
8581132727Skan
8582169699Skan/* Fold function call to builtin signbit, signbitf or signbitl.  Return
8583169699Skan   NULL_TREE if no simplification can be made.  */
858490075Sobrien
8585169699Skanstatic tree
8586169699Skanfold_builtin_signbit (tree fndecl, tree arglist)
858790075Sobrien{
8588132727Skan  tree type = TREE_TYPE (TREE_TYPE (fndecl));
8589169699Skan  tree arg, temp;
859090075Sobrien
8591169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
8592169699Skan    return NULL_TREE;
8593169699Skan
8594169699Skan  arg = TREE_VALUE (arglist);
8595169699Skan
8596169699Skan  /* If ARG is a compile-time constant, determine the result.  */
8597169699Skan  if (TREE_CODE (arg) == REAL_CST
8598169699Skan      && !TREE_CONSTANT_OVERFLOW (arg))
8599169699Skan    {
8600169699Skan      REAL_VALUE_TYPE c;
8601169699Skan
8602169699Skan      c = TREE_REAL_CST (arg);
8603169699Skan      temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node;
8604169699Skan      return fold_convert (type, temp);
8605169699Skan    }
8606169699Skan
8607169699Skan  /* If ARG is non-negative, the result is always zero.  */
8608169699Skan  if (tree_expr_nonnegative_p (arg))
8609169699Skan    return omit_one_operand (type, integer_zero_node, arg);
8610169699Skan
8611169699Skan  /* If ARG's format doesn't have signed zeros, return "arg < 0.0".  */
8612169699Skan  if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg))))
8613169699Skan    return fold_build2 (LT_EXPR, type, arg,
8614169699Skan			build_real (TREE_TYPE (arg), dconst0));
8615169699Skan
8616169699Skan  return NULL_TREE;
8617169699Skan}
8618169699Skan
8619169699Skan/* Fold function call to builtin copysign, copysignf or copysignl.
8620169699Skan   Return NULL_TREE if no simplification can be made.  */
8621169699Skan
8622169699Skanstatic tree
8623169699Skanfold_builtin_copysign (tree fndecl, tree arglist, tree type)
8624169699Skan{
8625169699Skan  tree arg1, arg2, tem;
8626169699Skan
8627169699Skan  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
8628169699Skan    return NULL_TREE;
8629169699Skan
8630169699Skan  arg1 = TREE_VALUE (arglist);
8631169699Skan  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
8632169699Skan
8633169699Skan  /* copysign(X,X) is X.  */
8634169699Skan  if (operand_equal_p (arg1, arg2, 0))
8635169699Skan    return fold_convert (type, arg1);
8636169699Skan
8637169699Skan  /* If ARG1 and ARG2 are compile-time constants, determine the result.  */
8638169699Skan  if (TREE_CODE (arg1) == REAL_CST
8639169699Skan      && TREE_CODE (arg2) == REAL_CST
8640169699Skan      && !TREE_CONSTANT_OVERFLOW (arg1)
8641169699Skan      && !TREE_CONSTANT_OVERFLOW (arg2))
8642169699Skan    {
8643169699Skan      REAL_VALUE_TYPE c1, c2;
8644169699Skan
8645169699Skan      c1 = TREE_REAL_CST (arg1);
8646169699Skan      c2 = TREE_REAL_CST (arg2);
8647169699Skan      /* c1.sign := c2.sign.  */
8648169699Skan      real_copysign (&c1, &c2);
8649169699Skan      return build_real (type, c1);
8650169699Skan    }
8651169699Skan
8652169699Skan  /* copysign(X, Y) is fabs(X) when Y is always non-negative.
8653169699Skan     Remember to evaluate Y for side-effects.  */
8654169699Skan  if (tree_expr_nonnegative_p (arg2))
8655169699Skan    return omit_one_operand (type,
8656169699Skan			     fold_build1 (ABS_EXPR, type, arg1),
8657169699Skan			     arg2);
8658169699Skan
8659169699Skan  /* Strip sign changing operations for the first argument.  */
8660169699Skan  tem = fold_strip_sign_ops (arg1);
8661169699Skan  if (tem)
8662169699Skan    {
8663169699Skan      arglist = tree_cons (NULL_TREE, tem, TREE_CHAIN (arglist));
8664169699Skan      return build_function_call_expr (fndecl, arglist);
8665169699Skan    }
8666169699Skan
8667169699Skan  return NULL_TREE;
8668169699Skan}
8669169699Skan
8670169699Skan/* Fold a call to builtin isascii.  */
8671169699Skan
8672169699Skanstatic tree
8673169699Skanfold_builtin_isascii (tree arglist)
8674169699Skan{
8675169699Skan  if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
867690075Sobrien    return 0;
8677169699Skan  else
8678169699Skan    {
8679169699Skan      /* Transform isascii(c) -> ((c & ~0x7f) == 0).  */
8680169699Skan      tree arg = TREE_VALUE (arglist);
868190075Sobrien
8682169699Skan      arg = build2 (BIT_AND_EXPR, integer_type_node, arg,
8683169699Skan		    build_int_cst (NULL_TREE,
8684169699Skan				   ~ (unsigned HOST_WIDE_INT) 0x7f));
8685169699Skan      arg = fold_build2 (EQ_EXPR, integer_type_node,
8686169699Skan			 arg, integer_zero_node);
8687169699Skan
8688169699Skan      if (in_gimple_form && !TREE_CONSTANT (arg))
8689169699Skan	return NULL_TREE;
8690169699Skan      else
8691169699Skan	return arg;
8692169699Skan    }
8693169699Skan}
8694169699Skan
8695169699Skan/* Fold a call to builtin toascii.  */
8696169699Skan
8697169699Skanstatic tree
8698169699Skanfold_builtin_toascii (tree arglist)
8699169699Skan{
8700169699Skan  if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
8701169699Skan    return 0;
8702169699Skan  else
870390075Sobrien    {
8704169699Skan      /* Transform toascii(c) -> (c & 0x7f).  */
8705169699Skan      tree arg = TREE_VALUE (arglist);
870690075Sobrien
8707169699Skan      return fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
8708169699Skan			  build_int_cst (NULL_TREE, 0x7f));
8709169699Skan    }
8710169699Skan}
871190075Sobrien
8712169699Skan/* Fold a call to builtin isdigit.  */
871390075Sobrien
8714169699Skanstatic tree
8715169699Skanfold_builtin_isdigit (tree arglist)
8716169699Skan{
8717169699Skan  if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
8718169699Skan    return 0;
8719169699Skan  else
8720169699Skan    {
8721169699Skan      /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9.  */
8722169699Skan      /* According to the C standard, isdigit is unaffected by locale.
8723169699Skan	 However, it definitely is affected by the target character set.  */
8724169699Skan      tree arg;
8725169699Skan      unsigned HOST_WIDE_INT target_digit0
8726169699Skan	= lang_hooks.to_target_charset ('0');
8727132727Skan
8728169699Skan      if (target_digit0 == 0)
8729169699Skan	return NULL_TREE;
8730132727Skan
8731169699Skan      arg = fold_convert (unsigned_type_node, TREE_VALUE (arglist));
8732169699Skan      arg = build2 (MINUS_EXPR, unsigned_type_node, arg,
8733169699Skan		    build_int_cst (unsigned_type_node, target_digit0));
8734169699Skan      arg = fold_build2 (LE_EXPR, integer_type_node, arg,
8735169699Skan			 build_int_cst (unsigned_type_node, 9));
8736169699Skan      if (in_gimple_form && !TREE_CONSTANT (arg))
8737169699Skan	return NULL_TREE;
8738169699Skan      else
8739169699Skan	return arg;
8740169699Skan    }
8741169699Skan}
8742132727Skan
8743169699Skan/* Fold a call to fabs, fabsf or fabsl.  */
8744132727Skan
8745169699Skanstatic tree
8746169699Skanfold_builtin_fabs (tree arglist, tree type)
8747169699Skan{
8748169699Skan  tree arg;
8749132727Skan
8750169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
8751169699Skan    return 0;
8752132727Skan
8753169699Skan  arg = TREE_VALUE (arglist);
8754169699Skan  arg = fold_convert (type, arg);
8755169699Skan  if (TREE_CODE (arg) == REAL_CST)
8756169699Skan    return fold_abs_const (arg, type);
8757169699Skan  return fold_build1 (ABS_EXPR, type, arg);
8758169699Skan}
8759132727Skan
8760169699Skan/* Fold a call to abs, labs, llabs or imaxabs.  */
8761132727Skan
8762169699Skanstatic tree
8763169699Skanfold_builtin_abs (tree arglist, tree type)
8764169699Skan{
8765169699Skan  tree arg;
8766132727Skan
8767169699Skan  if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
8768169699Skan    return 0;
8769132727Skan
8770169699Skan  arg = TREE_VALUE (arglist);
8771169699Skan  arg = fold_convert (type, arg);
8772169699Skan  if (TREE_CODE (arg) == INTEGER_CST)
8773169699Skan    return fold_abs_const (arg, type);
8774169699Skan  return fold_build1 (ABS_EXPR, type, arg);
8775169699Skan}
8776132727Skan
8777169699Skan/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
8778169699Skan   EXP is the CALL_EXPR for the call.  */
8779132727Skan
8780169699Skanstatic tree
8781169699Skanfold_builtin_classify (tree fndecl, tree arglist, int builtin_index)
8782169699Skan{
8783169699Skan  tree type = TREE_TYPE (TREE_TYPE (fndecl));
8784169699Skan  tree arg;
8785169699Skan  REAL_VALUE_TYPE r;
8786132727Skan
8787169699Skan  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
8788169699Skan    {
8789169699Skan      /* Check that we have exactly one argument.  */
8790169699Skan      if (arglist == 0)
8791132727Skan	{
8792169699Skan	  error ("too few arguments to function %qs",
8793169699Skan		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
8794169699Skan	  return error_mark_node;
8795169699Skan	}
8796169699Skan      else if (TREE_CHAIN (arglist) != 0)
8797169699Skan	{
8798169699Skan	  error ("too many arguments to function %qs",
8799169699Skan		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
8800169699Skan	  return error_mark_node;
8801169699Skan	}
8802169699Skan      else
8803169699Skan	{
8804169699Skan	  error ("non-floating-point argument to function %qs",
8805169699Skan		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
8806169699Skan	  return error_mark_node;
8807169699Skan	}
8808169699Skan    }
8809132727Skan
8810169699Skan  arg = TREE_VALUE (arglist);
8811169699Skan  switch (builtin_index)
8812169699Skan    {
8813169699Skan    case BUILT_IN_ISINF:
8814228756Spfg      if (!HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg))))
8815169699Skan	return omit_one_operand (type, integer_zero_node, arg);
8816132727Skan
8817169699Skan      if (TREE_CODE (arg) == REAL_CST)
8818169699Skan	{
8819169699Skan	  r = TREE_REAL_CST (arg);
8820169699Skan	  if (real_isinf (&r))
8821169699Skan	    return real_compare (GT_EXPR, &r, &dconst0)
8822169699Skan		   ? integer_one_node : integer_minus_one_node;
8823169699Skan	  else
8824169699Skan	    return integer_zero_node;
8825132727Skan	}
8826132727Skan
8827169699Skan      return NULL_TREE;
8828169699Skan
8829169699Skan    case BUILT_IN_FINITE:
8830228756Spfg      if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg)))
8831228756Spfg	  && !HONOR_INFINITIES (TYPE_MODE (TREE_TYPE (arg))))
8832233923Spfg	return omit_one_operand (type, integer_one_node, arg);
8833169699Skan
8834169699Skan      if (TREE_CODE (arg) == REAL_CST)
8835132727Skan	{
8836169699Skan	  r = TREE_REAL_CST (arg);
8837169699Skan	  return real_isinf (&r) || real_isnan (&r)
8838169699Skan		 ? integer_zero_node : integer_one_node;
8839169699Skan	}
8840132727Skan
8841169699Skan      return NULL_TREE;
8842132727Skan
8843169699Skan    case BUILT_IN_ISNAN:
8844228756Spfg      if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg))))
8845169699Skan	return omit_one_operand (type, integer_zero_node, arg);
8846132727Skan
8847169699Skan      if (TREE_CODE (arg) == REAL_CST)
8848169699Skan	{
8849169699Skan	  r = TREE_REAL_CST (arg);
8850169699Skan	  return real_isnan (&r) ? integer_one_node : integer_zero_node;
8851132727Skan	}
8852132727Skan
8853169699Skan      arg = builtin_save_expr (arg);
8854169699Skan      return fold_build2 (UNORDERED_EXPR, type, arg, arg);
8855169699Skan
8856169699Skan    default:
8857169699Skan      gcc_unreachable ();
8858169699Skan    }
8859169699Skan}
8860169699Skan
8861169699Skan/* Fold a call to an unordered comparison function such as
8862169699Skan   __builtin_isgreater().  FNDECL is the FUNCTION_DECL for the function
8863169699Skan   being called and ARGLIST is the argument list for the call.
8864169699Skan   UNORDERED_CODE and ORDERED_CODE are comparison codes that give
8865169699Skan   the opposite of the desired result.  UNORDERED_CODE is used
8866169699Skan   for modes that can hold NaNs and ORDERED_CODE is used for
8867169699Skan   the rest.  */
8868169699Skan
8869169699Skanstatic tree
8870169699Skanfold_builtin_unordered_cmp (tree fndecl, tree arglist,
8871169699Skan			    enum tree_code unordered_code,
8872169699Skan			    enum tree_code ordered_code)
8873169699Skan{
8874169699Skan  tree type = TREE_TYPE (TREE_TYPE (fndecl));
8875169699Skan  enum tree_code code;
8876169699Skan  tree arg0, arg1;
8877169699Skan  tree type0, type1;
8878169699Skan  enum tree_code code0, code1;
8879169699Skan  tree cmp_type = NULL_TREE;
8880169699Skan
8881169699Skan  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
8882169699Skan    {
8883169699Skan      /* Check that we have exactly two arguments.  */
8884169699Skan      if (arglist == 0 || TREE_CHAIN (arglist) == 0)
8885132727Skan	{
8886169699Skan	  error ("too few arguments to function %qs",
8887169699Skan		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
8888169699Skan	  return error_mark_node;
8889169699Skan	}
8890169699Skan      else if (TREE_CHAIN (TREE_CHAIN (arglist)) != 0)
8891169699Skan	{
8892169699Skan	  error ("too many arguments to function %qs",
8893169699Skan		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
8894169699Skan	  return error_mark_node;
8895169699Skan	}
8896169699Skan    }
8897132727Skan
8898169699Skan  arg0 = TREE_VALUE (arglist);
8899169699Skan  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
8900132727Skan
8901169699Skan  type0 = TREE_TYPE (arg0);
8902169699Skan  type1 = TREE_TYPE (arg1);
8903132727Skan
8904169699Skan  code0 = TREE_CODE (type0);
8905169699Skan  code1 = TREE_CODE (type1);
8906132727Skan
8907169699Skan  if (code0 == REAL_TYPE && code1 == REAL_TYPE)
8908169699Skan    /* Choose the wider of two real types.  */
8909169699Skan    cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
8910169699Skan      ? type0 : type1;
8911169699Skan  else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
8912169699Skan    cmp_type = type0;
8913169699Skan  else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
8914169699Skan    cmp_type = type1;
8915169699Skan  else
8916169699Skan    {
8917169699Skan      error ("non-floating-point argument to function %qs",
8918169699Skan		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
8919169699Skan      return error_mark_node;
8920169699Skan    }
8921132727Skan
8922169699Skan  arg0 = fold_convert (cmp_type, arg0);
8923169699Skan  arg1 = fold_convert (cmp_type, arg1);
8924132727Skan
8925169699Skan  if (unordered_code == UNORDERED_EXPR)
8926169699Skan    {
8927228756Spfg      if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg0))))
8928169699Skan	return omit_two_operands (type, integer_zero_node, arg0, arg1);
8929169699Skan      return fold_build2 (UNORDERED_EXPR, type, arg0, arg1);
8930169699Skan    }
8931132727Skan
8932228756Spfg  code = HONOR_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code
8933228756Spfg						   : ordered_code;
8934169699Skan  return fold_build1 (TRUTH_NOT_EXPR, type,
8935169699Skan		      fold_build2 (code, type, arg0, arg1));
8936169699Skan}
8937132727Skan
8938169699Skan/* Used by constant folding to simplify calls to builtin functions.  EXP is
8939169699Skan   the CALL_EXPR of a call to a builtin function.  IGNORE is true if the
8940169699Skan   result of the function call is ignored.  This function returns NULL_TREE
8941169699Skan   if no simplification was possible.  */
8942132727Skan
8943169699Skanstatic tree
8944169699Skanfold_builtin_1 (tree fndecl, tree arglist, bool ignore)
8945169699Skan{
8946169699Skan  tree type = TREE_TYPE (TREE_TYPE (fndecl));
8947169699Skan  enum built_in_function fcode;
8948132727Skan
8949169699Skan  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
8950169699Skan    return targetm.fold_builtin (fndecl, arglist, ignore);
8951132727Skan
8952169699Skan  fcode = DECL_FUNCTION_CODE (fndecl);
8953169699Skan  switch (fcode)
8954169699Skan    {
8955169699Skan    case BUILT_IN_FPUTS:
8956169699Skan      return fold_builtin_fputs (arglist, ignore, false, NULL_TREE);
8957132727Skan
8958169699Skan    case BUILT_IN_FPUTS_UNLOCKED:
8959169699Skan      return fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
8960132727Skan
8961169699Skan    case BUILT_IN_STRSTR:
8962169699Skan      return fold_builtin_strstr (arglist, type);
8963132727Skan
8964169699Skan    case BUILT_IN_STRCAT:
8965169699Skan      return fold_builtin_strcat (arglist);
8966132727Skan
8967169699Skan    case BUILT_IN_STRNCAT:
8968169699Skan      return fold_builtin_strncat (arglist);
8969169699Skan
8970169699Skan    case BUILT_IN_STRSPN:
8971169699Skan      return fold_builtin_strspn (arglist);
8972169699Skan
8973169699Skan    case BUILT_IN_STRCSPN:
8974169699Skan      return fold_builtin_strcspn (arglist);
8975169699Skan
8976169699Skan    case BUILT_IN_STRCHR:
8977169699Skan    case BUILT_IN_INDEX:
8978169699Skan      return fold_builtin_strchr (arglist, type);
8979169699Skan
8980169699Skan    case BUILT_IN_STRRCHR:
8981169699Skan    case BUILT_IN_RINDEX:
8982169699Skan      return fold_builtin_strrchr (arglist, type);
8983169699Skan
8984169699Skan    case BUILT_IN_STRCPY:
8985169699Skan      return fold_builtin_strcpy (fndecl, arglist, NULL_TREE);
8986169699Skan
8987169699Skan    case BUILT_IN_STRNCPY:
8988169699Skan      return fold_builtin_strncpy (fndecl, arglist, NULL_TREE);
8989169699Skan
8990169699Skan    case BUILT_IN_STRCMP:
8991169699Skan      return fold_builtin_strcmp (arglist);
8992169699Skan
8993169699Skan    case BUILT_IN_STRNCMP:
8994169699Skan      return fold_builtin_strncmp (arglist);
8995169699Skan
8996169699Skan    case BUILT_IN_STRPBRK:
8997169699Skan      return fold_builtin_strpbrk (arglist, type);
8998169699Skan
8999169699Skan    case BUILT_IN_BCMP:
9000169699Skan    case BUILT_IN_MEMCMP:
9001169699Skan      return fold_builtin_memcmp (arglist);
9002169699Skan
9003169699Skan    case BUILT_IN_SPRINTF:
9004169699Skan      return fold_builtin_sprintf (arglist, ignore);
9005169699Skan
9006169699Skan    case BUILT_IN_CONSTANT_P:
9007169699Skan      {
9008169699Skan	tree val;
9009169699Skan
9010169699Skan	val = fold_builtin_constant_p (arglist);
9011169699Skan	/* Gimplification will pull the CALL_EXPR for the builtin out of
9012169699Skan	   an if condition.  When not optimizing, we'll not CSE it back.
9013169699Skan	   To avoid link error types of regressions, return false now.  */
9014169699Skan	if (!val && !optimize)
9015169699Skan	  val = integer_zero_node;
9016169699Skan
9017169699Skan	return val;
9018169699Skan      }
9019169699Skan
9020169699Skan    case BUILT_IN_EXPECT:
9021169699Skan      return fold_builtin_expect (arglist);
9022169699Skan
9023169699Skan    case BUILT_IN_CLASSIFY_TYPE:
9024169699Skan      return fold_builtin_classify_type (arglist);
9025169699Skan
9026169699Skan    case BUILT_IN_STRLEN:
9027169699Skan      return fold_builtin_strlen (arglist);
9028169699Skan
9029169699Skan    CASE_FLT_FN (BUILT_IN_FABS):
9030169699Skan      return fold_builtin_fabs (arglist, type);
9031169699Skan
9032169699Skan    case BUILT_IN_ABS:
9033169699Skan    case BUILT_IN_LABS:
9034169699Skan    case BUILT_IN_LLABS:
9035169699Skan    case BUILT_IN_IMAXABS:
9036169699Skan      return fold_builtin_abs (arglist, type);
9037169699Skan
9038169699Skan    CASE_FLT_FN (BUILT_IN_CONJ):
9039169699Skan      if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
9040169699Skan	return fold_build1 (CONJ_EXPR, type, TREE_VALUE (arglist));
9041132727Skan      break;
9042132727Skan
9043169699Skan    CASE_FLT_FN (BUILT_IN_CREAL):
9044169699Skan      if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
9045169699Skan	return non_lvalue (fold_build1 (REALPART_EXPR, type,
9046169699Skan					TREE_VALUE (arglist)));
9047169699Skan      break;
9048169699Skan
9049169699Skan    CASE_FLT_FN (BUILT_IN_CIMAG):
9050169699Skan      if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
9051169699Skan	return non_lvalue (fold_build1 (IMAGPART_EXPR, type,
9052169699Skan					TREE_VALUE (arglist)));
9053169699Skan      break;
9054169699Skan
9055169699Skan    CASE_FLT_FN (BUILT_IN_CABS):
9056169699Skan      return fold_builtin_cabs (arglist, type, fndecl);
9057169699Skan
9058169699Skan    CASE_FLT_FN (BUILT_IN_SQRT):
9059169699Skan      return fold_builtin_sqrt (arglist, type);
9060169699Skan
9061169699Skan    CASE_FLT_FN (BUILT_IN_CBRT):
9062169699Skan      return fold_builtin_cbrt (arglist, type);
9063169699Skan
9064169699Skan    CASE_FLT_FN (BUILT_IN_SIN):
9065169699Skan      return fold_builtin_sin (arglist);
9066169699Skan
9067169699Skan    CASE_FLT_FN (BUILT_IN_COS):
9068169699Skan      return fold_builtin_cos (arglist, type, fndecl);
9069169699Skan
9070169699Skan    CASE_FLT_FN (BUILT_IN_EXP):
9071169699Skan      return fold_builtin_exponent (fndecl, arglist, &dconste);
9072169699Skan
9073169699Skan    CASE_FLT_FN (BUILT_IN_EXP2):
9074169699Skan      return fold_builtin_exponent (fndecl, arglist, &dconst2);
9075169699Skan
9076169699Skan    CASE_FLT_FN (BUILT_IN_EXP10):
9077169699Skan    CASE_FLT_FN (BUILT_IN_POW10):
9078169699Skan      return fold_builtin_exponent (fndecl, arglist, &dconst10);
9079169699Skan
9080169699Skan    CASE_FLT_FN (BUILT_IN_LOG):
9081169699Skan      return fold_builtin_logarithm (fndecl, arglist, &dconste);
9082169699Skan
9083169699Skan    CASE_FLT_FN (BUILT_IN_LOG2):
9084169699Skan      return fold_builtin_logarithm (fndecl, arglist, &dconst2);
9085169699Skan
9086169699Skan    CASE_FLT_FN (BUILT_IN_LOG10):
9087169699Skan      return fold_builtin_logarithm (fndecl, arglist, &dconst10);
9088169699Skan
9089169699Skan    CASE_FLT_FN (BUILT_IN_TAN):
9090169699Skan      return fold_builtin_tan (arglist);
9091169699Skan
9092169699Skan    CASE_FLT_FN (BUILT_IN_ATAN):
9093169699Skan      return fold_builtin_atan (arglist, type);
9094169699Skan
9095169699Skan    CASE_FLT_FN (BUILT_IN_POW):
9096169699Skan      return fold_builtin_pow (fndecl, arglist, type);
9097169699Skan
9098169699Skan    CASE_FLT_FN (BUILT_IN_POWI):
9099169699Skan      return fold_builtin_powi (fndecl, arglist, type);
9100169699Skan
9101169699Skan    CASE_FLT_FN (BUILT_IN_INF):
9102169699Skan    case BUILT_IN_INFD32:
9103169699Skan    case BUILT_IN_INFD64:
9104169699Skan    case BUILT_IN_INFD128:
9105132727Skan      return fold_builtin_inf (type, true);
9106117404Skan
9107169699Skan    CASE_FLT_FN (BUILT_IN_HUGE_VAL):
9108132727Skan      return fold_builtin_inf (type, false);
9109117404Skan
9110169699Skan    CASE_FLT_FN (BUILT_IN_NAN):
9111169699Skan    case BUILT_IN_NAND32:
9112169699Skan    case BUILT_IN_NAND64:
9113169699Skan    case BUILT_IN_NAND128:
9114132727Skan      return fold_builtin_nan (arglist, type, true);
9115117404Skan
9116169699Skan    CASE_FLT_FN (BUILT_IN_NANS):
9117132727Skan      return fold_builtin_nan (arglist, type, false);
9118117404Skan
9119169699Skan    CASE_FLT_FN (BUILT_IN_FLOOR):
9120169699Skan      return fold_builtin_floor (fndecl, arglist);
9121132727Skan
9122169699Skan    CASE_FLT_FN (BUILT_IN_CEIL):
9123169699Skan      return fold_builtin_ceil (fndecl, arglist);
9124132727Skan
9125169699Skan    CASE_FLT_FN (BUILT_IN_TRUNC):
9126169699Skan      return fold_builtin_trunc (fndecl, arglist);
9127132727Skan
9128169699Skan    CASE_FLT_FN (BUILT_IN_ROUND):
9129169699Skan      return fold_builtin_round (fndecl, arglist);
9130132727Skan
9131169699Skan    CASE_FLT_FN (BUILT_IN_NEARBYINT):
9132169699Skan    CASE_FLT_FN (BUILT_IN_RINT):
9133169699Skan      return fold_trunc_transparent_mathfn (fndecl, arglist);
9134132727Skan
9135169699Skan    CASE_FLT_FN (BUILT_IN_LCEIL):
9136169699Skan    CASE_FLT_FN (BUILT_IN_LLCEIL):
9137169699Skan    CASE_FLT_FN (BUILT_IN_LFLOOR):
9138169699Skan    CASE_FLT_FN (BUILT_IN_LLFLOOR):
9139169699Skan    CASE_FLT_FN (BUILT_IN_LROUND):
9140169699Skan    CASE_FLT_FN (BUILT_IN_LLROUND):
9141169699Skan      return fold_builtin_int_roundingfn (fndecl, arglist);
9142169699Skan
9143169699Skan    CASE_FLT_FN (BUILT_IN_LRINT):
9144169699Skan    CASE_FLT_FN (BUILT_IN_LLRINT):
9145169699Skan      return fold_fixed_mathfn (fndecl, arglist);
9146169699Skan
9147259563Spfg    case BUILT_IN_BSWAP32:
9148259563Spfg    case BUILT_IN_BSWAP64:
9149259563Spfg      return fold_builtin_bswap (fndecl, arglist);
9150259563Spfg
9151169699Skan    CASE_INT_FN (BUILT_IN_FFS):
9152169699Skan    CASE_INT_FN (BUILT_IN_CLZ):
9153169699Skan    CASE_INT_FN (BUILT_IN_CTZ):
9154169699Skan    CASE_INT_FN (BUILT_IN_POPCOUNT):
9155169699Skan    CASE_INT_FN (BUILT_IN_PARITY):
9156169699Skan      return fold_builtin_bitop (fndecl, arglist);
9157169699Skan
9158169699Skan    case BUILT_IN_MEMSET:
9159169699Skan      return fold_builtin_memset (arglist, type, ignore);
9160169699Skan
9161132727Skan    case BUILT_IN_MEMCPY:
9162169699Skan      return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/0);
9163132727Skan
9164132727Skan    case BUILT_IN_MEMPCPY:
9165169699Skan      return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/1);
9166132727Skan
9167132727Skan    case BUILT_IN_MEMMOVE:
9168169699Skan      return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/3);
9169132727Skan
9170169699Skan    case BUILT_IN_BZERO:
9171169699Skan      return fold_builtin_bzero (arglist, ignore);
9172132727Skan
9173169699Skan    case BUILT_IN_BCOPY:
9174169699Skan      return fold_builtin_bcopy (arglist, ignore);
9175132727Skan
9176169699Skan    CASE_FLT_FN (BUILT_IN_SIGNBIT):
9177169699Skan      return fold_builtin_signbit (fndecl, arglist);
9178132727Skan
9179169699Skan    case BUILT_IN_ISASCII:
9180169699Skan      return fold_builtin_isascii (arglist);
9181132727Skan
9182169699Skan    case BUILT_IN_TOASCII:
9183169699Skan      return fold_builtin_toascii (arglist);
9184132727Skan
9185169699Skan    case BUILT_IN_ISDIGIT:
9186169699Skan      return fold_builtin_isdigit (arglist);
9187169699Skan
9188169699Skan    CASE_FLT_FN (BUILT_IN_COPYSIGN):
9189169699Skan      return fold_builtin_copysign (fndecl, arglist, type);
9190169699Skan
9191169699Skan    CASE_FLT_FN (BUILT_IN_FINITE):
9192169699Skan    case BUILT_IN_FINITED32:
9193169699Skan    case BUILT_IN_FINITED64:
9194169699Skan    case BUILT_IN_FINITED128:
9195169699Skan      return fold_builtin_classify (fndecl, arglist, BUILT_IN_FINITE);
9196169699Skan
9197169699Skan    CASE_FLT_FN (BUILT_IN_ISINF):
9198169699Skan    case BUILT_IN_ISINFD32:
9199169699Skan    case BUILT_IN_ISINFD64:
9200169699Skan    case BUILT_IN_ISINFD128:
9201169699Skan      return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISINF);
9202169699Skan
9203169699Skan    CASE_FLT_FN (BUILT_IN_ISNAN):
9204169699Skan    case BUILT_IN_ISNAND32:
9205169699Skan    case BUILT_IN_ISNAND64:
9206169699Skan    case BUILT_IN_ISNAND128:
9207169699Skan      return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN);
9208169699Skan
9209169699Skan    case BUILT_IN_ISGREATER:
9210169699Skan      return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR);
9211169699Skan    case BUILT_IN_ISGREATEREQUAL:
9212169699Skan      return fold_builtin_unordered_cmp (fndecl, arglist, UNLT_EXPR, LT_EXPR);
9213169699Skan    case BUILT_IN_ISLESS:
9214169699Skan      return fold_builtin_unordered_cmp (fndecl, arglist, UNGE_EXPR, GE_EXPR);
9215169699Skan    case BUILT_IN_ISLESSEQUAL:
9216169699Skan      return fold_builtin_unordered_cmp (fndecl, arglist, UNGT_EXPR, GT_EXPR);
9217169699Skan    case BUILT_IN_ISLESSGREATER:
9218169699Skan      return fold_builtin_unordered_cmp (fndecl, arglist, UNEQ_EXPR, EQ_EXPR);
9219169699Skan    case BUILT_IN_ISUNORDERED:
9220169699Skan      return fold_builtin_unordered_cmp (fndecl, arglist, UNORDERED_EXPR,
9221169699Skan					 NOP_EXPR);
9222169699Skan
9223169699Skan      /* We do the folding for va_start in the expander.  */
9224169699Skan    case BUILT_IN_VA_START:
9225169699Skan      break;
9226169699Skan
9227169699Skan    case BUILT_IN_OBJECT_SIZE:
9228169699Skan      return fold_builtin_object_size (arglist);
9229169699Skan    case BUILT_IN_MEMCPY_CHK:
9230169699Skan    case BUILT_IN_MEMPCPY_CHK:
9231169699Skan    case BUILT_IN_MEMMOVE_CHK:
9232169699Skan    case BUILT_IN_MEMSET_CHK:
9233169699Skan      return fold_builtin_memory_chk (fndecl, arglist, NULL_TREE, ignore,
9234169699Skan				      DECL_FUNCTION_CODE (fndecl));
9235169699Skan    case BUILT_IN_STRCPY_CHK:
9236169699Skan    case BUILT_IN_STPCPY_CHK:
9237169699Skan      return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore,
9238169699Skan				      DECL_FUNCTION_CODE (fndecl));
9239169699Skan    case BUILT_IN_STRNCPY_CHK:
9240169699Skan      return fold_builtin_strncpy_chk (arglist, NULL_TREE);
9241169699Skan    case BUILT_IN_STRCAT_CHK:
9242169699Skan      return fold_builtin_strcat_chk (fndecl, arglist);
9243169699Skan    case BUILT_IN_STRNCAT_CHK:
9244169699Skan      return fold_builtin_strncat_chk (fndecl, arglist);
9245169699Skan    case BUILT_IN_SPRINTF_CHK:
9246169699Skan    case BUILT_IN_VSPRINTF_CHK:
9247169699Skan      return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl));
9248169699Skan    case BUILT_IN_SNPRINTF_CHK:
9249169699Skan    case BUILT_IN_VSNPRINTF_CHK:
9250169699Skan      return fold_builtin_snprintf_chk (arglist, NULL_TREE,
9251169699Skan					DECL_FUNCTION_CODE (fndecl));
9252169699Skan
9253169699Skan    case BUILT_IN_PRINTF:
9254169699Skan    case BUILT_IN_PRINTF_UNLOCKED:
9255169699Skan    case BUILT_IN_VPRINTF:
9256169699Skan    case BUILT_IN_PRINTF_CHK:
9257169699Skan    case BUILT_IN_VPRINTF_CHK:
9258169699Skan      return fold_builtin_printf (fndecl, arglist, ignore,
9259169699Skan				  DECL_FUNCTION_CODE (fndecl));
9260169699Skan
9261169699Skan    case BUILT_IN_FPRINTF:
9262169699Skan    case BUILT_IN_FPRINTF_UNLOCKED:
9263169699Skan    case BUILT_IN_VFPRINTF:
9264169699Skan    case BUILT_IN_FPRINTF_CHK:
9265169699Skan    case BUILT_IN_VFPRINTF_CHK:
9266169699Skan      return fold_builtin_fprintf (fndecl, arglist, ignore,
9267169699Skan				   DECL_FUNCTION_CODE (fndecl));
9268169699Skan
926990075Sobrien    default:
927090075Sobrien      break;
927190075Sobrien    }
927290075Sobrien
927390075Sobrien  return 0;
927490075Sobrien}
927590075Sobrien
9276169699Skan/* A wrapper function for builtin folding that prevents warnings for
9277169699Skan   "statement without effect" and the like, caused by removing the
9278169699Skan   call node earlier than the warning is generated.  */
9279169699Skan
9280169699Skantree
9281169699Skanfold_builtin (tree fndecl, tree arglist, bool ignore)
9282169699Skan{
9283169699Skan  tree exp = fold_builtin_1 (fndecl, arglist, ignore);
9284169699Skan  if (exp)
9285169699Skan    {
9286169699Skan      exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp);
9287169699Skan      TREE_NO_WARNING (exp) = 1;
9288169699Skan    }
9289169699Skan
9290169699Skan  return exp;
9291169699Skan}
9292169699Skan
9293132727Skan/* Conveniently construct a function call expression.  */
9294132727Skan
9295132727Skantree
9296132727Skanbuild_function_call_expr (tree fn, tree arglist)
929790075Sobrien{
929890075Sobrien  tree call_expr;
929990075Sobrien
930090075Sobrien  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
9301169699Skan  return fold_build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
9302169699Skan		      call_expr, arglist, NULL_TREE);
930390075Sobrien}
930490075Sobrien
930590075Sobrien/* This function validates the types of a function call argument list
930690075Sobrien   represented as a tree chain of parameters against a specified list
930790075Sobrien   of tree_codes.  If the last specifier is a 0, that represents an
930890075Sobrien   ellipses, otherwise the last specifier must be a VOID_TYPE.  */
930990075Sobrien
931090075Sobrienstatic int
9311132727Skanvalidate_arglist (tree arglist, ...)
931290075Sobrien{
931390075Sobrien  enum tree_code code;
931490075Sobrien  int res = 0;
9315132727Skan  va_list ap;
931690075Sobrien
9317132727Skan  va_start (ap, arglist);
931890075Sobrien
9319117404Skan  do
932090075Sobrien    {
9321117404Skan      code = va_arg (ap, enum tree_code);
9322117404Skan      switch (code)
9323117404Skan	{
9324117404Skan	case 0:
9325117404Skan	  /* This signifies an ellipses, any further arguments are all ok.  */
9326117404Skan	  res = 1;
9327117404Skan	  goto end;
9328117404Skan	case VOID_TYPE:
9329117404Skan	  /* This signifies an endlink, if no arguments remain, return
9330117404Skan	     true, otherwise return false.  */
9331117404Skan	  res = arglist == 0;
9332117404Skan	  goto end;
9333117404Skan	default:
9334117404Skan	  /* If no parameters remain or the parameter's code does not
9335117404Skan	     match the specified code, return false.  Otherwise continue
9336117404Skan	     checking any remaining arguments.  */
9337169699Skan	  if (arglist == 0)
9338117404Skan	    goto end;
9339169699Skan	  if (code == POINTER_TYPE)
9340169699Skan	    {
9341169699Skan	      if (! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))))
9342169699Skan		goto end;
9343169699Skan	    }
9344169699Skan	  else if (code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))))
9345169699Skan	    goto end;
9346117404Skan	  break;
9347117404Skan	}
9348117404Skan      arglist = TREE_CHAIN (arglist);
934990075Sobrien    }
9350117404Skan  while (1);
935190075Sobrien
935290075Sobrien  /* We need gotos here since we can only have one VA_CLOSE in a
935390075Sobrien     function.  */
935490075Sobrien end: ;
9355132727Skan  va_end (ap);
935690075Sobrien
935790075Sobrien  return res;
935890075Sobrien}
935990075Sobrien
9360132727Skan/* Default target-specific builtin expander that does nothing.  */
936190075Sobrien
9362132727Skanrtx
9363132727Skandefault_expand_builtin (tree exp ATTRIBUTE_UNUSED,
9364132727Skan			rtx target ATTRIBUTE_UNUSED,
9365132727Skan			rtx subtarget ATTRIBUTE_UNUSED,
9366132727Skan			enum machine_mode mode ATTRIBUTE_UNUSED,
9367132727Skan			int ignore ATTRIBUTE_UNUSED)
9368132727Skan{
9369132727Skan  return NULL_RTX;
9370132727Skan}
9371132727Skan
9372169699Skan/* Returns true is EXP represents data that would potentially reside
9373169699Skan   in a readonly section.  */
9374132727Skan
9375169699Skanstatic bool
9376169699Skanreadonly_data_expr (tree exp)
937790075Sobrien{
9378169699Skan  STRIP_NOPS (exp);
9379132727Skan
9380169699Skan  if (TREE_CODE (exp) != ADDR_EXPR)
9381169699Skan    return false;
9382169699Skan
9383169699Skan  exp = get_base_address (TREE_OPERAND (exp, 0));
9384169699Skan  if (!exp)
9385169699Skan    return false;
9386169699Skan
9387169699Skan  /* Make sure we call decl_readonly_section only for trees it
9388169699Skan     can handle (since it returns true for everything it doesn't
9389169699Skan     understand).  */
9390169699Skan  if (TREE_CODE (exp) == STRING_CST
9391169699Skan      || TREE_CODE (exp) == CONSTRUCTOR
9392169699Skan      || (TREE_CODE (exp) == VAR_DECL && TREE_STATIC (exp)))
9393169699Skan    return decl_readonly_section (exp, 0);
9394169699Skan  else
9395169699Skan    return false;
9396169699Skan}
9397169699Skan
9398169699Skan/* Simplify a call to the strstr builtin.
9399169699Skan
9400169699Skan   Return 0 if no simplification was possible, otherwise return the
9401169699Skan   simplified form of the call as a tree.
9402169699Skan
9403169699Skan   The simplified form may be a constant or other expression which
9404169699Skan   computes the same value, but in a more efficient manner (including
9405169699Skan   calls to other builtin functions).
9406169699Skan
9407169699Skan   The call may contain arguments which need to be evaluated, but
9408169699Skan   which are not useful to determine the result of the call.  In
9409169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9410169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9411169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9412169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9413169699Skan   form of the builtin function call.  */
9414169699Skan
9415169699Skanstatic tree
9416169699Skanfold_builtin_strstr (tree arglist, tree type)
9417169699Skan{
9418169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
9419169699Skan    return 0;
9420169699Skan  else
9421169699Skan    {
9422169699Skan      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
9423169699Skan      tree fn;
9424169699Skan      const char *p1, *p2;
9425169699Skan
9426169699Skan      p2 = c_getstr (s2);
9427169699Skan      if (p2 == NULL)
9428169699Skan	return 0;
9429169699Skan
9430169699Skan      p1 = c_getstr (s1);
9431169699Skan      if (p1 != NULL)
9432169699Skan	{
9433169699Skan	  const char *r = strstr (p1, p2);
9434169699Skan	  tree tem;
9435169699Skan
9436169699Skan	  if (r == NULL)
9437169699Skan	    return build_int_cst (TREE_TYPE (s1), 0);
9438169699Skan
9439169699Skan	  /* Return an offset into the constant string argument.  */
9440169699Skan	  tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1),
9441169699Skan			     s1, build_int_cst (TREE_TYPE (s1), r - p1));
9442169699Skan	  return fold_convert (type, tem);
9443169699Skan	}
9444169699Skan
9445169699Skan      /* The argument is const char *, and the result is char *, so we need
9446169699Skan	 a type conversion here to avoid a warning.  */
9447169699Skan      if (p2[0] == '\0')
9448169699Skan	return fold_convert (type, s1);
9449169699Skan
9450169699Skan      if (p2[1] != '\0')
9451169699Skan	return 0;
9452169699Skan
9453169699Skan      fn = implicit_built_in_decls[BUILT_IN_STRCHR];
9454169699Skan      if (!fn)
9455169699Skan	return 0;
9456169699Skan
9457169699Skan      /* New argument list transforming strstr(s1, s2) to
9458169699Skan	 strchr(s1, s2[0]).  */
9459169699Skan      arglist = build_tree_list (NULL_TREE,
9460169699Skan				 build_int_cst (NULL_TREE, p2[0]));
9461169699Skan      arglist = tree_cons (NULL_TREE, s1, arglist);
9462169699Skan      return build_function_call_expr (fn, arglist);
9463169699Skan    }
9464169699Skan}
9465169699Skan
9466169699Skan/* Simplify a call to the strchr builtin.
9467169699Skan
9468169699Skan   Return 0 if no simplification was possible, otherwise return the
9469169699Skan   simplified form of the call as a tree.
9470169699Skan
9471169699Skan   The simplified form may be a constant or other expression which
9472169699Skan   computes the same value, but in a more efficient manner (including
9473169699Skan   calls to other builtin functions).
9474169699Skan
9475169699Skan   The call may contain arguments which need to be evaluated, but
9476169699Skan   which are not useful to determine the result of the call.  In
9477169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9478169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9479169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9480169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9481169699Skan   form of the builtin function call.  */
9482169699Skan
9483169699Skanstatic tree
9484169699Skanfold_builtin_strchr (tree arglist, tree type)
9485169699Skan{
9486169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
9487169699Skan    return 0;
9488169699Skan  else
9489169699Skan    {
9490169699Skan      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
9491169699Skan      const char *p1;
9492169699Skan
9493169699Skan      if (TREE_CODE (s2) != INTEGER_CST)
9494169699Skan	return 0;
9495169699Skan
9496169699Skan      p1 = c_getstr (s1);
9497169699Skan      if (p1 != NULL)
9498169699Skan	{
9499169699Skan	  char c;
9500169699Skan	  const char *r;
9501169699Skan	  tree tem;
9502169699Skan
9503169699Skan	  if (target_char_cast (s2, &c))
9504169699Skan	    return 0;
9505169699Skan
9506169699Skan	  r = strchr (p1, c);
9507169699Skan
9508169699Skan	  if (r == NULL)
9509169699Skan	    return build_int_cst (TREE_TYPE (s1), 0);
9510169699Skan
9511169699Skan	  /* Return an offset into the constant string argument.  */
9512169699Skan	  tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1),
9513169699Skan			     s1, build_int_cst (TREE_TYPE (s1), r - p1));
9514169699Skan	  return fold_convert (type, tem);
9515169699Skan	}
9516169699Skan      return 0;
9517169699Skan    }
9518169699Skan}
9519169699Skan
9520169699Skan/* Simplify a call to the strrchr builtin.
9521169699Skan
9522169699Skan   Return 0 if no simplification was possible, otherwise return the
9523169699Skan   simplified form of the call as a tree.
9524169699Skan
9525169699Skan   The simplified form may be a constant or other expression which
9526169699Skan   computes the same value, but in a more efficient manner (including
9527169699Skan   calls to other builtin functions).
9528169699Skan
9529169699Skan   The call may contain arguments which need to be evaluated, but
9530169699Skan   which are not useful to determine the result of the call.  In
9531169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9532169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9533169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9534169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9535169699Skan   form of the builtin function call.  */
9536169699Skan
9537169699Skanstatic tree
9538169699Skanfold_builtin_strrchr (tree arglist, tree type)
9539169699Skan{
9540169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
9541169699Skan    return 0;
9542169699Skan  else
9543169699Skan    {
9544169699Skan      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
9545169699Skan      tree fn;
9546169699Skan      const char *p1;
9547169699Skan
9548169699Skan      if (TREE_CODE (s2) != INTEGER_CST)
9549169699Skan	return 0;
9550169699Skan
9551169699Skan      p1 = c_getstr (s1);
9552169699Skan      if (p1 != NULL)
9553169699Skan	{
9554169699Skan	  char c;
9555169699Skan	  const char *r;
9556169699Skan	  tree tem;
9557169699Skan
9558169699Skan	  if (target_char_cast (s2, &c))
9559169699Skan	    return 0;
9560169699Skan
9561169699Skan	  r = strrchr (p1, c);
9562169699Skan
9563169699Skan	  if (r == NULL)
9564169699Skan	    return build_int_cst (TREE_TYPE (s1), 0);
9565169699Skan
9566169699Skan	  /* Return an offset into the constant string argument.  */
9567169699Skan	  tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1),
9568169699Skan			     s1, build_int_cst (TREE_TYPE (s1), r - p1));
9569169699Skan	  return fold_convert (type, tem);
9570169699Skan	}
9571169699Skan
9572169699Skan      if (! integer_zerop (s2))
9573169699Skan	return 0;
9574169699Skan
9575169699Skan      fn = implicit_built_in_decls[BUILT_IN_STRCHR];
9576169699Skan      if (!fn)
9577169699Skan	return 0;
9578169699Skan
9579169699Skan      /* Transform strrchr(s1, '\0') to strchr(s1, '\0').  */
9580169699Skan      return build_function_call_expr (fn, arglist);
9581169699Skan    }
9582169699Skan}
9583169699Skan
9584169699Skan/* Simplify a call to the strpbrk builtin.
9585169699Skan
9586169699Skan   Return 0 if no simplification was possible, otherwise return the
9587169699Skan   simplified form of the call as a tree.
9588169699Skan
9589169699Skan   The simplified form may be a constant or other expression which
9590169699Skan   computes the same value, but in a more efficient manner (including
9591169699Skan   calls to other builtin functions).
9592169699Skan
9593169699Skan   The call may contain arguments which need to be evaluated, but
9594169699Skan   which are not useful to determine the result of the call.  In
9595169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9596169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9597169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9598169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9599169699Skan   form of the builtin function call.  */
9600169699Skan
9601169699Skanstatic tree
9602169699Skanfold_builtin_strpbrk (tree arglist, tree type)
9603169699Skan{
9604169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
9605169699Skan    return 0;
9606169699Skan  else
9607169699Skan    {
9608169699Skan      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
9609169699Skan      tree fn;
9610169699Skan      const char *p1, *p2;
9611169699Skan
9612169699Skan      p2 = c_getstr (s2);
9613169699Skan      if (p2 == NULL)
9614169699Skan	return 0;
9615169699Skan
9616169699Skan      p1 = c_getstr (s1);
9617169699Skan      if (p1 != NULL)
9618169699Skan	{
9619169699Skan	  const char *r = strpbrk (p1, p2);
9620169699Skan	  tree tem;
9621169699Skan
9622169699Skan	  if (r == NULL)
9623169699Skan	    return build_int_cst (TREE_TYPE (s1), 0);
9624169699Skan
9625169699Skan	  /* Return an offset into the constant string argument.  */
9626169699Skan	  tem = fold_build2 (PLUS_EXPR, TREE_TYPE (s1),
9627169699Skan			     s1, build_int_cst (TREE_TYPE (s1), r - p1));
9628169699Skan	  return fold_convert (type, tem);
9629169699Skan	}
9630169699Skan
9631169699Skan      if (p2[0] == '\0')
9632169699Skan	/* strpbrk(x, "") == NULL.
9633169699Skan	   Evaluate and ignore s1 in case it had side-effects.  */
9634169699Skan	return omit_one_operand (TREE_TYPE (s1), integer_zero_node, s1);
9635169699Skan
9636169699Skan      if (p2[1] != '\0')
9637169699Skan	return 0;  /* Really call strpbrk.  */
9638169699Skan
9639169699Skan      fn = implicit_built_in_decls[BUILT_IN_STRCHR];
9640169699Skan      if (!fn)
9641169699Skan	return 0;
9642169699Skan
9643169699Skan      /* New argument list transforming strpbrk(s1, s2) to
9644169699Skan	 strchr(s1, s2[0]).  */
9645169699Skan      arglist = build_tree_list (NULL_TREE,
9646169699Skan				 build_int_cst (NULL_TREE, p2[0]));
9647169699Skan      arglist = tree_cons (NULL_TREE, s1, arglist);
9648169699Skan      return build_function_call_expr (fn, arglist);
9649169699Skan    }
9650169699Skan}
9651169699Skan
9652169699Skan/* Simplify a call to the strcat builtin.
9653169699Skan
9654169699Skan   Return 0 if no simplification was possible, otherwise return the
9655169699Skan   simplified form of the call as a tree.
9656169699Skan
9657169699Skan   The simplified form may be a constant or other expression which
9658169699Skan   computes the same value, but in a more efficient manner (including
9659169699Skan   calls to other builtin functions).
9660169699Skan
9661169699Skan   The call may contain arguments which need to be evaluated, but
9662169699Skan   which are not useful to determine the result of the call.  In
9663169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9664169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9665169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9666169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9667169699Skan   form of the builtin function call.  */
9668169699Skan
9669169699Skanstatic tree
9670169699Skanfold_builtin_strcat (tree arglist)
9671169699Skan{
9672169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
9673169699Skan    return 0;
9674169699Skan  else
9675169699Skan    {
9676169699Skan      tree dst = TREE_VALUE (arglist),
9677169699Skan	src = TREE_VALUE (TREE_CHAIN (arglist));
9678169699Skan      const char *p = c_getstr (src);
9679169699Skan
9680169699Skan      /* If the string length is zero, return the dst parameter.  */
9681169699Skan      if (p && *p == '\0')
9682169699Skan	return dst;
9683169699Skan
9684169699Skan      return 0;
9685169699Skan    }
9686169699Skan}
9687169699Skan
9688169699Skan/* Simplify a call to the strncat builtin.
9689169699Skan
9690169699Skan   Return 0 if no simplification was possible, otherwise return the
9691169699Skan   simplified form of the call as a tree.
9692169699Skan
9693169699Skan   The simplified form may be a constant or other expression which
9694169699Skan   computes the same value, but in a more efficient manner (including
9695169699Skan   calls to other builtin functions).
9696169699Skan
9697169699Skan   The call may contain arguments which need to be evaluated, but
9698169699Skan   which are not useful to determine the result of the call.  In
9699169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9700169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9701169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9702169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9703169699Skan   form of the builtin function call.  */
9704169699Skan
9705169699Skanstatic tree
9706169699Skanfold_builtin_strncat (tree arglist)
9707169699Skan{
9708169699Skan  if (!validate_arglist (arglist,
9709169699Skan			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
9710169699Skan    return 0;
9711169699Skan  else
9712169699Skan    {
9713169699Skan      tree dst = TREE_VALUE (arglist);
9714169699Skan      tree src = TREE_VALUE (TREE_CHAIN (arglist));
9715169699Skan      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
9716169699Skan      const char *p = c_getstr (src);
9717169699Skan
9718169699Skan      /* If the requested length is zero, or the src parameter string
9719169699Skan	 length is zero, return the dst parameter.  */
9720169699Skan      if (integer_zerop (len) || (p && *p == '\0'))
9721169699Skan	return omit_two_operands (TREE_TYPE (dst), dst, src, len);
9722169699Skan
9723169699Skan      /* If the requested len is greater than or equal to the string
9724169699Skan	 length, call strcat.  */
9725169699Skan      if (TREE_CODE (len) == INTEGER_CST && p
9726169699Skan	  && compare_tree_int (len, strlen (p)) >= 0)
9727169699Skan	{
9728169699Skan	  tree newarglist
9729169699Skan	    = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src));
9730169699Skan	  tree fn = implicit_built_in_decls[BUILT_IN_STRCAT];
9731169699Skan
9732169699Skan	  /* If the replacement _DECL isn't initialized, don't do the
9733169699Skan	     transformation.  */
9734169699Skan	  if (!fn)
9735169699Skan	    return 0;
9736169699Skan
9737169699Skan	  return build_function_call_expr (fn, newarglist);
9738169699Skan	}
9739169699Skan      return 0;
9740169699Skan    }
9741169699Skan}
9742169699Skan
9743169699Skan/* Simplify a call to the strspn builtin.
9744169699Skan
9745169699Skan   Return 0 if no simplification was possible, otherwise return the
9746169699Skan   simplified form of the call as a tree.
9747169699Skan
9748169699Skan   The simplified form may be a constant or other expression which
9749169699Skan   computes the same value, but in a more efficient manner (including
9750169699Skan   calls to other builtin functions).
9751169699Skan
9752169699Skan   The call may contain arguments which need to be evaluated, but
9753169699Skan   which are not useful to determine the result of the call.  In
9754169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9755169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9756169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9757169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9758169699Skan   form of the builtin function call.  */
9759169699Skan
9760169699Skanstatic tree
9761169699Skanfold_builtin_strspn (tree arglist)
9762169699Skan{
9763169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
9764169699Skan    return 0;
9765169699Skan  else
9766169699Skan    {
9767169699Skan      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
9768169699Skan      const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
9769169699Skan
9770169699Skan      /* If both arguments are constants, evaluate at compile-time.  */
9771169699Skan      if (p1 && p2)
9772169699Skan	{
9773169699Skan	  const size_t r = strspn (p1, p2);
9774169699Skan	  return size_int (r);
9775169699Skan	}
9776169699Skan
9777169699Skan      /* If either argument is "", return 0.  */
9778169699Skan      if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
9779169699Skan	/* Evaluate and ignore both arguments in case either one has
9780169699Skan	   side-effects.  */
9781169699Skan	return omit_two_operands (integer_type_node, integer_zero_node,
9782169699Skan				  s1, s2);
9783169699Skan      return 0;
9784169699Skan    }
9785169699Skan}
9786169699Skan
9787169699Skan/* Simplify a call to the strcspn builtin.
9788169699Skan
9789169699Skan   Return 0 if no simplification was possible, otherwise return the
9790169699Skan   simplified form of the call as a tree.
9791169699Skan
9792169699Skan   The simplified form may be a constant or other expression which
9793169699Skan   computes the same value, but in a more efficient manner (including
9794169699Skan   calls to other builtin functions).
9795169699Skan
9796169699Skan   The call may contain arguments which need to be evaluated, but
9797169699Skan   which are not useful to determine the result of the call.  In
9798169699Skan   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
9799169699Skan   COMPOUND_EXPR will be an argument which must be evaluated.
9800169699Skan   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
9801169699Skan   COMPOUND_EXPR in the chain will contain the tree for the simplified
9802169699Skan   form of the builtin function call.  */
9803169699Skan
9804169699Skanstatic tree
9805169699Skanfold_builtin_strcspn (tree arglist)
9806169699Skan{
9807169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
9808169699Skan    return 0;
9809169699Skan  else
9810169699Skan    {
9811169699Skan      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
9812169699Skan      const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
9813169699Skan
9814169699Skan      /* If both arguments are constants, evaluate at compile-time.  */
9815169699Skan      if (p1 && p2)
9816169699Skan	{
9817169699Skan	  const size_t r = strcspn (p1, p2);
9818169699Skan	  return size_int (r);
9819169699Skan	}
9820169699Skan
9821169699Skan      /* If the first argument is "", return 0.  */
9822169699Skan      if (p1 && *p1 == '\0')
9823169699Skan	{
9824169699Skan	  /* Evaluate and ignore argument s2 in case it has
9825169699Skan	     side-effects.  */
9826169699Skan	  return omit_one_operand (integer_type_node,
9827169699Skan				   integer_zero_node, s2);
9828169699Skan	}
9829169699Skan
9830169699Skan      /* If the second argument is "", return __builtin_strlen(s1).  */
9831169699Skan      if (p2 && *p2 == '\0')
9832169699Skan	{
9833169699Skan	  tree newarglist = build_tree_list (NULL_TREE, s1),
9834169699Skan	    fn = implicit_built_in_decls[BUILT_IN_STRLEN];
9835169699Skan
9836169699Skan	  /* If the replacement _DECL isn't initialized, don't do the
9837169699Skan	     transformation.  */
9838169699Skan	  if (!fn)
9839169699Skan	    return 0;
9840169699Skan
9841169699Skan	  return build_function_call_expr (fn, newarglist);
9842169699Skan	}
9843169699Skan      return 0;
9844169699Skan    }
9845169699Skan}
9846169699Skan
9847169699Skan/* Fold a call to the fputs builtin.  IGNORE is true if the value returned
9848169699Skan   by the builtin will be ignored.  UNLOCKED is true is true if this
9849169699Skan   actually a call to fputs_unlocked.  If LEN in non-NULL, it represents
9850169699Skan   the known length of the string.  Return NULL_TREE if no simplification
9851169699Skan   was possible.  */
9852169699Skan
9853169699Skantree
9854169699Skanfold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len)
9855169699Skan{
9856169699Skan  tree fn;
9857169699Skan  /* If we're using an unlocked function, assume the other unlocked
9858169699Skan     functions exist explicitly.  */
9859169699Skan  tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
9860169699Skan    : implicit_built_in_decls[BUILT_IN_FPUTC];
9861169699Skan  tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED]
9862169699Skan    : implicit_built_in_decls[BUILT_IN_FWRITE];
9863169699Skan
9864169699Skan  /* If the return value is used, don't do the transformation.  */
9865169699Skan  if (!ignore)
9866169699Skan    return 0;
9867169699Skan
9868169699Skan  /* Verify the arguments in the original call.  */
9869169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
9870169699Skan    return 0;
9871169699Skan
9872169699Skan  if (! len)
9873169699Skan    len = c_strlen (TREE_VALUE (arglist), 0);
9874169699Skan
9875169699Skan  /* Get the length of the string passed to fputs.  If the length
9876169699Skan     can't be determined, punt.  */
9877169699Skan  if (!len
9878169699Skan      || TREE_CODE (len) != INTEGER_CST)
9879169699Skan    return 0;
9880169699Skan
9881169699Skan  switch (compare_tree_int (len, 1))
9882169699Skan    {
9883169699Skan    case -1: /* length is 0, delete the call entirely .  */
9884169699Skan      return omit_one_operand (integer_type_node, integer_zero_node,
9885169699Skan			       TREE_VALUE (TREE_CHAIN (arglist)));
9886169699Skan
9887169699Skan    case 0: /* length is 1, call fputc.  */
9888132727Skan      {
9889169699Skan	const char *p = c_getstr (TREE_VALUE (arglist));
9890132727Skan
9891169699Skan	if (p != NULL)
9892169699Skan	  {
9893169699Skan	    /* New argument list transforming fputs(string, stream) to
9894169699Skan	       fputc(string[0], stream).  */
9895169699Skan	    arglist = build_tree_list (NULL_TREE,
9896169699Skan				       TREE_VALUE (TREE_CHAIN (arglist)));
9897169699Skan	    arglist = tree_cons (NULL_TREE,
9898169699Skan				 build_int_cst (NULL_TREE, p[0]),
9899169699Skan				 arglist);
9900169699Skan	    fn = fn_fputc;
9901169699Skan	    break;
9902169699Skan	  }
9903132727Skan      }
9904169699Skan      /* FALLTHROUGH */
9905169699Skan    case 1: /* length is greater than 1, call fwrite.  */
9906169699Skan      {
9907169699Skan	tree string_arg;
9908169699Skan
9909169699Skan	/* If optimizing for size keep fputs.  */
9910169699Skan	if (optimize_size)
9911169699Skan	  return 0;
9912169699Skan	string_arg = TREE_VALUE (arglist);
9913169699Skan	/* New argument list transforming fputs(string, stream) to
9914169699Skan	   fwrite(string, 1, len, stream).  */
9915169699Skan	arglist = build_tree_list (NULL_TREE,
9916169699Skan				   TREE_VALUE (TREE_CHAIN (arglist)));
9917169699Skan	arglist = tree_cons (NULL_TREE, len, arglist);
9918169699Skan	arglist = tree_cons (NULL_TREE, size_one_node, arglist);
9919169699Skan	arglist = tree_cons (NULL_TREE, string_arg, arglist);
9920169699Skan	fn = fn_fwrite;
9921169699Skan	break;
9922169699Skan      }
9923169699Skan    default:
9924169699Skan      gcc_unreachable ();
9925169699Skan    }
9926169699Skan
9927169699Skan  /* If the replacement _DECL isn't initialized, don't do the
9928169699Skan     transformation.  */
9929169699Skan  if (!fn)
9930169699Skan    return 0;
9931169699Skan
9932169699Skan  /* These optimizations are only performed when the result is ignored,
9933169699Skan     hence there's no need to cast the result to integer_type_node.  */
9934169699Skan  return build_function_call_expr (fn, arglist);
993590075Sobrien}
993690075Sobrien
9937169699Skan/* Fold the new_arg's arguments (ARGLIST). Returns true if there was an error
9938169699Skan   produced.  False otherwise.  This is done so that we don't output the error
9939169699Skan   or warning twice or three times.  */
9940169699Skanbool
9941169699Skanfold_builtin_next_arg (tree arglist)
9942169699Skan{
9943169699Skan  tree fntype = TREE_TYPE (current_function_decl);
994490075Sobrien
9945169699Skan  if (TYPE_ARG_TYPES (fntype) == 0
9946169699Skan      || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
9947169699Skan	  == void_type_node))
9948169699Skan    {
9949169699Skan      error ("%<va_start%> used in function with fixed args");
9950169699Skan      return true;
9951169699Skan    }
9952169699Skan  else if (!arglist)
9953169699Skan    {
9954169699Skan      /* Evidently an out of date version of <stdarg.h>; can't validate
9955169699Skan	 va_start's second argument, but can still work as intended.  */
9956169699Skan      warning (0, "%<__builtin_next_arg%> called without an argument");
9957169699Skan      return true;
9958169699Skan    }
9959169699Skan  /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
9960169699Skan     when we checked the arguments and if needed issued a warning.  */
9961169699Skan  else if (!TREE_CHAIN (arglist)
9962169699Skan	   || !integer_zerop (TREE_VALUE (arglist))
9963169699Skan	   || !integer_zerop (TREE_VALUE (TREE_CHAIN (arglist)))
9964169699Skan	   || TREE_CHAIN (TREE_CHAIN (arglist)))
9965169699Skan    {
9966169699Skan      tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
9967169699Skan      tree arg = TREE_VALUE (arglist);
9968169699Skan
9969169699Skan      if (TREE_CHAIN (arglist))
9970169699Skan	{
9971169699Skan	  error ("%<va_start%> used with too many arguments");
9972169699Skan	  return true;
9973169699Skan	}
9974169699Skan
9975169699Skan      /* Strip off all nops for the sake of the comparison.  This
9976169699Skan	 is not quite the same as STRIP_NOPS.  It does more.
9977169699Skan	 We must also strip off INDIRECT_EXPR for C++ reference
9978169699Skan	 parameters.  */
9979169699Skan      while (TREE_CODE (arg) == NOP_EXPR
9980169699Skan	     || TREE_CODE (arg) == CONVERT_EXPR
9981169699Skan	     || TREE_CODE (arg) == NON_LVALUE_EXPR
9982169699Skan	     || TREE_CODE (arg) == INDIRECT_REF)
9983169699Skan	arg = TREE_OPERAND (arg, 0);
9984169699Skan      if (arg != last_parm)
9985169699Skan	{
9986169699Skan	  /* FIXME: Sometimes with the tree optimizers we can get the
9987169699Skan	     not the last argument even though the user used the last
9988169699Skan	     argument.  We just warn and set the arg to be the last
9989169699Skan	     argument so that we will get wrong-code because of
9990169699Skan	     it.  */
9991169699Skan	  warning (0, "second parameter of %<va_start%> not last named argument");
9992169699Skan	}
9993169699Skan      /* We want to verify the second parameter just once before the tree
9994169699Skan	 optimizers are run and then avoid keeping it in the tree,
9995169699Skan	 as otherwise we could warn even for correct code like:
9996169699Skan	 void foo (int i, ...)
9997169699Skan	 { va_list ap; i++; va_start (ap, i); va_end (ap); }  */
9998169699Skan      TREE_VALUE (arglist) = integer_zero_node;
9999169699Skan      TREE_CHAIN (arglist) = build_tree_list (NULL, integer_zero_node);
10000169699Skan    }
10001169699Skan  return false;
10002169699Skan}
10003169699Skan
10004169699Skan
10005169699Skan/* Simplify a call to the sprintf builtin.
10006169699Skan
10007169699Skan   Return 0 if no simplification was possible, otherwise return the
10008169699Skan   simplified form of the call as a tree.  If IGNORED is true, it means that
10009169699Skan   the caller does not use the returned value of the function.  */
10010169699Skan
10011169699Skanstatic tree
10012169699Skanfold_builtin_sprintf (tree arglist, int ignored)
1001390075Sobrien{
10014169699Skan  tree call, retval, dest, fmt;
10015169699Skan  const char *fmt_str = NULL;
10016132727Skan
10017169699Skan  /* Verify the required arguments in the original call.  We deal with two
10018169699Skan     types of sprintf() calls: 'sprintf (str, fmt)' and
10019169699Skan     'sprintf (dest, "%s", orig)'.  */
10020169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
10021169699Skan      && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
10022169699Skan			    VOID_TYPE))
10023169699Skan    return NULL_TREE;
10024169699Skan
10025169699Skan  /* Get the destination string and the format specifier.  */
10026169699Skan  dest = TREE_VALUE (arglist);
10027169699Skan  fmt = TREE_VALUE (TREE_CHAIN (arglist));
10028169699Skan  arglist = TREE_CHAIN (TREE_CHAIN (arglist));
10029169699Skan
10030169699Skan  /* Check whether the format is a literal string constant.  */
10031169699Skan  fmt_str = c_getstr (fmt);
10032169699Skan  if (fmt_str == NULL)
10033169699Skan    return NULL_TREE;
10034169699Skan
10035169699Skan  call = NULL_TREE;
10036169699Skan  retval = NULL_TREE;
10037169699Skan
10038169699Skan  if (!init_target_chars())
10039169699Skan    return 0;
10040169699Skan
10041169699Skan  /* If the format doesn't contain % args or %%, use strcpy.  */
10042169699Skan  if (strchr (fmt_str, target_percent) == NULL)
10043169699Skan    {
10044169699Skan      tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
10045169699Skan
10046169699Skan      if (!fn)
10047169699Skan	return NULL_TREE;
10048169699Skan
10049169699Skan      /* Don't optimize sprintf (buf, "abc", ptr++).  */
10050169699Skan      if (arglist)
10051169699Skan	return NULL_TREE;
10052169699Skan
10053169699Skan      /* Convert sprintf (str, fmt) into strcpy (str, fmt) when
10054169699Skan	 'format' is known to contain no % formats.  */
10055169699Skan      arglist = build_tree_list (NULL_TREE, fmt);
10056169699Skan      arglist = tree_cons (NULL_TREE, dest, arglist);
10057169699Skan      call = build_function_call_expr (fn, arglist);
10058169699Skan      if (!ignored)
10059169699Skan	retval = build_int_cst (NULL_TREE, strlen (fmt_str));
10060169699Skan    }
10061169699Skan
10062169699Skan  /* If the format is "%s", use strcpy if the result isn't used.  */
10063169699Skan  else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
10064169699Skan    {
10065169699Skan      tree fn, orig;
10066169699Skan      fn = implicit_built_in_decls[BUILT_IN_STRCPY];
10067169699Skan
10068169699Skan      if (!fn)
10069169699Skan	return NULL_TREE;
10070169699Skan
10071169699Skan      /* Don't crash on sprintf (str1, "%s").  */
10072169699Skan      if (!arglist)
10073169699Skan	return NULL_TREE;
10074169699Skan
10075169699Skan      /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2).  */
10076169699Skan      orig = TREE_VALUE (arglist);
10077169699Skan      arglist = build_tree_list (NULL_TREE, orig);
10078169699Skan      arglist = tree_cons (NULL_TREE, dest, arglist);
10079169699Skan      if (!ignored)
10080169699Skan	{
10081169699Skan	  retval = c_strlen (orig, 1);
10082169699Skan	  if (!retval || TREE_CODE (retval) != INTEGER_CST)
10083169699Skan	    return NULL_TREE;
10084169699Skan	}
10085169699Skan      call = build_function_call_expr (fn, arglist);
10086169699Skan    }
10087169699Skan
10088169699Skan  if (call && retval)
10089169699Skan    {
10090169699Skan      retval = fold_convert
10091169699Skan	(TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SPRINTF])),
10092169699Skan	 retval);
10093169699Skan      return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval);
10094169699Skan    }
10095132727Skan  else
10096169699Skan    return call;
1009790075Sobrien}
10098169699Skan
10099169699Skan/* Expand a call to __builtin_object_size.  */
10100169699Skan
10101169699Skanrtx
10102169699Skanexpand_builtin_object_size (tree exp)
10103169699Skan{
10104169699Skan  tree ost;
10105169699Skan  int object_size_type;
10106169699Skan  tree fndecl = get_callee_fndecl (exp);
10107169699Skan  tree arglist = TREE_OPERAND (exp, 1);
10108169699Skan  location_t locus = EXPR_LOCATION (exp);
10109169699Skan
10110169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
10111169699Skan    {
10112169699Skan      error ("%Hfirst argument of %D must be a pointer, second integer constant",
10113169699Skan	     &locus, fndecl);
10114169699Skan      expand_builtin_trap ();
10115169699Skan      return const0_rtx;
10116169699Skan    }
10117169699Skan
10118169699Skan  ost = TREE_VALUE (TREE_CHAIN (arglist));
10119169699Skan  STRIP_NOPS (ost);
10120169699Skan
10121169699Skan  if (TREE_CODE (ost) != INTEGER_CST
10122169699Skan      || tree_int_cst_sgn (ost) < 0
10123169699Skan      || compare_tree_int (ost, 3) > 0)
10124169699Skan    {
10125169699Skan      error ("%Hlast argument of %D is not integer constant between 0 and 3",
10126169699Skan	     &locus, fndecl);
10127169699Skan      expand_builtin_trap ();
10128169699Skan      return const0_rtx;
10129169699Skan    }
10130169699Skan
10131169699Skan  object_size_type = tree_low_cst (ost, 0);
10132169699Skan
10133169699Skan  return object_size_type < 2 ? constm1_rtx : const0_rtx;
10134169699Skan}
10135169699Skan
10136169699Skan/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
10137169699Skan   FCODE is the BUILT_IN_* to use.
10138169699Skan   Return 0 if we failed; the caller should emit a normal call,
10139169699Skan   otherwise try to get the result in TARGET, if convenient (and in
10140169699Skan   mode MODE if that's convenient).  */
10141169699Skan
10142169699Skanstatic rtx
10143169699Skanexpand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode,
10144169699Skan			   enum built_in_function fcode)
10145169699Skan{
10146169699Skan  tree arglist = TREE_OPERAND (exp, 1);
10147169699Skan  tree dest, src, len, size;
10148169699Skan
10149169699Skan  if (!validate_arglist (arglist,
10150169699Skan			 POINTER_TYPE,
10151169699Skan			 fcode == BUILT_IN_MEMSET_CHK
10152169699Skan			 ? INTEGER_TYPE : POINTER_TYPE,
10153169699Skan			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
10154169699Skan    return 0;
10155169699Skan
10156169699Skan  dest = TREE_VALUE (arglist);
10157169699Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
10158169699Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
10159169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
10160169699Skan
10161169699Skan  if (! host_integerp (size, 1))
10162169699Skan    return 0;
10163169699Skan
10164169699Skan  if (host_integerp (len, 1) || integer_all_onesp (size))
10165169699Skan    {
10166169699Skan      tree fn;
10167169699Skan
10168169699Skan      if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
10169169699Skan	{
10170169699Skan	  location_t locus = EXPR_LOCATION (exp);
10171169699Skan	  warning (0, "%Hcall to %D will always overflow destination buffer",
10172169699Skan		   &locus, get_callee_fndecl (exp));
10173169699Skan	  return 0;
10174169699Skan	}
10175169699Skan
10176169699Skan      arglist = build_tree_list (NULL_TREE, len);
10177169699Skan      arglist = tree_cons (NULL_TREE, src, arglist);
10178169699Skan      arglist = tree_cons (NULL_TREE, dest, arglist);
10179169699Skan
10180169699Skan      fn = NULL_TREE;
10181169699Skan      /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
10182169699Skan	 mem{cpy,pcpy,move,set} is available.  */
10183169699Skan      switch (fcode)
10184169699Skan	{
10185169699Skan	case BUILT_IN_MEMCPY_CHK:
10186169699Skan	  fn = built_in_decls[BUILT_IN_MEMCPY];
10187169699Skan	  break;
10188169699Skan	case BUILT_IN_MEMPCPY_CHK:
10189169699Skan	  fn = built_in_decls[BUILT_IN_MEMPCPY];
10190169699Skan	  break;
10191169699Skan	case BUILT_IN_MEMMOVE_CHK:
10192169699Skan	  fn = built_in_decls[BUILT_IN_MEMMOVE];
10193169699Skan	  break;
10194169699Skan	case BUILT_IN_MEMSET_CHK:
10195169699Skan	  fn = built_in_decls[BUILT_IN_MEMSET];
10196169699Skan	  break;
10197169699Skan	default:
10198169699Skan	  break;
10199169699Skan	}
10200169699Skan
10201169699Skan      if (! fn)
10202169699Skan	return 0;
10203169699Skan
10204169699Skan      fn = build_function_call_expr (fn, arglist);
10205169699Skan      if (TREE_CODE (fn) == CALL_EXPR)
10206169699Skan	CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
10207169699Skan      return expand_expr (fn, target, mode, EXPAND_NORMAL);
10208169699Skan    }
10209169699Skan  else if (fcode == BUILT_IN_MEMSET_CHK)
10210169699Skan    return 0;
10211169699Skan  else
10212169699Skan    {
10213169699Skan      unsigned int dest_align
10214169699Skan	= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
10215169699Skan
10216169699Skan      /* If DEST is not a pointer type, call the normal function.  */
10217169699Skan      if (dest_align == 0)
10218169699Skan	return 0;
10219169699Skan
10220169699Skan      /* If SRC and DEST are the same (and not volatile), do nothing.  */
10221169699Skan      if (operand_equal_p (src, dest, 0))
10222169699Skan	{
10223169699Skan	  tree expr;
10224169699Skan
10225169699Skan	  if (fcode != BUILT_IN_MEMPCPY_CHK)
10226169699Skan	    {
10227169699Skan	      /* Evaluate and ignore LEN in case it has side-effects.  */
10228169699Skan	      expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
10229169699Skan	      return expand_expr (dest, target, mode, EXPAND_NORMAL);
10230169699Skan	    }
10231169699Skan
10232169699Skan	  len = fold_convert (TREE_TYPE (dest), len);
10233169699Skan	  expr = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len);
10234169699Skan	  return expand_expr (expr, target, mode, EXPAND_NORMAL);
10235169699Skan	}
10236169699Skan
10237169699Skan      /* __memmove_chk special case.  */
10238169699Skan      if (fcode == BUILT_IN_MEMMOVE_CHK)
10239169699Skan	{
10240169699Skan	  unsigned int src_align
10241169699Skan	    = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
10242169699Skan
10243169699Skan	  if (src_align == 0)
10244169699Skan	    return 0;
10245169699Skan
10246169699Skan	  /* If src is categorized for a readonly section we can use
10247169699Skan	     normal __memcpy_chk.  */
10248169699Skan	  if (readonly_data_expr (src))
10249169699Skan	    {
10250169699Skan	      tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
10251169699Skan	      if (!fn)
10252169699Skan		return 0;
10253169699Skan	      fn = build_function_call_expr (fn, arglist);
10254169699Skan	      if (TREE_CODE (fn) == CALL_EXPR)
10255169699Skan		CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
10256169699Skan	      return expand_expr (fn, target, mode, EXPAND_NORMAL);
10257169699Skan	    }
10258169699Skan	}
10259169699Skan      return 0;
10260169699Skan    }
10261169699Skan}
10262169699Skan
10263169699Skan/* Emit warning if a buffer overflow is detected at compile time.  */
10264169699Skan
10265169699Skanstatic void
10266169699Skanmaybe_emit_chk_warning (tree exp, enum built_in_function fcode)
10267169699Skan{
10268169699Skan  int arg_mask, is_strlen = 0;
10269169699Skan  tree arglist = TREE_OPERAND (exp, 1), a;
10270169699Skan  tree len, size;
10271169699Skan  location_t locus;
10272169699Skan
10273169699Skan  switch (fcode)
10274169699Skan    {
10275169699Skan    case BUILT_IN_STRCPY_CHK:
10276169699Skan    case BUILT_IN_STPCPY_CHK:
10277169699Skan    /* For __strcat_chk the warning will be emitted only if overflowing
10278169699Skan       by at least strlen (dest) + 1 bytes.  */
10279169699Skan    case BUILT_IN_STRCAT_CHK:
10280169699Skan      arg_mask = 6;
10281169699Skan      is_strlen = 1;
10282169699Skan      break;
10283169699Skan    case BUILT_IN_STRNCPY_CHK:
10284169699Skan      arg_mask = 12;
10285169699Skan      break;
10286169699Skan    case BUILT_IN_SNPRINTF_CHK:
10287169699Skan    case BUILT_IN_VSNPRINTF_CHK:
10288169699Skan      arg_mask = 10;
10289169699Skan      break;
10290169699Skan    default:
10291169699Skan      gcc_unreachable ();
10292169699Skan    }
10293169699Skan
10294169699Skan  len = NULL_TREE;
10295169699Skan  size = NULL_TREE;
10296169699Skan  for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1)
10297169699Skan    if (arg_mask & 1)
10298169699Skan      {
10299169699Skan	if (len)
10300169699Skan	  size = a;
10301169699Skan	else
10302169699Skan	  len = a;
10303169699Skan      }
10304169699Skan
10305169699Skan  if (!len || !size)
10306169699Skan    return;
10307169699Skan
10308169699Skan  len = TREE_VALUE (len);
10309169699Skan  size = TREE_VALUE (size);
10310169699Skan
10311169699Skan  if (! host_integerp (size, 1) || integer_all_onesp (size))
10312169699Skan    return;
10313169699Skan
10314169699Skan  if (is_strlen)
10315169699Skan    {
10316169699Skan      len = c_strlen (len, 1);
10317169699Skan      if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size))
10318169699Skan	return;
10319169699Skan    }
10320169699Skan  else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len))
10321169699Skan    return;
10322169699Skan
10323169699Skan  locus = EXPR_LOCATION (exp);
10324169699Skan  warning (0, "%Hcall to %D will always overflow destination buffer",
10325169699Skan	   &locus, get_callee_fndecl (exp));
10326169699Skan}
10327169699Skan
10328169699Skan/* Emit warning if a buffer overflow is detected at compile time
10329169699Skan   in __sprintf_chk/__vsprintf_chk calls.  */
10330169699Skan
10331169699Skanstatic void
10332169699Skanmaybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
10333169699Skan{
10334169699Skan  tree arglist = TREE_OPERAND (exp, 1);
10335169699Skan  tree dest, size, len, fmt, flag;
10336169699Skan  const char *fmt_str;
10337169699Skan
10338169699Skan  /* Verify the required arguments in the original call.  */
10339169699Skan  if (! arglist)
10340169699Skan    return;
10341169699Skan  dest = TREE_VALUE (arglist);
10342169699Skan  arglist = TREE_CHAIN (arglist);
10343169699Skan  if (! arglist)
10344169699Skan    return;
10345169699Skan  flag = TREE_VALUE (arglist);
10346169699Skan  arglist = TREE_CHAIN (arglist);
10347169699Skan  if (! arglist)
10348169699Skan    return;
10349169699Skan  size = TREE_VALUE (arglist);
10350169699Skan  arglist = TREE_CHAIN (arglist);
10351169699Skan  if (! arglist)
10352169699Skan    return;
10353169699Skan  fmt = TREE_VALUE (arglist);
10354169699Skan  arglist = TREE_CHAIN (arglist);
10355169699Skan
10356169699Skan  if (! host_integerp (size, 1) || integer_all_onesp (size))
10357169699Skan    return;
10358169699Skan
10359169699Skan  /* Check whether the format is a literal string constant.  */
10360169699Skan  fmt_str = c_getstr (fmt);
10361169699Skan  if (fmt_str == NULL)
10362169699Skan    return;
10363169699Skan
10364169699Skan  if (!init_target_chars())
10365169699Skan    return;
10366169699Skan
10367169699Skan  /* If the format doesn't contain % args or %%, we know its size.  */
10368169699Skan  if (strchr (fmt_str, target_percent) == 0)
10369169699Skan    len = build_int_cstu (size_type_node, strlen (fmt_str));
10370169699Skan  /* If the format is "%s" and first ... argument is a string literal,
10371169699Skan     we know it too.  */
10372169699Skan  else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0)
10373169699Skan    {
10374169699Skan      tree arg;
10375169699Skan
10376169699Skan      if (! arglist)
10377169699Skan	return;
10378169699Skan      arg = TREE_VALUE (arglist);
10379169699Skan      if (! POINTER_TYPE_P (TREE_TYPE (arg)))
10380169699Skan	return;
10381169699Skan
10382169699Skan      len = c_strlen (arg, 1);
10383169699Skan      if (!len || ! host_integerp (len, 1))
10384169699Skan	return;
10385169699Skan    }
10386169699Skan  else
10387169699Skan    return;
10388169699Skan
10389169699Skan  if (! tree_int_cst_lt (len, size))
10390169699Skan    {
10391169699Skan      location_t locus = EXPR_LOCATION (exp);
10392169699Skan      warning (0, "%Hcall to %D will always overflow destination buffer",
10393169699Skan	       &locus, get_callee_fndecl (exp));
10394169699Skan    }
10395169699Skan}
10396169699Skan
10397169699Skan/* Fold a call to __builtin_object_size, if possible.  */
10398169699Skan
10399169699Skantree
10400169699Skanfold_builtin_object_size (tree arglist)
10401169699Skan{
10402169699Skan  tree ptr, ost, ret = 0;
10403169699Skan  int object_size_type;
10404169699Skan
10405169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
10406169699Skan    return 0;
10407169699Skan
10408169699Skan  ptr = TREE_VALUE (arglist);
10409169699Skan  ost = TREE_VALUE (TREE_CHAIN (arglist));
10410169699Skan  STRIP_NOPS (ost);
10411169699Skan
10412169699Skan  if (TREE_CODE (ost) != INTEGER_CST
10413169699Skan      || tree_int_cst_sgn (ost) < 0
10414169699Skan      || compare_tree_int (ost, 3) > 0)
10415169699Skan    return 0;
10416169699Skan
10417169699Skan  object_size_type = tree_low_cst (ost, 0);
10418169699Skan
10419169699Skan  /* __builtin_object_size doesn't evaluate side-effects in its arguments;
10420169699Skan     if there are any side-effects, it returns (size_t) -1 for types 0 and 1
10421169699Skan     and (size_t) 0 for types 2 and 3.  */
10422169699Skan  if (TREE_SIDE_EFFECTS (ptr))
10423169699Skan    return fold_convert (size_type_node,
10424169699Skan			 object_size_type < 2
10425169699Skan			 ? integer_minus_one_node : integer_zero_node);
10426169699Skan
10427169699Skan  if (TREE_CODE (ptr) == ADDR_EXPR)
10428169699Skan    ret = build_int_cstu (size_type_node,
10429169699Skan			compute_builtin_object_size (ptr, object_size_type));
10430169699Skan
10431169699Skan  else if (TREE_CODE (ptr) == SSA_NAME)
10432169699Skan    {
10433169699Skan      unsigned HOST_WIDE_INT bytes;
10434169699Skan
10435169699Skan      /* If object size is not known yet, delay folding until
10436169699Skan       later.  Maybe subsequent passes will help determining
10437169699Skan       it.  */
10438169699Skan      bytes = compute_builtin_object_size (ptr, object_size_type);
10439169699Skan      if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2
10440169699Skan					     ? -1 : 0))
10441169699Skan	ret = build_int_cstu (size_type_node, bytes);
10442169699Skan    }
10443169699Skan
10444169699Skan  if (ret)
10445169699Skan    {
10446169699Skan      ret = force_fit_type (ret, -1, false, false);
10447169699Skan      if (TREE_CONSTANT_OVERFLOW (ret))
10448169699Skan	ret = 0;
10449169699Skan    }
10450169699Skan
10451169699Skan  return ret;
10452169699Skan}
10453169699Skan
10454169699Skan/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin.
10455169699Skan   IGNORE is true, if return value can be ignored.  FCODE is the BUILT_IN_*
10456169699Skan   code of the builtin.  If MAXLEN is not NULL, it is maximum length
10457169699Skan   passed as third argument.  */
10458169699Skan
10459169699Skantree
10460169699Skanfold_builtin_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore,
10461169699Skan			 enum built_in_function fcode)
10462169699Skan{
10463169699Skan  tree dest, src, len, size, fn;
10464169699Skan
10465169699Skan  if (!validate_arglist (arglist,
10466169699Skan			 POINTER_TYPE,
10467169699Skan			 fcode == BUILT_IN_MEMSET_CHK
10468169699Skan			 ? INTEGER_TYPE : POINTER_TYPE,
10469169699Skan			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
10470169699Skan    return 0;
10471169699Skan
10472169699Skan  dest = TREE_VALUE (arglist);
10473169699Skan  /* Actually val for __memset_chk, but it doesn't matter.  */
10474169699Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
10475169699Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
10476169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
10477169699Skan
10478169699Skan  /* If SRC and DEST are the same (and not volatile), return DEST
10479169699Skan     (resp. DEST+LEN for __mempcpy_chk).  */
10480169699Skan  if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
10481169699Skan    {
10482169699Skan      if (fcode != BUILT_IN_MEMPCPY_CHK)
10483169699Skan	return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len);
10484169699Skan      else
10485169699Skan	{
10486169699Skan	  tree temp = fold_convert (TREE_TYPE (dest), len);
10487169699Skan	  temp = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp);
10488169699Skan	  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), temp);
10489169699Skan	}
10490169699Skan    }
10491169699Skan
10492169699Skan  if (! host_integerp (size, 1))
10493169699Skan    return 0;
10494169699Skan
10495169699Skan  if (! integer_all_onesp (size))
10496169699Skan    {
10497169699Skan      if (! host_integerp (len, 1))
10498169699Skan	{
10499169699Skan	  /* If LEN is not constant, try MAXLEN too.
10500169699Skan	     For MAXLEN only allow optimizing into non-_ocs function
10501169699Skan	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
10502169699Skan	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
10503169699Skan	    {
10504169699Skan	      if (fcode == BUILT_IN_MEMPCPY_CHK && ignore)
10505169699Skan		{
10506169699Skan		  /* (void) __mempcpy_chk () can be optimized into
10507169699Skan		     (void) __memcpy_chk ().  */
10508169699Skan		  fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
10509169699Skan		  if (!fn)
10510169699Skan		    return 0;
10511169699Skan
10512169699Skan		  return build_function_call_expr (fn, arglist);
10513169699Skan		}
10514169699Skan	      return 0;
10515169699Skan	    }
10516169699Skan	}
10517169699Skan      else
10518169699Skan	maxlen = len;
10519169699Skan
10520169699Skan      if (tree_int_cst_lt (size, maxlen))
10521169699Skan	return 0;
10522169699Skan    }
10523169699Skan
10524169699Skan  arglist = build_tree_list (NULL_TREE, len);
10525169699Skan  arglist = tree_cons (NULL_TREE, src, arglist);
10526169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10527169699Skan
10528169699Skan  fn = NULL_TREE;
10529169699Skan  /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
10530169699Skan     mem{cpy,pcpy,move,set} is available.  */
10531169699Skan  switch (fcode)
10532169699Skan    {
10533169699Skan    case BUILT_IN_MEMCPY_CHK:
10534169699Skan      fn = built_in_decls[BUILT_IN_MEMCPY];
10535169699Skan      break;
10536169699Skan    case BUILT_IN_MEMPCPY_CHK:
10537169699Skan      fn = built_in_decls[BUILT_IN_MEMPCPY];
10538169699Skan      break;
10539169699Skan    case BUILT_IN_MEMMOVE_CHK:
10540169699Skan      fn = built_in_decls[BUILT_IN_MEMMOVE];
10541169699Skan      break;
10542169699Skan    case BUILT_IN_MEMSET_CHK:
10543169699Skan      fn = built_in_decls[BUILT_IN_MEMSET];
10544169699Skan      break;
10545169699Skan    default:
10546169699Skan      break;
10547169699Skan    }
10548169699Skan
10549169699Skan  if (!fn)
10550169699Skan    return 0;
10551169699Skan
10552169699Skan  return build_function_call_expr (fn, arglist);
10553169699Skan}
10554169699Skan
10555169699Skan/* Fold a call to the __st[rp]cpy_chk builtin.
10556169699Skan   IGNORE is true, if return value can be ignored.  FCODE is the BUILT_IN_*
10557169699Skan   code of the builtin.  If MAXLEN is not NULL, it is maximum length of
10558169699Skan   strings passed as second argument.  */
10559169699Skan
10560169699Skantree
10561169699Skanfold_builtin_stxcpy_chk (tree fndecl, tree arglist, tree maxlen, bool ignore,
10562169699Skan			 enum built_in_function fcode)
10563169699Skan{
10564169699Skan  tree dest, src, size, len, fn;
10565169699Skan
10566169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
10567169699Skan			 VOID_TYPE))
10568169699Skan    return 0;
10569169699Skan
10570169699Skan  dest = TREE_VALUE (arglist);
10571169699Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
10572169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
10573169699Skan
10574169699Skan  /* If SRC and DEST are the same (and not volatile), return DEST.  */
10575169699Skan  if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
10576169699Skan    return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest);
10577169699Skan
10578169699Skan  if (! host_integerp (size, 1))
10579169699Skan    return 0;
10580169699Skan
10581169699Skan  if (! integer_all_onesp (size))
10582169699Skan    {
10583169699Skan      len = c_strlen (src, 1);
10584169699Skan      if (! len || ! host_integerp (len, 1))
10585169699Skan	{
10586169699Skan	  /* If LEN is not constant, try MAXLEN too.
10587169699Skan	     For MAXLEN only allow optimizing into non-_ocs function
10588169699Skan	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
10589169699Skan	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
10590169699Skan	    {
10591169699Skan	      if (fcode == BUILT_IN_STPCPY_CHK)
10592169699Skan		{
10593169699Skan		  if (! ignore)
10594169699Skan		    return 0;
10595169699Skan
10596169699Skan		  /* If return value of __stpcpy_chk is ignored,
10597169699Skan		     optimize into __strcpy_chk.  */
10598169699Skan		  fn = built_in_decls[BUILT_IN_STRCPY_CHK];
10599169699Skan		  if (!fn)
10600169699Skan		    return 0;
10601169699Skan
10602169699Skan		  return build_function_call_expr (fn, arglist);
10603169699Skan		}
10604169699Skan
10605169699Skan	      if (! len || TREE_SIDE_EFFECTS (len))
10606169699Skan		return 0;
10607169699Skan
10608169699Skan	      /* If c_strlen returned something, but not a constant,
10609169699Skan		 transform __strcpy_chk into __memcpy_chk.  */
10610169699Skan	      fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
10611169699Skan	      if (!fn)
10612169699Skan		return 0;
10613169699Skan
10614169699Skan	      len = size_binop (PLUS_EXPR, len, ssize_int (1));
10615169699Skan	      arglist = build_tree_list (NULL_TREE, size);
10616169699Skan	      arglist = tree_cons (NULL_TREE, len, arglist);
10617169699Skan	      arglist = tree_cons (NULL_TREE, src, arglist);
10618169699Skan	      arglist = tree_cons (NULL_TREE, dest, arglist);
10619169699Skan	      return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
10620169699Skan				   build_function_call_expr (fn, arglist));
10621169699Skan	    }
10622169699Skan	}
10623169699Skan      else
10624169699Skan	maxlen = len;
10625169699Skan
10626169699Skan      if (! tree_int_cst_lt (maxlen, size))
10627169699Skan	return 0;
10628169699Skan    }
10629169699Skan
10630169699Skan  arglist = build_tree_list (NULL_TREE, src);
10631169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10632169699Skan
10633169699Skan  /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available.  */
10634169699Skan  fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK
10635169699Skan		      ? BUILT_IN_STPCPY : BUILT_IN_STRCPY];
10636169699Skan  if (!fn)
10637169699Skan    return 0;
10638169699Skan
10639169699Skan  return build_function_call_expr (fn, arglist);
10640169699Skan}
10641169699Skan
10642169699Skan/* Fold a call to the __strncpy_chk builtin.
10643169699Skan   If MAXLEN is not NULL, it is maximum length passed as third argument.  */
10644169699Skan
10645169699Skantree
10646169699Skanfold_builtin_strncpy_chk (tree arglist, tree maxlen)
10647169699Skan{
10648169699Skan  tree dest, src, size, len, fn;
10649169699Skan
10650169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
10651169699Skan			 INTEGER_TYPE, VOID_TYPE))
10652169699Skan    return 0;
10653169699Skan
10654169699Skan  dest = TREE_VALUE (arglist);
10655169699Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
10656169699Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
10657169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
10658169699Skan
10659169699Skan  if (! host_integerp (size, 1))
10660169699Skan    return 0;
10661169699Skan
10662169699Skan  if (! integer_all_onesp (size))
10663169699Skan    {
10664169699Skan      if (! host_integerp (len, 1))
10665169699Skan	{
10666169699Skan	  /* If LEN is not constant, try MAXLEN too.
10667169699Skan	     For MAXLEN only allow optimizing into non-_ocs function
10668169699Skan	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
10669169699Skan	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
10670169699Skan	    return 0;
10671169699Skan	}
10672169699Skan      else
10673169699Skan	maxlen = len;
10674169699Skan
10675169699Skan      if (tree_int_cst_lt (size, maxlen))
10676169699Skan	return 0;
10677169699Skan    }
10678169699Skan
10679169699Skan  arglist = build_tree_list (NULL_TREE, len);
10680169699Skan  arglist = tree_cons (NULL_TREE, src, arglist);
10681169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10682169699Skan
10683169699Skan  /* If __builtin_strncpy_chk is used, assume strncpy is available.  */
10684169699Skan  fn = built_in_decls[BUILT_IN_STRNCPY];
10685169699Skan  if (!fn)
10686169699Skan    return 0;
10687169699Skan
10688169699Skan  return build_function_call_expr (fn, arglist);
10689169699Skan}
10690169699Skan
10691169699Skan/* Fold a call to the __strcat_chk builtin FNDECL with ARGLIST.  */
10692169699Skan
10693169699Skanstatic tree
10694169699Skanfold_builtin_strcat_chk (tree fndecl, tree arglist)
10695169699Skan{
10696169699Skan  tree dest, src, size, fn;
10697169699Skan  const char *p;
10698169699Skan
10699169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
10700169699Skan			 VOID_TYPE))
10701169699Skan    return 0;
10702169699Skan
10703169699Skan  dest = TREE_VALUE (arglist);
10704169699Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
10705169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
10706169699Skan
10707169699Skan  p = c_getstr (src);
10708169699Skan  /* If the SRC parameter is "", return DEST.  */
10709169699Skan  if (p && *p == '\0')
10710169699Skan    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
10711169699Skan
10712169699Skan  if (! host_integerp (size, 1) || ! integer_all_onesp (size))
10713169699Skan    return 0;
10714169699Skan
10715169699Skan  arglist = build_tree_list (NULL_TREE, src);
10716169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10717169699Skan
10718169699Skan  /* If __builtin_strcat_chk is used, assume strcat is available.  */
10719169699Skan  fn = built_in_decls[BUILT_IN_STRCAT];
10720169699Skan  if (!fn)
10721169699Skan    return 0;
10722169699Skan
10723169699Skan  return build_function_call_expr (fn, arglist);
10724169699Skan}
10725169699Skan
10726169699Skan/* Fold a call to the __strncat_chk builtin EXP.  */
10727169699Skan
10728169699Skanstatic tree
10729169699Skanfold_builtin_strncat_chk (tree fndecl, tree arglist)
10730169699Skan{
10731169699Skan  tree dest, src, size, len, fn;
10732169699Skan  const char *p;
10733169699Skan
10734169699Skan  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
10735169699Skan			 INTEGER_TYPE, VOID_TYPE))
10736169699Skan    return 0;
10737169699Skan
10738169699Skan  dest = TREE_VALUE (arglist);
10739169699Skan  src = TREE_VALUE (TREE_CHAIN (arglist));
10740169699Skan  len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
10741169699Skan  size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
10742169699Skan
10743169699Skan  p = c_getstr (src);
10744169699Skan  /* If the SRC parameter is "" or if LEN is 0, return DEST.  */
10745169699Skan  if (p && *p == '\0')
10746169699Skan    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len);
10747169699Skan  else if (integer_zerop (len))
10748169699Skan    return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
10749169699Skan
10750169699Skan  if (! host_integerp (size, 1))
10751169699Skan    return 0;
10752169699Skan
10753169699Skan  if (! integer_all_onesp (size))
10754169699Skan    {
10755169699Skan      tree src_len = c_strlen (src, 1);
10756169699Skan      if (src_len
10757169699Skan	  && host_integerp (src_len, 1)
10758169699Skan	  && host_integerp (len, 1)
10759169699Skan	  && ! tree_int_cst_lt (len, src_len))
10760169699Skan	{
10761169699Skan	  /* If LEN >= strlen (SRC), optimize into __strcat_chk.  */
10762169699Skan	  fn = built_in_decls[BUILT_IN_STRCAT_CHK];
10763169699Skan	  if (!fn)
10764169699Skan	    return 0;
10765169699Skan
10766169699Skan	  arglist = build_tree_list (NULL_TREE, size);
10767169699Skan	  arglist = tree_cons (NULL_TREE, src, arglist);
10768169699Skan	  arglist = tree_cons (NULL_TREE, dest, arglist);
10769169699Skan	  return build_function_call_expr (fn, arglist);
10770169699Skan	}
10771169699Skan      return 0;
10772169699Skan    }
10773169699Skan
10774169699Skan  arglist = build_tree_list (NULL_TREE, len);
10775169699Skan  arglist = tree_cons (NULL_TREE, src, arglist);
10776169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10777169699Skan
10778169699Skan  /* If __builtin_strncat_chk is used, assume strncat is available.  */
10779169699Skan  fn = built_in_decls[BUILT_IN_STRNCAT];
10780169699Skan  if (!fn)
10781169699Skan    return 0;
10782169699Skan
10783169699Skan  return build_function_call_expr (fn, arglist);
10784169699Skan}
10785169699Skan
10786169699Skan/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST.  Return 0 if
10787169699Skan   a normal call should be emitted rather than expanding the function
10788169699Skan   inline.  FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK.  */
10789169699Skan
10790169699Skanstatic tree
10791169699Skanfold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode)
10792169699Skan{
10793169699Skan  tree dest, size, len, fn, fmt, flag;
10794169699Skan  const char *fmt_str;
10795169699Skan
10796169699Skan  /* Verify the required arguments in the original call.  */
10797169699Skan  if (! arglist)
10798169699Skan    return 0;
10799169699Skan  dest = TREE_VALUE (arglist);
10800169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (dest)))
10801169699Skan    return 0;
10802169699Skan  arglist = TREE_CHAIN (arglist);
10803169699Skan  if (! arglist)
10804169699Skan    return 0;
10805169699Skan  flag = TREE_VALUE (arglist);
10806169699Skan  if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE)
10807169699Skan    return 0;
10808169699Skan  arglist = TREE_CHAIN (arglist);
10809169699Skan  if (! arglist)
10810169699Skan    return 0;
10811169699Skan  size = TREE_VALUE (arglist);
10812169699Skan  if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE)
10813169699Skan    return 0;
10814169699Skan  arglist = TREE_CHAIN (arglist);
10815169699Skan  if (! arglist)
10816169699Skan    return 0;
10817169699Skan  fmt = TREE_VALUE (arglist);
10818169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
10819169699Skan    return 0;
10820169699Skan  arglist = TREE_CHAIN (arglist);
10821169699Skan
10822169699Skan  if (! host_integerp (size, 1))
10823169699Skan    return 0;
10824169699Skan
10825169699Skan  len = NULL_TREE;
10826169699Skan
10827169699Skan  if (!init_target_chars())
10828169699Skan    return 0;
10829169699Skan
10830169699Skan  /* Check whether the format is a literal string constant.  */
10831169699Skan  fmt_str = c_getstr (fmt);
10832169699Skan  if (fmt_str != NULL)
10833169699Skan    {
10834169699Skan      /* If the format doesn't contain % args or %%, we know the size.  */
10835169699Skan      if (strchr (fmt_str, target_percent) == 0)
10836169699Skan	{
10837169699Skan	  if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE)
10838169699Skan	    len = build_int_cstu (size_type_node, strlen (fmt_str));
10839169699Skan	}
10840169699Skan      /* If the format is "%s" and first ... argument is a string literal,
10841169699Skan	 we know the size too.  */
10842169699Skan      else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0)
10843169699Skan	{
10844169699Skan	  tree arg;
10845169699Skan
10846169699Skan	  if (arglist && !TREE_CHAIN (arglist))
10847169699Skan	    {
10848169699Skan	      arg = TREE_VALUE (arglist);
10849169699Skan	      if (POINTER_TYPE_P (TREE_TYPE (arg)))
10850169699Skan		{
10851169699Skan		  len = c_strlen (arg, 1);
10852169699Skan		  if (! len || ! host_integerp (len, 1))
10853169699Skan		    len = NULL_TREE;
10854169699Skan		}
10855169699Skan	    }
10856169699Skan	}
10857169699Skan    }
10858169699Skan
10859169699Skan  if (! integer_all_onesp (size))
10860169699Skan    {
10861169699Skan      if (! len || ! tree_int_cst_lt (len, size))
10862169699Skan	return 0;
10863169699Skan    }
10864169699Skan
10865169699Skan  /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
10866169699Skan     or if format doesn't contain % chars or is "%s".  */
10867169699Skan  if (! integer_zerop (flag))
10868169699Skan    {
10869169699Skan      if (fmt_str == NULL)
10870169699Skan	return 0;
10871169699Skan      if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s))
10872169699Skan	return 0;
10873169699Skan    }
10874169699Skan
10875169699Skan  arglist = tree_cons (NULL_TREE, fmt, arglist);
10876169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10877169699Skan
10878169699Skan  /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available.  */
10879169699Skan  fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK
10880169699Skan		      ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF];
10881169699Skan  if (!fn)
10882169699Skan    return 0;
10883169699Skan
10884169699Skan  return build_function_call_expr (fn, arglist);
10885169699Skan}
10886169699Skan
10887169699Skan/* Fold a call to {,v}snprintf with argument list ARGLIST.  Return 0 if
10888169699Skan   a normal call should be emitted rather than expanding the function
10889169699Skan   inline.  FCODE is either BUILT_IN_SNPRINTF_CHK or
10890169699Skan   BUILT_IN_VSNPRINTF_CHK.  If MAXLEN is not NULL, it is maximum length
10891169699Skan   passed as second argument.  */
10892169699Skan
10893169699Skantree
10894169699Skanfold_builtin_snprintf_chk (tree arglist, tree maxlen,
10895169699Skan			   enum built_in_function fcode)
10896169699Skan{
10897169699Skan  tree dest, size, len, fn, fmt, flag;
10898169699Skan  const char *fmt_str;
10899169699Skan
10900169699Skan  /* Verify the required arguments in the original call.  */
10901169699Skan  if (! arglist)
10902169699Skan    return 0;
10903169699Skan  dest = TREE_VALUE (arglist);
10904169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (dest)))
10905169699Skan    return 0;
10906169699Skan  arglist = TREE_CHAIN (arglist);
10907169699Skan  if (! arglist)
10908169699Skan    return 0;
10909169699Skan  len = TREE_VALUE (arglist);
10910169699Skan  if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE)
10911169699Skan    return 0;
10912169699Skan  arglist = TREE_CHAIN (arglist);
10913169699Skan  if (! arglist)
10914169699Skan    return 0;
10915169699Skan  flag = TREE_VALUE (arglist);
10916169699Skan  if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE)
10917169699Skan    return 0;
10918169699Skan  arglist = TREE_CHAIN (arglist);
10919169699Skan  if (! arglist)
10920169699Skan    return 0;
10921169699Skan  size = TREE_VALUE (arglist);
10922169699Skan  if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE)
10923169699Skan    return 0;
10924169699Skan  arglist = TREE_CHAIN (arglist);
10925169699Skan  if (! arglist)
10926169699Skan    return 0;
10927169699Skan  fmt = TREE_VALUE (arglist);
10928169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
10929169699Skan    return 0;
10930169699Skan  arglist = TREE_CHAIN (arglist);
10931169699Skan
10932169699Skan  if (! host_integerp (size, 1))
10933169699Skan    return 0;
10934169699Skan
10935169699Skan  if (! integer_all_onesp (size))
10936169699Skan    {
10937169699Skan      if (! host_integerp (len, 1))
10938169699Skan	{
10939169699Skan	  /* If LEN is not constant, try MAXLEN too.
10940169699Skan	     For MAXLEN only allow optimizing into non-_ocs function
10941169699Skan	     if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
10942169699Skan	  if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
10943169699Skan	    return 0;
10944169699Skan	}
10945169699Skan      else
10946169699Skan	maxlen = len;
10947169699Skan
10948169699Skan      if (tree_int_cst_lt (size, maxlen))
10949169699Skan	return 0;
10950169699Skan    }
10951169699Skan
10952169699Skan  if (!init_target_chars())
10953169699Skan    return 0;
10954169699Skan
10955169699Skan  /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
10956169699Skan     or if format doesn't contain % chars or is "%s".  */
10957169699Skan  if (! integer_zerop (flag))
10958169699Skan    {
10959169699Skan      fmt_str = c_getstr (fmt);
10960169699Skan      if (fmt_str == NULL)
10961169699Skan	return 0;
10962169699Skan      if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s))
10963169699Skan	return 0;
10964169699Skan    }
10965169699Skan
10966169699Skan  arglist = tree_cons (NULL_TREE, fmt, arglist);
10967169699Skan  arglist = tree_cons (NULL_TREE, len, arglist);
10968169699Skan  arglist = tree_cons (NULL_TREE, dest, arglist);
10969169699Skan
10970169699Skan  /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
10971169699Skan     available.  */
10972169699Skan  fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK
10973169699Skan		      ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF];
10974169699Skan  if (!fn)
10975169699Skan    return 0;
10976169699Skan
10977169699Skan  return build_function_call_expr (fn, arglist);
10978169699Skan}
10979169699Skan
10980169699Skan/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
10981169699Skan
10982169699Skan   Return 0 if no simplification was possible, otherwise return the
10983169699Skan   simplified form of the call as a tree.  FCODE is the BUILT_IN_*
10984169699Skan   code of the function to be simplified.  */
10985169699Skan
10986169699Skanstatic tree
10987169699Skanfold_builtin_printf (tree fndecl, tree arglist, bool ignore,
10988169699Skan		     enum built_in_function fcode)
10989169699Skan{
10990169699Skan  tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call;
10991169699Skan  const char *fmt_str = NULL;
10992169699Skan
10993169699Skan  /* If the return value is used, don't do the transformation.  */
10994169699Skan  if (! ignore)
10995169699Skan    return 0;
10996169699Skan
10997169699Skan  /* Verify the required arguments in the original call.  */
10998169699Skan  if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK)
10999169699Skan    {
11000169699Skan      tree flag;
11001169699Skan
11002169699Skan      if (! arglist)
11003169699Skan	return 0;
11004169699Skan      flag = TREE_VALUE (arglist);
11005169699Skan      if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE
11006169699Skan	  || TREE_SIDE_EFFECTS (flag))
11007169699Skan	return 0;
11008169699Skan      arglist = TREE_CHAIN (arglist);
11009169699Skan    }
11010169699Skan
11011169699Skan  if (! arglist)
11012169699Skan    return 0;
11013169699Skan  fmt = TREE_VALUE (arglist);
11014169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
11015169699Skan    return 0;
11016169699Skan  arglist = TREE_CHAIN (arglist);
11017169699Skan
11018169699Skan  /* Check whether the format is a literal string constant.  */
11019169699Skan  fmt_str = c_getstr (fmt);
11020169699Skan  if (fmt_str == NULL)
11021169699Skan    return NULL_TREE;
11022169699Skan
11023169699Skan  if (fcode == BUILT_IN_PRINTF_UNLOCKED)
11024169699Skan    {
11025169699Skan      /* If we're using an unlocked function, assume the other
11026169699Skan	 unlocked functions exist explicitly.  */
11027169699Skan      fn_putchar = built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED];
11028169699Skan      fn_puts = built_in_decls[BUILT_IN_PUTS_UNLOCKED];
11029169699Skan    }
11030169699Skan  else
11031169699Skan    {
11032169699Skan      fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR];
11033169699Skan      fn_puts = implicit_built_in_decls[BUILT_IN_PUTS];
11034169699Skan    }
11035169699Skan
11036169699Skan  if (!init_target_chars())
11037169699Skan    return 0;
11038169699Skan
11039169699Skan  if (strcmp (fmt_str, target_percent_s) == 0 || strchr (fmt_str, target_percent) == NULL)
11040169699Skan    {
11041169699Skan      const char *str;
11042169699Skan
11043169699Skan      if (strcmp (fmt_str, target_percent_s) == 0)
11044169699Skan	{
11045169699Skan	  if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
11046169699Skan	    return 0;
11047169699Skan
11048169699Skan	  if (! arglist
11049169699Skan	      || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
11050169699Skan	      || TREE_CHAIN (arglist))
11051169699Skan	    return 0;
11052169699Skan
11053169699Skan	  str = c_getstr (TREE_VALUE (arglist));
11054169699Skan	  if (str == NULL)
11055169699Skan	    return 0;
11056169699Skan	}
11057169699Skan      else
11058169699Skan	{
11059169699Skan	  /* The format specifier doesn't contain any '%' characters.  */
11060169699Skan	  if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK
11061169699Skan	      && arglist)
11062169699Skan	    return 0;
11063169699Skan	  str = fmt_str;
11064169699Skan	}
11065169699Skan
11066169699Skan      /* If the string was "", printf does nothing.  */
11067169699Skan      if (str[0] == '\0')
11068169699Skan	return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0);
11069169699Skan
11070169699Skan      /* If the string has length of 1, call putchar.  */
11071169699Skan      if (str[1] == '\0')
11072169699Skan	{
11073169699Skan	  /* Given printf("c"), (where c is any one character,)
11074169699Skan	     convert "c"[0] to an int and pass that to the replacement
11075169699Skan	     function.  */
11076169699Skan	  arg = build_int_cst (NULL_TREE, str[0]);
11077169699Skan	  arglist = build_tree_list (NULL_TREE, arg);
11078169699Skan	  fn = fn_putchar;
11079169699Skan	}
11080169699Skan      else
11081169699Skan	{
11082169699Skan	  /* If the string was "string\n", call puts("string").  */
11083169699Skan	  size_t len = strlen (str);
11084169699Skan	  if ((unsigned char)str[len - 1] == target_newline)
11085169699Skan	    {
11086169699Skan	      /* Create a NUL-terminated string that's one char shorter
11087169699Skan		 than the original, stripping off the trailing '\n'.  */
11088169699Skan	      char *newstr = alloca (len);
11089169699Skan	      memcpy (newstr, str, len - 1);
11090169699Skan	      newstr[len - 1] = 0;
11091169699Skan
11092169699Skan	      arg = build_string_literal (len, newstr);
11093169699Skan	      arglist = build_tree_list (NULL_TREE, arg);
11094169699Skan	      fn = fn_puts;
11095169699Skan	    }
11096169699Skan	  else
11097169699Skan	    /* We'd like to arrange to call fputs(string,stdout) here,
11098169699Skan	       but we need stdout and don't have a way to get it yet.  */
11099169699Skan	    return 0;
11100169699Skan	}
11101169699Skan    }
11102169699Skan
11103169699Skan  /* The other optimizations can be done only on the non-va_list variants.  */
11104169699Skan  else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
11105169699Skan    return 0;
11106169699Skan
11107169699Skan  /* If the format specifier was "%s\n", call __builtin_puts(arg).  */
11108169699Skan  else if (strcmp (fmt_str, target_percent_s_newline) == 0)
11109169699Skan    {
11110169699Skan      if (! arglist
11111169699Skan	  || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
11112169699Skan	  || TREE_CHAIN (arglist))
11113169699Skan	return 0;
11114169699Skan      fn = fn_puts;
11115169699Skan    }
11116169699Skan
11117169699Skan  /* If the format specifier was "%c", call __builtin_putchar(arg).  */
11118169699Skan  else if (strcmp (fmt_str, target_percent_c) == 0)
11119169699Skan    {
11120169699Skan      if (! arglist
11121169699Skan	  || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
11122169699Skan	  || TREE_CHAIN (arglist))
11123169699Skan	return 0;
11124169699Skan      fn = fn_putchar;
11125169699Skan    }
11126169699Skan
11127169699Skan  if (!fn)
11128169699Skan    return 0;
11129169699Skan
11130169699Skan  call = build_function_call_expr (fn, arglist);
11131169699Skan  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call);
11132169699Skan}
11133169699Skan
11134169699Skan/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins.
11135169699Skan
11136169699Skan   Return 0 if no simplification was possible, otherwise return the
11137169699Skan   simplified form of the call as a tree.  FCODE is the BUILT_IN_*
11138169699Skan   code of the function to be simplified.  */
11139169699Skan
11140169699Skanstatic tree
11141169699Skanfold_builtin_fprintf (tree fndecl, tree arglist, bool ignore,
11142169699Skan		      enum built_in_function fcode)
11143169699Skan{
11144169699Skan  tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call;
11145169699Skan  const char *fmt_str = NULL;
11146169699Skan
11147169699Skan  /* If the return value is used, don't do the transformation.  */
11148169699Skan  if (! ignore)
11149169699Skan    return 0;
11150169699Skan
11151169699Skan  /* Verify the required arguments in the original call.  */
11152169699Skan  if (! arglist)
11153169699Skan    return 0;
11154169699Skan  fp = TREE_VALUE (arglist);
11155169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fp)))
11156169699Skan    return 0;
11157169699Skan  arglist = TREE_CHAIN (arglist);
11158169699Skan
11159169699Skan  if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK)
11160169699Skan    {
11161169699Skan      tree flag;
11162169699Skan
11163169699Skan      if (! arglist)
11164169699Skan	return 0;
11165169699Skan      flag = TREE_VALUE (arglist);
11166169699Skan      if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE
11167169699Skan	  || TREE_SIDE_EFFECTS (flag))
11168169699Skan	return 0;
11169169699Skan      arglist = TREE_CHAIN (arglist);
11170169699Skan    }
11171169699Skan
11172169699Skan  if (! arglist)
11173169699Skan    return 0;
11174169699Skan  fmt = TREE_VALUE (arglist);
11175169699Skan  if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
11176169699Skan    return 0;
11177169699Skan  arglist = TREE_CHAIN (arglist);
11178169699Skan
11179169699Skan  /* Check whether the format is a literal string constant.  */
11180169699Skan  fmt_str = c_getstr (fmt);
11181169699Skan  if (fmt_str == NULL)
11182169699Skan    return NULL_TREE;
11183169699Skan
11184169699Skan  if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
11185169699Skan    {
11186169699Skan      /* If we're using an unlocked function, assume the other
11187169699Skan	 unlocked functions exist explicitly.  */
11188169699Skan      fn_fputc = built_in_decls[BUILT_IN_FPUTC_UNLOCKED];
11189169699Skan      fn_fputs = built_in_decls[BUILT_IN_FPUTS_UNLOCKED];
11190169699Skan    }
11191169699Skan  else
11192169699Skan    {
11193169699Skan      fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC];
11194169699Skan      fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS];
11195169699Skan    }
11196169699Skan
11197169699Skan  if (!init_target_chars())
11198169699Skan    return 0;
11199169699Skan
11200169699Skan  /* If the format doesn't contain % args or %%, use strcpy.  */
11201169699Skan  if (strchr (fmt_str, target_percent) == NULL)
11202169699Skan    {
11203169699Skan      if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
11204169699Skan	  && arglist)
11205169699Skan	return 0;
11206169699Skan
11207169699Skan      /* If the format specifier was "", fprintf does nothing.  */
11208169699Skan      if (fmt_str[0] == '\0')
11209169699Skan	{
11210169699Skan	  /* If FP has side-effects, just wait until gimplification is
11211169699Skan	     done.  */
11212169699Skan	  if (TREE_SIDE_EFFECTS (fp))
11213169699Skan	    return 0;
11214169699Skan
11215169699Skan	  return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0);
11216169699Skan	}
11217169699Skan
11218169699Skan      /* When "string" doesn't contain %, replace all cases of
11219169699Skan	 fprintf (fp, string) with fputs (string, fp).  The fputs
11220169699Skan	 builtin will take care of special cases like length == 1.  */
11221169699Skan      arglist = build_tree_list (NULL_TREE, fp);
11222169699Skan      arglist = tree_cons (NULL_TREE, fmt, arglist);
11223169699Skan      fn = fn_fputs;
11224169699Skan    }
11225169699Skan
11226169699Skan  /* The other optimizations can be done only on the non-va_list variants.  */
11227169699Skan  else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK)
11228169699Skan    return 0;
11229169699Skan
11230169699Skan  /* If the format specifier was "%s", call __builtin_fputs (arg, fp).  */
11231169699Skan  else if (strcmp (fmt_str, target_percent_s) == 0)
11232169699Skan    {
11233169699Skan      if (! arglist
11234169699Skan	  || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
11235169699Skan	  || TREE_CHAIN (arglist))
11236169699Skan	return 0;
11237169699Skan      arg = TREE_VALUE (arglist);
11238169699Skan      arglist = build_tree_list (NULL_TREE, fp);
11239169699Skan      arglist = tree_cons (NULL_TREE, arg, arglist);
11240169699Skan      fn = fn_fputs;
11241169699Skan    }
11242169699Skan
11243169699Skan  /* If the format specifier was "%c", call __builtin_fputc (arg, fp).  */
11244169699Skan  else if (strcmp (fmt_str, target_percent_c) == 0)
11245169699Skan    {
11246169699Skan      if (! arglist
11247169699Skan	  || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
11248169699Skan	  || TREE_CHAIN (arglist))
11249169699Skan	return 0;
11250169699Skan      arg = TREE_VALUE (arglist);
11251169699Skan      arglist = build_tree_list (NULL_TREE, fp);
11252169699Skan      arglist = tree_cons (NULL_TREE, arg, arglist);
11253169699Skan      fn = fn_fputc;
11254169699Skan    }
11255169699Skan
11256169699Skan  if (!fn)
11257169699Skan    return 0;
11258169699Skan
11259169699Skan  call = build_function_call_expr (fn, arglist);
11260169699Skan  return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call);
11261169699Skan}
11262169699Skan
11263169699Skan/* Initialize format string characters in the target charset.  */
11264169699Skan
11265169699Skanstatic bool
11266169699Skaninit_target_chars (void)
11267169699Skan{
11268169699Skan  static bool init;
11269169699Skan  if (!init)
11270169699Skan    {
11271169699Skan      target_newline = lang_hooks.to_target_charset ('\n');
11272169699Skan      target_percent = lang_hooks.to_target_charset ('%');
11273169699Skan      target_c = lang_hooks.to_target_charset ('c');
11274169699Skan      target_s = lang_hooks.to_target_charset ('s');
11275169699Skan      if (target_newline == 0 || target_percent == 0 || target_c == 0
11276169699Skan	  || target_s == 0)
11277169699Skan	return false;
11278169699Skan
11279169699Skan      target_percent_c[0] = target_percent;
11280169699Skan      target_percent_c[1] = target_c;
11281169699Skan      target_percent_c[2] = '\0';
11282169699Skan
11283169699Skan      target_percent_s[0] = target_percent;
11284169699Skan      target_percent_s[1] = target_s;
11285169699Skan      target_percent_s[2] = '\0';
11286169699Skan
11287169699Skan      target_percent_s_newline[0] = target_percent;
11288169699Skan      target_percent_s_newline[1] = target_s;
11289169699Skan      target_percent_s_newline[2] = target_newline;
11290169699Skan      target_percent_s_newline[3] = '\0';
11291169699Skan
11292169699Skan      init = true;
11293169699Skan    }
11294169699Skan  return true;
11295169699Skan}
11296