/* Statement simplification on GIMPLE.
- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2010-2015 Free Software Foundation, Inc.
Split out from tree-ssa-ccp.c.
This file is part of GCC.
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "stringpool.h"
-#include "expr.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
#include "stmt.h"
+#include "expr.h"
#include "stor-layout.h"
-#include "flags.h"
-#include "function.h"
#include "dumpfile.h"
#include "bitmap.h"
+#include "predict.h"
+#include "dominance.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-fold.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "tree-ssa.h"
#include "tree-ssa-propagate.h"
#include "target.h"
+#include "cgraph.h"
#include "ipa-utils.h"
#include "gimple-pretty-print.h"
#include "tree-ssa-address.h"
#include "dbgcnt.h"
#include "builtins.h"
#include "output.h"
+#include "tree-eh.h"
+#include "gimple-match.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
/* Return true when DECL can be referenced from current unit.
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
struct cgraph_node *node;
symtab_node *snode;
- if (DECL_ABSTRACT (decl))
+ if (DECL_ABSTRACT_P (decl))
return false;
/* We are concerned only about static/external vars and functions. */
{
/* Before we start optimizing unreachable code we can be sure all
static objects are defined. */
- if (cgraph_function_flags_ready)
+ if (symtab->function_flags_ready)
return true;
snode = symtab_node::get (decl);
if (!snode || !snode->definition)
units where they are used and when the other unit was compiled with LTO
it is possible that vtable was kept public while the function itself
was privatized. */
- if (!cgraph_function_flags_ready)
+ if (!symtab->function_flags_ready)
return true;
snode = symtab_node::get (decl);
have zero as the initializer if they may not be
overridden at link or run time. */
if (!val
- && (INTEGRAL_TYPE_P (TREE_TYPE (sym))
- || SCALAR_FLOAT_TYPE_P (TREE_TYPE (sym))))
+ && is_gimple_reg_type (TREE_TYPE (sym)))
return build_zero_cst (TREE_TYPE (sym));
}
{
tree rhs = gimple_assign_rhs1 (stmt);
+ if (TREE_CLOBBER_P (rhs))
+ return NULL_TREE;
+
if (REFERENCE_CLASS_P (rhs))
return maybe_fold_reference (rhs, false);
= possible_polymorphic_call_targets (rhs, stmt, &final);
if (final && targets.length () <= 1 && dbg_cnt (devirt))
{
- tree fndecl;
-
- if (targets.length () == 1)
- fndecl = targets[0]->decl;
- else
- /* We can not use __builtin_unreachable here because it
- can not have address taken. */
- fndecl = integer_zero_node;
if (dump_enabled_p ())
{
location_t loc = gimple_location_safe (stmt);
"reference to function %s\n",
targets.length () == 1
? targets[0]->name ()
- : "__builtin_unreachable");
+ : "NULL");
+ }
+ if (targets.length () == 1)
+ {
+ val = fold_convert (TREE_TYPE (val),
+ build_fold_addr_expr_loc
+ (loc, targets[0]->decl));
+ STRIP_USELESS_TYPE_CONVERSION (val);
}
- val = fold_convert (TREE_TYPE (val),
- build_fold_addr_expr_loc (loc, fndecl));
- STRIP_USELESS_TYPE_CONVERSION (val);
+ else
+ /* We can not use __builtin_unreachable here because it
+ can not have address taken. */
+ val = build_int_cst (TREE_TYPE (val), 0);
return val;
}
}
break;
case GIMPLE_UNARY_RHS:
- {
- tree rhs = gimple_assign_rhs1 (stmt);
-
- result = fold_unary_loc (loc, subcode, gimple_expr_type (stmt), rhs);
- if (result)
- {
- /* If the operation was a conversion do _not_ mark a
- resulting constant with TREE_OVERFLOW if the original
- constant was not. These conversions have implementation
- defined behavior and retaining the TREE_OVERFLOW flag
- here would confuse later passes such as VRP. */
- if (CONVERT_EXPR_CODE_P (subcode)
- && TREE_CODE (result) == INTEGER_CST
- && TREE_CODE (rhs) == INTEGER_CST)
- TREE_OVERFLOW (result) = TREE_OVERFLOW (rhs);
-
- STRIP_USELESS_TYPE_CONVERSION (result);
- if (valid_gimple_rhs_p (result))
- return result;
- }
- }
break;
case GIMPLE_BINARY_RHS:
assumed that the operands have been previously folded. */
static bool
-fold_gimple_cond (gimple stmt)
+fold_gimple_cond (gcond *stmt)
{
tree result = fold_binary_loc (gimple_location (stmt),
gimple_cond_code (stmt),
}
/* Fold function call to builtin mem{{,p}cpy,move}. Return
- NULL_TREE if no simplification can be made.
+ false if no simplification can be made.
If ENDP is 0, return DEST (like memcpy).
If ENDP is 1, return DEST+LEN (like mempcpy).
If ENDP is 2, return DEST+LEN-1 (like stpcpy).
srcmem = make_ssa_name (TREE_TYPE (srcmem),
new_stmt);
else
- srcmem = create_tmp_reg (TREE_TYPE (srcmem),
- NULL);
+ srcmem = create_tmp_reg (TREE_TYPE (srcmem));
gimple_assign_set_lhs (new_stmt, srcmem);
gimple_set_vuse (new_stmt, gimple_vuse (stmt));
gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
if (gimple_in_ssa_p (cfun))
srcvar = make_ssa_name (TREE_TYPE (srcvar), new_stmt);
else
- srcvar = create_tmp_reg (TREE_TYPE (srcvar), NULL);
+ srcvar = create_tmp_reg (TREE_TYPE (srcvar));
gimple_assign_set_lhs (new_stmt, srcvar);
gimple_set_vuse (new_stmt, gimple_vuse (stmt));
gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
length and 2 for maximum value ARG can have. */
static bool
-get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
+get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
{
tree var, val;
gimple def_stmt;
return false;
/* If we were already here, break the infinite cycle. */
- if (!bitmap_set_bit (visited, SSA_NAME_VERSION (arg)))
+ if (!*visited)
+ *visited = BITMAP_ALLOC (NULL);
+ if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (arg)))
return true;
var = arg;
}
}
+tree
+get_maxval_strlen (tree arg, int type)
+{
+ bitmap visited = NULL;
+ tree len = NULL_TREE;
+ if (!get_maxval_strlen (arg, &len, &visited, type))
+ len = NULL_TREE;
+ if (visited)
+ BITMAP_FREE (visited);
+
+ return len;
+}
+
/* Fold function call to builtin strcpy with arguments DEST and SRC.
If LEN is not NULL, it represents the length of the string to be
static bool
gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
- location_t loc, tree dest, tree src, tree len)
+ tree dest, tree src)
{
+ location_t loc = gimple_location (gsi_stmt (*gsi));
tree fn;
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (!fn)
return false;
+ tree len = get_maxval_strlen (src, 0);
if (!len)
- {
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
- }
+ return false;
len = fold_convert_loc (loc, size_type_node, len);
len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1));
Return NULL_TREE if no simplification can be made. */
static bool
-gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi, location_t loc,
- tree dest, tree src, tree len, tree slen)
+gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
+ tree dest, tree src, tree len)
{
+ location_t loc = gimple_location (gsi_stmt (*gsi));
tree fn;
/* If the LEN parameter is zero, return DEST. */
/* We can't compare slen with len as constants below if len is not a
constant. */
- if (len == 0 || TREE_CODE (len) != INTEGER_CST)
+ if (TREE_CODE (len) != INTEGER_CST)
return false;
- if (!slen)
- slen = c_strlen (src, 1);
-
/* Now, we must be passed a constant src ptr parameter. */
- if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
+ tree slen = get_maxval_strlen (src, 0);
+ if (!slen || TREE_CODE (slen) != INTEGER_CST)
return false;
slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
form of the builtin function call. */
static bool
-gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi,
- location_t loc ATTRIBUTE_UNUSED, tree dst, tree src,
- tree len)
+gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
{
gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
const char *p = c_getstr (src);
/* If the length of the source string isn't computable don't
split strcat into strlen and memcpy. */
+ tree len = get_maxval_strlen (src, 0);
if (! len)
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
return false;
/* Create strlen (dst). */
gimple repl = gimple_build_call (strlen_fn, 1, dst);
gimple_set_location (repl, loc);
if (gimple_in_ssa_p (cfun))
- newdst = make_ssa_name (size_type_node, NULL);
+ newdst = make_ssa_name (size_type_node);
else
- newdst = create_tmp_reg (size_type_node, NULL);
+ newdst = create_tmp_reg (size_type_node);
gimple_call_set_lhs (repl, newdst);
gimple_seq_add_stmt_without_update (&stmts, repl);
return true;
}
+/* Fold a call to the __strcat_chk builtin FNDECL. DEST, SRC, and SIZE
+ are the arguments to the call. */
+
+static bool
+gimple_fold_builtin_strcat_chk (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree dest = gimple_call_arg (stmt, 0);
+ tree src = gimple_call_arg (stmt, 1);
+ tree size = gimple_call_arg (stmt, 2);
+ tree fn;
+ const char *p;
+
+
+ p = c_getstr (src);
+ /* If the SRC parameter is "", return DEST. */
+ if (p && *p == '\0')
+ {
+ replace_call_with_value (gsi, dest);
+ return true;
+ }
+
+ if (! tree_fits_uhwi_p (size) || ! integer_all_onesp (size))
+ return false;
+
+ /* If __builtin_strcat_chk is used, assume strcat is available. */
+ fn = builtin_decl_explicit (BUILT_IN_STRCAT);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 2, dest, src);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
+/* Simplify a call to the strncat builtin. */
+
+static bool
+gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree dst = gimple_call_arg (stmt, 0);
+ tree src = gimple_call_arg (stmt, 1);
+ tree len = gimple_call_arg (stmt, 2);
+
+ const char *p = c_getstr (src);
+
+ /* If the requested length is zero, or the src parameter string
+ length is zero, return the dst parameter. */
+ if (integer_zerop (len) || (p && *p == '\0'))
+ {
+ replace_call_with_value (gsi, dst);
+ return true;
+ }
+
+ /* If the requested len is greater than or equal to the string
+ length, call strcat. */
+ if (TREE_CODE (len) == INTEGER_CST && p
+ && compare_tree_int (len, strlen (p)) >= 0)
+ {
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
+
+ /* If the replacement _DECL isn't initialized, don't do the
+ transformation. */
+ if (!fn)
+ return false;
+
+ gcall *repl = gimple_build_call (fn, 2, dst, src);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+
+ return false;
+}
+
+/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC,
+ LEN, and SIZE. */
+
+static bool
+gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree dest = gimple_call_arg (stmt, 0);
+ tree src = gimple_call_arg (stmt, 1);
+ tree len = gimple_call_arg (stmt, 2);
+ tree size = gimple_call_arg (stmt, 3);
+ tree fn;
+ const char *p;
+
+ p = c_getstr (src);
+ /* If the SRC parameter is "" or if LEN is 0, return DEST. */
+ if ((p && *p == '\0')
+ || integer_zerop (len))
+ {
+ replace_call_with_value (gsi, dest);
+ return true;
+ }
+
+ if (! tree_fits_uhwi_p (size))
+ return false;
+
+ if (! integer_all_onesp (size))
+ {
+ tree src_len = c_strlen (src, 1);
+ if (src_len
+ && tree_fits_uhwi_p (src_len)
+ && tree_fits_uhwi_p (len)
+ && ! tree_int_cst_lt (len, src_len))
+ {
+ /* If LEN >= strlen (SRC), optimize into __strcat_chk. */
+ fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 3, dest, src, size);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ return false;
+ }
+
+ /* If __builtin_strncat_chk is used, assume strncat is available. */
+ fn = builtin_decl_explicit (BUILT_IN_STRNCAT);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 3, dest, src, len);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
/* Fold a call to the fputs builtin. ARG0 and ARG1 are the arguments
to the call. IGNORE is true if the value returned
by the builtin will be ignored. UNLOCKED is true is true if this
static bool
gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
- location_t loc ATTRIBUTE_UNUSED,
tree arg0, tree arg1,
- bool ignore, bool unlocked, tree len)
+ bool unlocked)
{
+ gimple stmt = gsi_stmt (*gsi);
+
/* If we're using an unlocked function, assume the other unlocked
functions exist explicitly. */
tree const fn_fputc = (unlocked
: builtin_decl_implicit (BUILT_IN_FWRITE));
/* If the return value is used, don't do the transformation. */
- if (!ignore)
+ if (gimple_call_lhs (stmt))
return false;
- if (! len)
- len = c_strlen (arg0, 0);
-
/* Get the length of the string passed to fputs. If the length
can't be determined, punt. */
+ tree len = get_maxval_strlen (arg0, 0);
if (!len
|| TREE_CODE (len) != INTEGER_CST)
return false;
static bool
gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
- location_t loc,
tree dest, tree src, tree len, tree size,
- tree maxlen, bool ignore,
enum built_in_function fcode)
{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
tree fn;
/* If SRC and DEST are the same (and not volatile), return DEST
if (! tree_fits_uhwi_p (size))
return false;
+ tree maxlen = get_maxval_strlen (len, 2);
if (! integer_all_onesp (size))
{
if (! tree_fits_uhwi_p (len))
static bool
gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
- location_t loc, tree dest,
+ tree dest,
tree src, tree size,
- tree maxlen, bool ignore,
enum built_in_function fcode)
{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
tree len, fn;
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (! tree_fits_uhwi_p (size))
return false;
+ tree maxlen = get_maxval_strlen (src, 1);
if (! integer_all_onesp (size))
{
len = c_strlen (src, 1);
static bool
gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
tree dest, tree src,
- tree len, tree size, tree maxlen, bool ignore,
+ tree len, tree size,
enum built_in_function fcode)
{
+ gimple stmt = gsi_stmt (*gsi);
+ bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
tree fn;
if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
if (! tree_fits_uhwi_p (size))
return false;
+ tree maxlen = get_maxval_strlen (len, 2);
if (! integer_all_onesp (size))
{
if (! tree_fits_uhwi_p (len))
return true;
}
+/* Fold function call to builtin stpcpy with arguments DEST and SRC.
+ Return NULL_TREE if no simplification can be made. */
+
+static bool
+gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ location_t loc = gimple_location (stmt);
+ tree dest = gimple_call_arg (stmt, 0);
+ tree src = gimple_call_arg (stmt, 1);
+ tree fn, len, lenp1;
+
+ /* If the result is unused, replace stpcpy with strcpy. */
+ if (gimple_call_lhs (stmt) == NULL_TREE)
+ {
+ tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+ if (!fn)
+ return false;
+ gimple_call_set_fndecl (stmt, fn);
+ fold_stmt (gsi);
+ return true;
+ }
+
+ len = c_strlen (src, 1);
+ if (!len
+ || TREE_CODE (len) != INTEGER_CST)
+ return false;
+
+ if (optimize_function_for_size_p (cfun)
+ /* If length is zero it's small enough. */
+ && !integer_zerop (len))
+ return false;
+
+ /* If the source has a known length replace stpcpy with memcpy. */
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+
+ gimple_seq stmts = NULL;
+ tree tem = gimple_convert (&stmts, loc, size_type_node, len);
+ lenp1 = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node,
+ tem, build_int_cst (size_type_node, 1));
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ gcall *repl = gimple_build_call (fn, 3, dest, src, lenp1);
+ gimple_set_vuse (repl, gimple_vuse (stmt));
+ gimple_set_vdef (repl, gimple_vdef (stmt));
+ if (gimple_vdef (repl)
+ && TREE_CODE (gimple_vdef (repl)) == SSA_NAME)
+ SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl;
+ gsi_insert_before (gsi, repl, GSI_SAME_STMT);
+ /* Replace the result with dest + len. */
+ stmts = NULL;
+ tem = gimple_convert (&stmts, loc, sizetype, len);
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ gassign *ret = gimple_build_assign (gimple_call_lhs (stmt),
+ POINTER_PLUS_EXPR, dest, tem);
+ gsi_replace (gsi, ret, true);
+ /* Finally fold the memcpy call. */
+ gimple_stmt_iterator gsi2 = *gsi;
+ gsi_prev (&gsi2);
+ fold_stmt (&gsi2);
+ return true;
+}
+
/* Fold a call EXP to {,v}snprintf having NARGS passed as ARGS. Return
NULL_TREE if a normal call should be emitted rather than expanding
the function inline. FCODE is either BUILT_IN_SNPRINTF_CHK or
static bool
gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi,
- tree maxlen, enum built_in_function fcode)
+ enum built_in_function fcode)
{
- gimple stmt = gsi_stmt (*gsi);
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
tree dest, size, len, fn, fmt, flag;
const char *fmt_str;
if (! integer_all_onesp (size))
{
+ tree maxlen = get_maxval_strlen (len, 2);
if (! tree_fits_uhwi_p (len))
{
/* If LEN is not constant, try MAXLEN too.
gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi,
enum built_in_function fcode)
{
- gimple stmt = gsi_stmt (*gsi);
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
tree dest, size, len, fn, fmt, flag;
const char *fmt_str;
unsigned nargs = gimple_call_num_args (stmt);
the caller does not use the returned value of the function. */
static bool
-gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi, tree orig_len)
+gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
tree dest = gimple_call_arg (stmt, 0);
if (!orig)
return false;
- if (gimple_call_lhs (stmt)
- && !orig_len)
+ tree orig_len = NULL_TREE;
+ if (gimple_call_lhs (stmt))
{
- orig_len = c_strlen (orig, 1);
+ orig_len = get_maxval_strlen (orig, 0);
if (!orig_len)
return false;
}
the caller does not use the returned value of the function. */
static bool
-gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi, tree orig_len)
+gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
{
- gimple stmt = gsi_stmt (*gsi);
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
tree dest = gimple_call_arg (stmt, 0);
tree destsize = gimple_call_arg (stmt, 1);
tree fmt = gimple_call_arg (stmt, 2);
if (!orig)
return false;
- if (!orig_len)
- {
- orig_len = c_strlen (orig, 1);
- if (!orig_len)
- return false;
- }
+ tree orig_len = get_maxval_strlen (orig, 0);
+ if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
+ return false;
/* We could expand this as
memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0';
return false;
}
+/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins.
+ FP, FMT, and ARG are the arguments to the call. We don't fold calls with
+ more than 3 arguments, and ARG may be null in the 2-argument case.
-/* Fold a call to __builtin_strlen with known length LEN. */
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. FCODE is the BUILT_IN_*
+ code of the function to be simplified. */
-static bool
-gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi, tree len)
+static bool
+gimple_fold_builtin_fprintf (gimple_stmt_iterator *gsi,
+ tree fp, tree fmt, tree arg,
+ enum built_in_function fcode)
{
- if (!len)
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree fn_fputc, fn_fputs;
+ const char *fmt_str = NULL;
+
+ /* If the return value is used, don't do the transformation. */
+ if (gimple_call_lhs (stmt) != NULL_TREE)
+ return false;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return false;
+
+ if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
{
- gimple stmt = gsi_stmt (*gsi);
- len = c_strlen (gimple_call_arg (stmt, 0), 0);
+ /* If we're using an unlocked function, assume the other
+ unlocked functions exist explicitly. */
+ fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED);
+ fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED);
}
- if (!len)
+ else
+ {
+ fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
+ fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
+ }
+
+ if (!init_target_chars ())
return false;
- replace_call_with_value (gsi, len);
- return true;
-}
+ /* If the format doesn't contain % args or %%, use strcpy. */
+ if (strchr (fmt_str, target_percent) == NULL)
+ {
+ if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
+ && arg)
+ return false;
-/* Fold builtins at *GSI with knowledge about a length argument. */
+ /* If the format specifier was "", fprintf does nothing. */
+ if (fmt_str[0] == '\0')
+ {
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+ }
-static bool
-gimple_fold_builtin_with_strlen (gimple_stmt_iterator *gsi)
-{
- gimple stmt = gsi_stmt (*gsi);
- tree val[4];
- tree a;
- int arg_idx, type;
- bitmap visited;
- bool ignore;
- location_t loc = gimple_location (stmt);
+ /* When "string" doesn't contain %, replace all cases of
+ fprintf (fp, string) with fputs (string, fp). The fputs
+ builtin will take care of special cases like length == 1. */
+ if (fn_fputs)
+ {
+ gcall *repl = gimple_build_call (fn_fputs, 2, fmt, fp);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
- ignore = (gimple_call_lhs (stmt) == NULL);
+ /* The other optimizations can be done only on the non-va_list variants. */
+ else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK)
+ return false;
- /* Limit the work only for builtins we know how to simplify. */
- tree callee = gimple_call_fndecl (stmt);
- switch (DECL_FUNCTION_CODE (callee))
+ /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */
+ else if (strcmp (fmt_str, target_percent_s) == 0)
{
- case BUILT_IN_STRLEN:
- case BUILT_IN_FPUTS:
- case BUILT_IN_FPUTS_UNLOCKED:
- arg_idx = 0;
- type = 0;
- break;
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STRCAT:
- arg_idx = 1;
- type = 0;
- break;
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- arg_idx = 2;
- type = 2;
- break;
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- arg_idx = 1;
- type = 1;
- break;
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- arg_idx = 1;
- type = 2;
- break;
- case BUILT_IN_SPRINTF:
- arg_idx = 2;
- type = 0;
- break;
- case BUILT_IN_SNPRINTF:
- arg_idx = 3;
- type = 0;
- break;
- default:
- return false;
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
+ if (fn_fputs)
+ {
+ gcall *repl = gimple_build_call (fn_fputs, 2, arg, fp);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
}
- int nargs = gimple_call_num_args (stmt);
+ /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */
+ else if (strcmp (fmt_str, target_percent_c) == 0)
+ {
+ if (!arg
+ || ! useless_type_conversion_p (integer_type_node, TREE_TYPE (arg)))
+ return false;
+ if (fn_fputc)
+ {
+ gcall *repl = gimple_build_call (fn_fputc, 2, arg, fp);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
- /* Try to use the dataflow information gathered by the CCP process. */
- visited = BITMAP_ALLOC (NULL);
- bitmap_clear (visited);
+ return false;
+}
+
+/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
+ FMT and ARG are the arguments to the call; we don't fold cases with
+ more than 2 arguments, and ARG may be null if this is a 1-argument case.
- memset (val, 0, sizeof (val));
- if (arg_idx < nargs)
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. FCODE is the BUILT_IN_*
+ code of the function to be simplified. */
+
+static bool
+gimple_fold_builtin_printf (gimple_stmt_iterator *gsi, tree fmt,
+ tree arg, enum built_in_function fcode)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree fn_putchar, fn_puts, newarg;
+ const char *fmt_str = NULL;
+
+ /* If the return value is used, don't do the transformation. */
+ if (gimple_call_lhs (stmt) != NULL_TREE)
+ return false;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return false;
+
+ if (fcode == BUILT_IN_PRINTF_UNLOCKED)
{
- a = gimple_call_arg (stmt, arg_idx);
- if (!get_maxval_strlen (a, &val[arg_idx], visited, type)
- || !is_gimple_val (val[arg_idx]))
- val[arg_idx] = NULL_TREE;
+ /* If we're using an unlocked function, assume the other
+ unlocked functions exist explicitly. */
+ fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED);
+ fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED);
+ }
+ else
+ {
+ fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR);
+ fn_puts = builtin_decl_implicit (BUILT_IN_PUTS);
}
- BITMAP_FREE (visited);
+ if (!init_target_chars ())
+ return false;
- switch (DECL_FUNCTION_CODE (callee))
+ if (strcmp (fmt_str, target_percent_s) == 0
+ || strchr (fmt_str, target_percent) == NULL)
{
- case BUILT_IN_STRLEN:
- return gimple_fold_builtin_strlen (gsi, val[0]);
+ const char *str;
- case BUILT_IN_STRCPY:
- return gimple_fold_builtin_strcpy (gsi, loc,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- val[1]);
-
- case BUILT_IN_STRNCPY:
- return gimple_fold_builtin_strncpy (gsi, loc,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- val[1]);
+ if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return false;
- case BUILT_IN_STRCAT:
- return gimple_fold_builtin_strcat (gsi, loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- val[1]);
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
- case BUILT_IN_FPUTS:
- return gimple_fold_builtin_fputs (gsi, loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- ignore, false, val[0]);
+ str = c_getstr (arg);
+ if (str == NULL)
+ return false;
+ }
+ else
+ {
+ /* The format specifier doesn't contain any '%' characters. */
+ if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK
+ && arg)
+ return false;
+ str = fmt_str;
+ }
- case BUILT_IN_FPUTS_UNLOCKED:
- return gimple_fold_builtin_fputs (gsi, loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- ignore, true, val[0]);
+ /* If the string was "", printf does nothing. */
+ if (str[0] == '\0')
+ {
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+ }
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- return gimple_fold_builtin_memory_chk (gsi, loc,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- gimple_call_arg (stmt, 3),
- val[2], ignore,
- DECL_FUNCTION_CODE (callee));
+ /* If the string has length of 1, call putchar. */
+ if (str[1] == '\0')
+ {
+ /* Given printf("c"), (where c is any one character,)
+ convert "c"[0] to an int and pass that to the replacement
+ function. */
+ newarg = build_int_cst (integer_type_node, str[0]);
+ if (fn_putchar)
+ {
+ gcall *repl = gimple_build_call (fn_putchar, 1, newarg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+ else
+ {
+ /* If the string was "string\n", call puts("string"). */
+ size_t len = strlen (str);
+ if ((unsigned char)str[len - 1] == target_newline
+ && (size_t) (int) len == len
+ && (int) len > 0)
+ {
+ char *newstr;
+ tree offset_node, string_cst;
+
+ /* Create a NUL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ newarg = build_string_literal (len, str);
+ string_cst = string_constant (newarg, &offset_node);
+ gcc_checking_assert (string_cst
+ && (TREE_STRING_LENGTH (string_cst)
+ == (int) len)
+ && integer_zerop (offset_node)
+ && (unsigned char)
+ TREE_STRING_POINTER (string_cst)[len - 1]
+ == target_newline);
+ /* build_string_literal creates a new STRING_CST,
+ modify it in place to avoid double copying. */
+ newstr = CONST_CAST (char *, TREE_STRING_POINTER (string_cst));
+ newstr[len - 1] = '\0';
+ if (fn_puts)
+ {
+ gcall *repl = gimple_build_call (fn_puts, 1, newarg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+ else
+ /* We'd like to arrange to call fputs(string,stdout) here,
+ but we need stdout and don't have a way to get it yet. */
+ return false;
+ }
+ }
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- return gimple_fold_builtin_stxcpy_chk (gsi, loc,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- val[1], ignore,
- DECL_FUNCTION_CODE (callee));
+ /* The other optimizations can be done only on the non-va_list variants. */
+ else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return false;
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- return gimple_fold_builtin_stxncpy_chk (gsi,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- gimple_call_arg (stmt, 3),
- val[2], ignore,
- DECL_FUNCTION_CODE (callee));
+ /* If the format specifier was "%s\n", call __builtin_puts(arg). */
+ else if (strcmp (fmt_str, target_percent_s_newline) == 0)
+ {
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
+ if (fn_puts)
+ {
+ gcall *repl = gimple_build_call (fn_puts, 1, arg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- return gimple_fold_builtin_snprintf_chk (gsi, val[1],
- DECL_FUNCTION_CODE (callee));
- case BUILT_IN_SNPRINTF:
- return gimple_fold_builtin_snprintf (gsi, val[3]);
- case BUILT_IN_SPRINTF:
- return gimple_fold_builtin_sprintf (gsi, val[2]);
- default:
- gcc_unreachable ();
+ /* If the format specifier was "%c", call __builtin_putchar(arg). */
+ else if (strcmp (fmt_str, target_percent_c) == 0)
+ {
+ if (!arg || ! useless_type_conversion_p (integer_type_node,
+ TREE_TYPE (arg)))
+ return false;
+ if (fn_putchar)
+ {
+ gcall *repl = gimple_build_call (fn_putchar, 1, arg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
}
return false;
}
+
+/* Fold a call to __builtin_strlen with known length LEN. */
+
+static bool
+gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
+ if (!len)
+ return false;
+ len = force_gimple_operand_gsi (gsi, len, true, NULL, true, GSI_SAME_STMT);
+ replace_call_with_value (gsi, len);
+ return true;
+}
+
+
/* Fold the non-target builtin at *GSI and return whether any simplification
was made. */
static bool
gimple_fold_builtin (gimple_stmt_iterator *gsi)
{
- gimple stmt = gsi_stmt (*gsi);
+ gcall *stmt = as_a <gcall *>(gsi_stmt (*gsi));
tree callee = gimple_call_fndecl (stmt);
/* Give up for always_inline inline builtins until they are
if (avoid_folding_inline_builtin (callee))
return false;
- if (gimple_fold_builtin_with_strlen (gsi))
- return true;
-
- switch (DECL_FUNCTION_CODE (callee))
+ unsigned n = gimple_call_num_args (stmt);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+ switch (fcode)
{
case BUILT_IN_BZERO:
return gimple_fold_builtin_memset (gsi, integer_zero_node,
gimple_call_arg (stmt, 1), 3);
case BUILT_IN_SPRINTF_CHK:
case BUILT_IN_VSPRINTF_CHK:
- return gimple_fold_builtin_sprintf_chk (gsi, DECL_FUNCTION_CODE (callee));
+ return gimple_fold_builtin_sprintf_chk (gsi, fcode);
+ case BUILT_IN_STRCAT_CHK:
+ return gimple_fold_builtin_strcat_chk (gsi);
+ case BUILT_IN_STRNCAT_CHK:
+ return gimple_fold_builtin_strncat_chk (gsi);
+ case BUILT_IN_STRLEN:
+ return gimple_fold_builtin_strlen (gsi);
+ case BUILT_IN_STRCPY:
+ return gimple_fold_builtin_strcpy (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1));
+ case BUILT_IN_STRNCPY:
+ return gimple_fold_builtin_strncpy (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2));
+ case BUILT_IN_STRCAT:
+ return gimple_fold_builtin_strcat (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1));
+ case BUILT_IN_STRNCAT:
+ return gimple_fold_builtin_strncat (gsi);
+ case BUILT_IN_FPUTS:
+ return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), false);
+ case BUILT_IN_FPUTS_UNLOCKED:
+ return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), true);
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMMOVE_CHK:
+ case BUILT_IN_MEMSET_CHK:
+ return gimple_fold_builtin_memory_chk (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2),
+ gimple_call_arg (stmt, 3),
+ fcode);
+ case BUILT_IN_STPCPY:
+ return gimple_fold_builtin_stpcpy (gsi);
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STPCPY_CHK:
+ return gimple_fold_builtin_stxcpy_chk (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2),
+ fcode);
+ case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STPNCPY_CHK:
+ return gimple_fold_builtin_stxncpy_chk (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2),
+ gimple_call_arg (stmt, 3),
+ fcode);
+ case BUILT_IN_SNPRINTF_CHK:
+ case BUILT_IN_VSNPRINTF_CHK:
+ return gimple_fold_builtin_snprintf_chk (gsi, fcode);
+ case BUILT_IN_SNPRINTF:
+ return gimple_fold_builtin_snprintf (gsi);
+ case BUILT_IN_SPRINTF:
+ return gimple_fold_builtin_sprintf (gsi);
+ case BUILT_IN_FPRINTF:
+ case BUILT_IN_FPRINTF_UNLOCKED:
+ case BUILT_IN_VFPRINTF:
+ if (n == 2 || n == 3)
+ return gimple_fold_builtin_fprintf (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ n == 3
+ ? gimple_call_arg (stmt, 2)
+ : NULL_TREE,
+ fcode);
+ break;
+ case BUILT_IN_FPRINTF_CHK:
+ case BUILT_IN_VFPRINTF_CHK:
+ if (n == 3 || n == 4)
+ return gimple_fold_builtin_fprintf (gsi,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 2),
+ n == 4
+ ? gimple_call_arg (stmt, 3)
+ : NULL_TREE,
+ fcode);
+ break;
+ case BUILT_IN_PRINTF:
+ case BUILT_IN_PRINTF_UNLOCKED:
+ case BUILT_IN_VPRINTF:
+ if (n == 1 || n == 2)
+ return gimple_fold_builtin_printf (gsi, gimple_call_arg (stmt, 0),
+ n == 2
+ ? gimple_call_arg (stmt, 1)
+ : NULL_TREE, fcode);
+ break;
+ case BUILT_IN_PRINTF_CHK:
+ case BUILT_IN_VPRINTF_CHK:
+ if (n == 2 || n == 3)
+ return gimple_fold_builtin_printf (gsi, gimple_call_arg (stmt, 1),
+ n == 3
+ ? gimple_call_arg (stmt, 2)
+ : NULL_TREE, fcode);
default:;
}
return false;
}
+/* Return true if ARG0 CODE ARG1 in infinite signed precision operation
+ doesn't fit into TYPE. The test for overflow should be regardless of
+ -fwrapv, and even for unsigned types. */
+
+bool
+arith_overflowed_p (enum tree_code code, const_tree type,
+ const_tree arg0, const_tree arg1)
+{
+ typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION * 2) widest2_int;
+ typedef generic_wide_int <wi::extended_tree <WIDE_INT_MAX_PRECISION * 2> >
+ widest2_int_cst;
+ widest2_int warg0 = widest2_int_cst (arg0);
+ widest2_int warg1 = widest2_int_cst (arg1);
+ widest2_int wres;
+ switch (code)
+ {
+ case PLUS_EXPR: wres = wi::add (warg0, warg1); break;
+ case MINUS_EXPR: wres = wi::sub (warg0, warg1); break;
+ case MULT_EXPR: wres = wi::mul (warg0, warg1); break;
+ default: gcc_unreachable ();
+ }
+ signop sign = TYPE_SIGN (type);
+ if (sign == UNSIGNED && wi::neg_p (wres))
+ return true;
+ return wi::min_precision (wres, sign) > TYPE_PRECISION (type);
+}
+
/* Attempt to fold a call statement referenced by the statement iterator GSI.
The statement may be replaced by another statement, e.g., if the call
simplifies to a constant value. Return true if any changes were made.
static bool
gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
{
- gimple stmt = gsi_stmt (*gsi);
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
tree callee;
bool changed = false;
unsigned i;
{
if (dump_file && virtual_method_call_p (callee)
&& !possible_polymorphic_call_target_p
- (callee, cgraph_node::get (gimple_call_addr_fndecl
- (OBJ_TYPE_REF_EXPR (callee)))))
+ (callee, stmt, cgraph_node::get (gimple_call_addr_fndecl
+ (OBJ_TYPE_REF_EXPR (callee)))))
{
fprintf (dump_file,
"Type inheritance inconsistent devirtualization of ");
{
if (TREE_CODE (lhs) == SSA_NAME)
{
- tree var = create_tmp_var (TREE_TYPE (lhs), NULL);
+ tree var = create_tmp_var (TREE_TYPE (lhs));
tree def = get_or_create_ssa_default_def (cfun, var);
gimple new_stmt = gimple_build_assign (lhs, def);
gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
}
gimple_call_set_lhs (stmt, NULL_TREE);
}
+ maybe_remove_unused_call_args (cfun, stmt);
}
else
{
gimple_set_location (new_stmt, gimple_location (stmt));
if (lhs && TREE_CODE (lhs) == SSA_NAME)
{
- tree var = create_tmp_var (TREE_TYPE (lhs), NULL);
+ tree var = create_tmp_var (TREE_TYPE (lhs));
tree def = get_or_create_ssa_default_def (cfun, var);
/* To satisfy condition for
gsi_insert_before (gsi, new_stmt, GSI_NEW_STMT);
}
else
- gsi_replace (gsi, new_stmt, true);
+ {
+ gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+ gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+ gsi_replace (gsi, new_stmt, false);
+ }
return true;
}
}
}
}
+ /* Check for indirect calls that became direct calls, and then
+ no longer require a static chain. */
+ if (gimple_call_chain (stmt))
+ {
+ tree fn = gimple_call_fndecl (stmt);
+ if (fn && !DECL_STATIC_CHAIN (fn))
+ {
+ gimple_call_set_chain (stmt, NULL);
+ changed = true;
+ }
+ else
+ {
+ tree tmp = maybe_fold_reference (gimple_call_chain (stmt), false);
+ if (tmp)
+ {
+ gimple_call_set_chain (stmt, tmp);
+ changed = true;
+ }
+ }
+ }
+
if (inplace)
return changed;
{
enum tree_code subcode = ERROR_MARK;
tree result = NULL_TREE;
+ bool cplx_result = false;
+ tree overflow = NULL_TREE;
switch (gimple_call_internal_fn (stmt))
{
case IFN_BUILTIN_EXPECT:
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2));
break;
+ case IFN_UBSAN_OBJECT_SIZE:
+ if (integer_all_onesp (gimple_call_arg (stmt, 2))
+ || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+ && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
+ && tree_int_cst_le (gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2))))
+ {
+ gsi_replace (gsi, gimple_build_nop (), true);
+ unlink_stmt_vdef (stmt);
+ release_defs (stmt);
+ return true;
+ }
+ break;
case IFN_UBSAN_CHECK_ADD:
subcode = PLUS_EXPR;
break;
case IFN_UBSAN_CHECK_MUL:
subcode = MULT_EXPR;
break;
+ case IFN_ADD_OVERFLOW:
+ subcode = PLUS_EXPR;
+ cplx_result = true;
+ break;
+ case IFN_SUB_OVERFLOW:
+ subcode = MINUS_EXPR;
+ cplx_result = true;
+ break;
+ case IFN_MUL_OVERFLOW:
+ subcode = MULT_EXPR;
+ cplx_result = true;
+ break;
default:
break;
}
{
tree arg0 = gimple_call_arg (stmt, 0);
tree arg1 = gimple_call_arg (stmt, 1);
+ tree type = TREE_TYPE (arg0);
+ if (cplx_result)
+ {
+ tree lhs = gimple_call_lhs (stmt);
+ if (lhs == NULL_TREE)
+ type = NULL_TREE;
+ else
+ type = TREE_TYPE (TREE_TYPE (lhs));
+ }
+ if (type == NULL_TREE)
+ ;
/* x = y + 0; x = y - 0; x = y * 0; */
- if (integer_zerop (arg1))
- result = subcode == MULT_EXPR
- ? build_zero_cst (TREE_TYPE (arg0))
- : arg0;
+ else if (integer_zerop (arg1))
+ result = subcode == MULT_EXPR ? integer_zero_node : arg0;
/* x = 0 + y; x = 0 * y; */
else if (subcode != MINUS_EXPR && integer_zerop (arg0))
- result = subcode == MULT_EXPR
- ? build_zero_cst (TREE_TYPE (arg0))
- : arg1;
+ result = subcode == MULT_EXPR ? integer_zero_node : arg1;
/* x = y - y; */
else if (subcode == MINUS_EXPR && operand_equal_p (arg0, arg1, 0))
- result = build_zero_cst (TREE_TYPE (arg0));
+ result = integer_zero_node;
/* x = y * 1; x = 1 * y; */
- else if (subcode == MULT_EXPR)
+ else if (subcode == MULT_EXPR && integer_onep (arg1))
+ result = arg0;
+ else if (subcode == MULT_EXPR && integer_onep (arg0))
+ result = arg1;
+ else if (TREE_CODE (arg0) == INTEGER_CST
+ && TREE_CODE (arg1) == INTEGER_CST)
+ {
+ if (cplx_result)
+ result = int_const_binop (subcode, fold_convert (type, arg0),
+ fold_convert (type, arg1));
+ else
+ result = int_const_binop (subcode, arg0, arg1);
+ if (result && arith_overflowed_p (subcode, type, arg0, arg1))
+ {
+ if (cplx_result)
+ overflow = build_one_cst (type);
+ else
+ result = NULL_TREE;
+ }
+ }
+ if (result)
{
- if (integer_onep (arg1))
- result = arg0;
- else if (integer_onep (arg0))
- result = arg1;
+ if (result == integer_zero_node)
+ result = build_zero_cst (type);
+ else if (cplx_result && TREE_TYPE (result) != type)
+ {
+ if (TREE_CODE (result) == INTEGER_CST)
+ {
+ if (arith_overflowed_p (PLUS_EXPR, type, result,
+ integer_zero_node))
+ overflow = build_one_cst (type);
+ }
+ else if ((!TYPE_UNSIGNED (TREE_TYPE (result))
+ && TYPE_UNSIGNED (type))
+ || (TYPE_PRECISION (type)
+ < (TYPE_PRECISION (TREE_TYPE (result))
+ + (TYPE_UNSIGNED (TREE_TYPE (result))
+ && !TYPE_UNSIGNED (type)))))
+ result = NULL_TREE;
+ if (result)
+ result = fold_convert (type, result);
+ }
}
}
+
if (result)
{
+ if (TREE_CODE (result) == INTEGER_CST && TREE_OVERFLOW (result))
+ result = drop_tree_overflow (result);
+ if (cplx_result)
+ {
+ if (overflow == NULL_TREE)
+ overflow = build_zero_cst (TREE_TYPE (result));
+ tree ctype = build_complex_type (TREE_TYPE (result));
+ if (TREE_CODE (result) == INTEGER_CST
+ && TREE_CODE (overflow) == INTEGER_CST)
+ result = build_complex (ctype, result, overflow);
+ else
+ result = build2_loc (gimple_location (stmt), COMPLEX_EXPR,
+ ctype, result, overflow);
+ }
if (!update_call_from_tree (gsi, result))
gimplify_and_update_call_from_tree (gsi, result);
changed = true;
return changed;
}
+
+/* Worker for fold_stmt_1 dispatch to pattern based folding with
+ gimple_simplify.
+
+ Replaces *GSI with the simplification result in RCODE and OPS
+ and the associated statements in *SEQ. Does the replacement
+ according to INPLACE and returns true if the operation succeeded. */
+
+static bool
+replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
+ code_helper rcode, tree *ops,
+ gimple_seq *seq, bool inplace)
+{
+ gimple stmt = gsi_stmt (*gsi);
+
+ /* Play safe and do not allow abnormals to be mentioned in
+ newly created statements. See also maybe_push_res_to_seq. */
+ if ((TREE_CODE (ops[0]) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0]))
+ || (ops[1]
+ && TREE_CODE (ops[1]) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1]))
+ || (ops[2]
+ && TREE_CODE (ops[2]) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])))
+ return false;
+
+ if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
+ {
+ gcc_assert (rcode.is_tree_code ());
+ if (TREE_CODE_CLASS ((enum tree_code)rcode) == tcc_comparison
+ /* GIMPLE_CONDs condition may not throw. */
+ && (!flag_exceptions
+ || !cfun->can_throw_non_call_exceptions
+ || !operation_could_trap_p (rcode,
+ FLOAT_TYPE_P (TREE_TYPE (ops[0])),
+ false, NULL_TREE)))
+ gimple_cond_set_condition (cond_stmt, rcode, ops[0], ops[1]);
+ else if (rcode == SSA_NAME)
+ gimple_cond_set_condition (cond_stmt, NE_EXPR, ops[0],
+ build_zero_cst (TREE_TYPE (ops[0])));
+ else if (rcode == INTEGER_CST)
+ {
+ if (integer_zerop (ops[0]))
+ gimple_cond_make_false (cond_stmt);
+ else
+ gimple_cond_make_true (cond_stmt);
+ }
+ else if (!inplace)
+ {
+ tree res = maybe_push_res_to_seq (rcode, boolean_type_node,
+ ops, seq);
+ if (!res)
+ return false;
+ gimple_cond_set_condition (cond_stmt, NE_EXPR, res,
+ build_zero_cst (TREE_TYPE (res)));
+ }
+ else
+ return false;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "gimple_simplified to ");
+ if (!gimple_seq_empty_p (*seq))
+ print_gimple_seq (dump_file, *seq, 0, TDF_SLIM);
+ print_gimple_stmt (dump_file, gsi_stmt (*gsi),
+ 0, TDF_SLIM);
+ }
+ gsi_insert_seq_before (gsi, *seq, GSI_SAME_STMT);
+ return true;
+ }
+ else if (is_gimple_assign (stmt)
+ && rcode.is_tree_code ())
+ {
+ if (!inplace
+ || gimple_num_ops (stmt) > get_gimple_rhs_num_ops (rcode))
+ {
+ maybe_build_generic_op (rcode,
+ TREE_TYPE (gimple_assign_lhs (stmt)),
+ &ops[0], ops[1], ops[2]);
+ gimple_assign_set_rhs_with_ops (gsi, rcode, ops[0], ops[1], ops[2]);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "gimple_simplified to ");
+ if (!gimple_seq_empty_p (*seq))
+ print_gimple_seq (dump_file, *seq, 0, TDF_SLIM);
+ print_gimple_stmt (dump_file, gsi_stmt (*gsi),
+ 0, TDF_SLIM);
+ }
+ gsi_insert_seq_before (gsi, *seq, GSI_SAME_STMT);
+ return true;
+ }
+ }
+ else if (!inplace)
+ {
+ if (gimple_has_lhs (stmt))
+ {
+ tree lhs = gimple_get_lhs (stmt);
+ if (!maybe_push_res_to_seq (rcode, TREE_TYPE (lhs),
+ ops, seq, lhs))
+ return false;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "gimple_simplified to ");
+ print_gimple_seq (dump_file, *seq, 0, TDF_SLIM);
+ }
+ gsi_replace_with_seq_vops (gsi, *seq);
+ return true;
+ }
+ else
+ gcc_unreachable ();
+ }
+
+ return false;
+}
+
/* Canonicalize MEM_REFs invariant address operand after propagation. */
static bool
accessed is a decl that has the same access semantics as the MEM_REF. */
if (TREE_CODE (*t) == MEM_REF
&& TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR
- && integer_zerop (TREE_OPERAND (*t, 1)))
+ && integer_zerop (TREE_OPERAND (*t, 1))
+ && MR_DEPENDENCE_CLIQUE (*t) == 0)
{
tree decl = TREE_OPERAND (TREE_OPERAND (*t, 0), 0);
tree alias_type = TREE_TYPE (TREE_OPERAND (*t, 1));
distinguishes both cases. */
static bool
-fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
+fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
{
bool changed = false;
gimple stmt = gsi_stmt (*gsi);
}
case GIMPLE_ASM:
{
- for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
+ gasm *asm_stmt = as_a <gasm *> (stmt);
+ for (i = 0; i < gimple_asm_noutputs (asm_stmt); ++i)
{
- tree link = gimple_asm_output_op (stmt, i);
+ tree link = gimple_asm_output_op (asm_stmt, i);
tree op = TREE_VALUE (link);
if (REFERENCE_CLASS_P (op)
&& maybe_canonicalize_mem_ref_addr (&TREE_VALUE (link)))
changed = true;
}
- for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
+ for (i = 0; i < gimple_asm_ninputs (asm_stmt); ++i)
{
- tree link = gimple_asm_input_op (stmt, i);
+ tree link = gimple_asm_input_op (asm_stmt, i);
tree op = TREE_VALUE (link);
if ((REFERENCE_CLASS_P (op)
|| TREE_CODE (op) == ADDR_EXPR)
default:;
}
+ /* Dispatch to pattern-based folding. */
+ if (!inplace
+ || is_gimple_assign (stmt)
+ || gimple_code (stmt) == GIMPLE_COND)
+ {
+ gimple_seq seq = NULL;
+ code_helper rcode;
+ tree ops[3] = {};
+ if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq,
+ valueize, valueize))
+ {
+ if (replace_stmt_with_simplification (gsi, rcode, ops, &seq, inplace))
+ changed = true;
+ else
+ gimple_seq_discard (seq);
+ }
+ }
+
+ stmt = gsi_stmt (*gsi);
+
/* Fold the main computation performed by the statement. */
switch (gimple_code (stmt))
{
}
case GIMPLE_COND:
- changed |= fold_gimple_cond (stmt);
+ changed |= fold_gimple_cond (as_a <gcond *> (stmt));
break;
case GIMPLE_CALL:
case GIMPLE_ASM:
/* Fold *& in asm operands. */
{
+ gasm *asm_stmt = as_a <gasm *> (stmt);
size_t noutputs;
const char **oconstraints;
const char *constraint;
bool allows_mem, allows_reg;
- noutputs = gimple_asm_noutputs (stmt);
+ noutputs = gimple_asm_noutputs (asm_stmt);
oconstraints = XALLOCAVEC (const char *, noutputs);
- for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
+ for (i = 0; i < gimple_asm_noutputs (asm_stmt); ++i)
{
- tree link = gimple_asm_output_op (stmt, i);
+ tree link = gimple_asm_output_op (asm_stmt, i);
tree op = TREE_VALUE (link);
oconstraints[i]
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
changed = true;
}
}
- for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
+ for (i = 0; i < gimple_asm_ninputs (asm_stmt); ++i)
{
- tree link = gimple_asm_input_op (stmt, i);
+ tree link = gimple_asm_input_op (asm_stmt, i);
tree op = TREE_VALUE (link);
constraint
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
return changed;
}
+/* Valueziation callback that ends up not following SSA edges. */
+
+tree
+no_follow_ssa_edges (tree)
+{
+ return NULL_TREE;
+}
+
+/* Valueization callback that ends up following single-use SSA edges only. */
+
+tree
+follow_single_use_edges (tree val)
+{
+ if (TREE_CODE (val) == SSA_NAME
+ && !has_single_use (val))
+ return NULL_TREE;
+ return val;
+}
+
/* Fold the statement pointed to by GSI. In some cases, this function may
replace the whole statement with a new one. Returns true iff folding
makes any changes.
bool
fold_stmt (gimple_stmt_iterator *gsi)
{
- return fold_stmt_1 (gsi, false);
+ return fold_stmt_1 (gsi, false, no_follow_ssa_edges);
+}
+
+bool
+fold_stmt (gimple_stmt_iterator *gsi, tree (*valueize) (tree))
+{
+ return fold_stmt_1 (gsi, false, valueize);
}
/* Perform the minimal folding on statement *GSI. Only operations like
fold_stmt_inplace (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
- bool changed = fold_stmt_1 (gsi, true);
+ bool changed = fold_stmt_1 (gsi, true, no_follow_ssa_edges);
gcc_assert (gsi_stmt (*gsi) == stmt);
return changed;
}
else if (TREE_CODE (expr) == SSA_NAME)
return fold_build2 (EQ_EXPR, boolean_type_node, expr,
build_int_cst (TREE_TYPE (expr), 0));
- else if (TREE_CODE_CLASS (TREE_CODE (expr)) == tcc_comparison)
+ else if (COMPARISON_CLASS_P (expr))
return fold_build2 (invert_tree_comparison (TREE_CODE (expr), false),
boolean_type_node,
TREE_OPERAND (expr, 0),
else if (TREE_CODE (expr) == SSA_NAME)
return fold_build2 (NE_EXPR, boolean_type_node, expr,
build_int_cst (TREE_TYPE (expr), 0));
- else if (TREE_CODE_CLASS (TREE_CODE (expr)) == tcc_comparison)
+ else if (COMPARISON_CLASS_P (expr))
return fold_build2 (TREE_CODE (expr),
boolean_type_node,
TREE_OPERAND (expr, 0),
/* Check the cases where at least one of the operands is a comparison.
These are a bit smarter than operand_equal_p in that they apply some
identifies on SSA_NAMEs. */
- if (TREE_CODE_CLASS (TREE_CODE (op2)) == tcc_comparison
+ if (COMPARISON_CLASS_P (op2)
&& same_bool_comparison_p (op1, TREE_CODE (op2),
TREE_OPERAND (op2, 0),
TREE_OPERAND (op2, 1)))
return true;
- if (TREE_CODE_CLASS (TREE_CODE (op1)) == tcc_comparison
+ if (COMPARISON_CLASS_P (op1)
&& same_bool_comparison_p (op2, TREE_CODE (op1),
TREE_OPERAND (op1, 0),
TREE_OPERAND (op1, 1)))
to avoid the indirect function call overhead. */
tree
-gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
+gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
+ tree (*gvalueize) (tree))
{
+ code_helper rcode;
+ tree ops[3] = {};
+ /* ??? The SSA propagators do not correctly deal with following SSA use-def
+ edges if there are intermediate VARYING defs. For this reason
+ do not follow SSA edges here even though SCCVN can technically
+ just deal fine with that. */
+ if (gimple_simplify (stmt, &rcode, ops, NULL, gvalueize, valueize)
+ && rcode.is_tree_code ()
+ && (TREE_CODE_LENGTH ((tree_code) rcode) == 0
+ || ((tree_code) rcode) == ADDR_EXPR)
+ && is_gimple_val (ops[0]))
+ {
+ tree res = ops[0];
+ if (dump_file && dump_flags & TDF_DETAILS)
+ {
+ fprintf (dump_file, "Match-and-simplified ");
+ print_gimple_expr (dump_file, stmt, 0, TDF_SLIM);
+ fprintf (dump_file, " to ");
+ print_generic_expr (dump_file, res, 0);
+ fprintf (dump_file, "\n");
+ }
+ return res;
+ }
+
location_t loc = gimple_location (stmt);
switch (gimple_code (stmt))
{
}
case GIMPLE_UNARY_RHS:
- {
- /* Handle unary operators that can appear in GIMPLE form.
- Note that we know the single operand must be a constant,
- so this should almost always return a simplified RHS. */
- tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
-
- return
- fold_unary_ignore_overflow_loc (loc, subcode,
- gimple_expr_type (stmt), op0);
- }
+ return NULL_TREE;
case GIMPLE_BINARY_RHS:
{
case GIMPLE_CALL:
{
tree fn;
+ gcall *call_stmt = as_a <gcall *> (stmt);
if (gimple_call_internal_p (stmt))
{
TREE_OPERAND (fn, 0)))
{
tree *args = XALLOCAVEC (tree, gimple_call_num_args (stmt));
- tree call, retval;
+ tree retval;
unsigned i;
for (i = 0; i < gimple_call_num_args (stmt); ++i)
args[i] = (*valueize) (gimple_call_arg (stmt, i));
- call = build_call_array_loc (loc,
- gimple_call_return_type (stmt),
+ retval = fold_builtin_call_array (loc,
+ gimple_call_return_type (call_stmt),
fn, gimple_call_num_args (stmt), args);
- retval = fold_call_expr (EXPR_LOCATION (call), call, false);
if (retval)
{
/* fold_call_expr wraps the result inside a NOP_EXPR. */
STRIP_NOPS (retval);
- retval = fold_convert (gimple_call_return_type (stmt), retval);
+ retval = fold_convert (gimple_call_return_type (call_stmt),
+ retval);
}
return retval;
}
/* The following set of functions are supposed to fold references using
their constant initializers. */
-static tree fold_ctor_reference (tree type, tree ctor,
- unsigned HOST_WIDE_INT offset,
- unsigned HOST_WIDE_INT size, tree);
-
/* See if we can find constructor defining value of BASE.
When we know the consructor with constant offset (such as
base is array[40] and we do know constructor of array), then
/* CTOR is value initializing memory, fold reference of type TYPE and size SIZE
to the memory at bit OFFSET. */
-static tree
+tree
fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
unsigned HOST_WIDE_INT size, tree from_decl)
{
ret = canonicalize_constructor_val (unshare_expr (ctor), from_decl);
ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
if (ret)
- STRIP_NOPS (ret);
+ STRIP_USELESS_TYPE_CONVERSION (ret);
return ret;
}
/* For constants and byte-aligned/sized reads try to go through
if (TREE_THIS_VOLATILE (t))
return NULL_TREE;
- if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration)
+ if (DECL_P (t))
return get_symbol_constant_value (t);
tem = fold_read_from_constant_string (t);
if (TREE_CODE (v) != VAR_DECL
|| !DECL_VIRTUAL_P (v))
{
- gcc_assert (in_lto_p);
/* Pass down that we lost track of the target. */
if (can_refer)
*can_refer = false;
CASE_FLT_FN (BUILT_IN_SQRT):
/* sqrt(-0.0) is -0.0, and sqrt is not defined over other
nonnegative inputs. */
- if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (val))))
+ if (!HONOR_SIGNED_ZEROS (val))
return true;
break;
if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
gimple_assign_set_rhs_code (stmt, PLUS_EXPR);
gimple_seq_add_stmt (&stmts, stmt);
- gimple cvt = gimple_build_assign_with_ops
- (NOP_EXPR, lhs, gimple_assign_lhs (stmt), NULL_TREE);
+ gimple cvt = gimple_build_assign (lhs, NOP_EXPR, gimple_assign_lhs (stmt));
gimple_seq_add_stmt (&stmts, cvt);
return stmts;
}
+
+
+/* The valueization hook we use for the gimple_build API simplification.
+ This makes us match fold_buildN behavior by only combining with
+ statements in the sequence(s) we are currently building. */
+
+static tree
+gimple_build_valueize (tree op)
+{
+ if (gimple_bb (SSA_NAME_DEF_STMT (op)) == NULL)
+ return op;
+ return NULL_TREE;
+}
+
+/* Build the expression CODE OP0 of type TYPE with location LOC,
+ simplifying it first if possible. Returns the built
+ expression value and appends statements possibly defining it
+ to SEQ. */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+ enum tree_code code, tree type, tree op0)
+{
+ tree res = gimple_simplify (code, type, op0, seq, gimple_build_valueize);
+ if (!res)
+ {
+ if (gimple_in_ssa_p (cfun))
+ res = make_ssa_name (type);
+ else
+ res = create_tmp_reg (type);
+ gimple stmt;
+ if (code == REALPART_EXPR
+ || code == IMAGPART_EXPR
+ || code == VIEW_CONVERT_EXPR)
+ stmt = gimple_build_assign (res, code, build1 (code, type, op0));
+ else
+ stmt = gimple_build_assign (res, code, op0);
+ gimple_set_location (stmt, loc);
+ gimple_seq_add_stmt_without_update (seq, stmt);
+ }
+ return res;
+}
+
+/* Build the expression OP0 CODE OP1 of type TYPE with location LOC,
+ simplifying it first if possible. Returns the built
+ expression value and appends statements possibly defining it
+ to SEQ. */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+ enum tree_code code, tree type, tree op0, tree op1)
+{
+ tree res = gimple_simplify (code, type, op0, op1, seq, gimple_build_valueize);
+ if (!res)
+ {
+ if (gimple_in_ssa_p (cfun))
+ res = make_ssa_name (type);
+ else
+ res = create_tmp_reg (type);
+ gimple stmt = gimple_build_assign (res, code, op0, op1);
+ gimple_set_location (stmt, loc);
+ gimple_seq_add_stmt_without_update (seq, stmt);
+ }
+ return res;
+}
+
+/* Build the expression (CODE OP0 OP1 OP2) of type TYPE with location LOC,
+ simplifying it first if possible. Returns the built
+ expression value and appends statements possibly defining it
+ to SEQ. */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+ enum tree_code code, tree type, tree op0, tree op1, tree op2)
+{
+ tree res = gimple_simplify (code, type, op0, op1, op2,
+ seq, gimple_build_valueize);
+ if (!res)
+ {
+ if (gimple_in_ssa_p (cfun))
+ res = make_ssa_name (type);
+ else
+ res = create_tmp_reg (type);
+ gimple stmt;
+ if (code == BIT_FIELD_REF)
+ stmt = gimple_build_assign (res, code,
+ build3 (code, type, op0, op1, op2));
+ else
+ stmt = gimple_build_assign (res, code, op0, op1, op2);
+ gimple_set_location (stmt, loc);
+ gimple_seq_add_stmt_without_update (seq, stmt);
+ }
+ return res;
+}
+
+/* Build the call FN (ARG0) with a result of type TYPE
+ (or no result if TYPE is void) with location LOC,
+ simplifying it first if possible. Returns the built
+ expression value (or NULL_TREE if TYPE is void) and appends
+ statements possibly defining it to SEQ. */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+ enum built_in_function fn, tree type, tree arg0)
+{
+ tree res = gimple_simplify (fn, type, arg0, seq, gimple_build_valueize);
+ if (!res)
+ {
+ tree decl = builtin_decl_implicit (fn);
+ gimple stmt = gimple_build_call (decl, 1, arg0);
+ if (!VOID_TYPE_P (type))
+ {
+ if (gimple_in_ssa_p (cfun))
+ res = make_ssa_name (type);
+ else
+ res = create_tmp_reg (type);
+ gimple_call_set_lhs (stmt, res);
+ }
+ gimple_set_location (stmt, loc);
+ gimple_seq_add_stmt_without_update (seq, stmt);
+ }
+ return res;
+}
+
+/* Build the call FN (ARG0, ARG1) with a result of type TYPE
+ (or no result if TYPE is void) with location LOC,
+ simplifying it first if possible. Returns the built
+ expression value (or NULL_TREE if TYPE is void) and appends
+ statements possibly defining it to SEQ. */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+ enum built_in_function fn, tree type, tree arg0, tree arg1)
+{
+ tree res = gimple_simplify (fn, type, arg0, arg1, seq, gimple_build_valueize);
+ if (!res)
+ {
+ tree decl = builtin_decl_implicit (fn);
+ gimple stmt = gimple_build_call (decl, 2, arg0, arg1);
+ if (!VOID_TYPE_P (type))
+ {
+ if (gimple_in_ssa_p (cfun))
+ res = make_ssa_name (type);
+ else
+ res = create_tmp_reg (type);
+ gimple_call_set_lhs (stmt, res);
+ }
+ gimple_set_location (stmt, loc);
+ gimple_seq_add_stmt_without_update (seq, stmt);
+ }
+ return res;
+}
+
+/* Build the call FN (ARG0, ARG1, ARG2) with a result of type TYPE
+ (or no result if TYPE is void) with location LOC,
+ simplifying it first if possible. Returns the built
+ expression value (or NULL_TREE if TYPE is void) and appends
+ statements possibly defining it to SEQ. */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+ enum built_in_function fn, tree type,
+ tree arg0, tree arg1, tree arg2)
+{
+ tree res = gimple_simplify (fn, type, arg0, arg1, arg2,
+ seq, gimple_build_valueize);
+ if (!res)
+ {
+ tree decl = builtin_decl_implicit (fn);
+ gimple stmt = gimple_build_call (decl, 3, arg0, arg1, arg2);
+ if (!VOID_TYPE_P (type))
+ {
+ if (gimple_in_ssa_p (cfun))
+ res = make_ssa_name (type);
+ else
+ res = create_tmp_reg (type);
+ gimple_call_set_lhs (stmt, res);
+ }
+ gimple_set_location (stmt, loc);
+ gimple_seq_add_stmt_without_update (seq, stmt);
+ }
+ return res;
+}
+
+/* Build the conversion (TYPE) OP with a result of type TYPE
+ with location LOC if such conversion is neccesary in GIMPLE,
+ simplifying it first.
+ Returns the built expression value and appends
+ statements possibly defining it to SEQ. */
+
+tree
+gimple_convert (gimple_seq *seq, location_t loc, tree type, tree op)
+{
+ if (useless_type_conversion_p (type, TREE_TYPE (op)))
+ return op;
+ return gimple_build (seq, loc, NOP_EXPR, type, op);
+}