/* 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 "stmt.h"
-#include "stor-layout.h"
-#include "flags.h"
-#include "hashtab.h"
-#include "hash-set.h"
-#include "vec.h"
-#include "machmode.h"
#include "hard-reg-set.h"
-#include "input.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 "dumpfile.h"
#include "bitmap.h"
#include "predict.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 "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
#include "cgraph.h"
#include "ipa-utils.h"
#include "gimple-pretty-print.h"
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));
}
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);
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;
}
+/* 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. */
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
gimple_fold_builtin_snprintf_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;
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);
static bool
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);
return false;
tree orig_len = get_maxval_strlen (orig, 0);
- if (!orig_len)
+ if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
return false;
/* We could expand this as
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.
+
+ 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_fprintf (gimple_stmt_iterator *gsi,
+ tree fp, tree fmt, tree arg,
+ enum built_in_function fcode)
+{
+ 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)
+ {
+ /* 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);
+ }
+ else
+ {
+ fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
+ fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
+ }
+
+ if (!init_target_chars ())
+ return false;
+
+ /* 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;
+
+ /* If the format specifier was "", fprintf does nothing. */
+ if (fmt_str[0] == '\0')
+ {
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+ }
+
+ /* 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;
+ }
+ }
+
+ /* 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;
+
+ /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */
+ else if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ 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;
+ }
+ }
+
+ /* 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;
+ }
+ }
+
+ 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.
+
+ 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)
+ {
+ /* 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);
+ }
+
+ if (!init_target_chars ())
+ return false;
+
+ if (strcmp (fmt_str, target_percent_s) == 0
+ || strchr (fmt_str, target_percent) == NULL)
+ {
+ const char *str;
+
+ if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return false;
+
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
+
+ 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;
+ }
+
+ /* If the string was "", printf does nothing. */
+ if (str[0] == '\0')
+ {
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+ }
+
+ /* 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;
+ }
+ }
+
+ /* 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;
+
+ /* 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;
+ }
+ }
+
+ /* 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 (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;
- 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:
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);
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2),
gimple_call_arg (stmt, 3),
- DECL_FUNCTION_CODE (callee));
+ 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),
- DECL_FUNCTION_CODE (callee));
+ fcode);
case BUILT_IN_STRNCPY_CHK:
case BUILT_IN_STPNCPY_CHK:
return gimple_fold_builtin_stxncpy_chk (gsi,
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2),
gimple_call_arg (stmt, 3),
- DECL_FUNCTION_CODE (callee));
+ fcode);
case BUILT_IN_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK:
- return gimple_fold_builtin_snprintf_chk (gsi,
- DECL_FUNCTION_CODE (callee));
+ 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:;
}
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 (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
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])))
return false;
- if (gimple_code (stmt) == GIMPLE_COND)
+ if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
{
gcc_assert (rcode.is_tree_code ());
if (TREE_CODE_CLASS ((enum tree_code)rcode) == tcc_comparison
|| !operation_could_trap_p (rcode,
FLOAT_TYPE_P (TREE_TYPE (ops[0])),
false, NULL_TREE)))
- gimple_cond_set_condition (stmt, rcode, ops[0], ops[1]);
+ gimple_cond_set_condition (cond_stmt, rcode, ops[0], ops[1]);
else if (rcode == SSA_NAME)
- gimple_cond_set_condition (stmt, NE_EXPR, ops[0],
+ 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 (stmt);
+ gimple_cond_make_false (cond_stmt);
else
- gimple_cond_make_true (stmt);
+ gimple_cond_make_true (cond_stmt);
}
else if (!inplace)
{
ops, seq);
if (!res)
return false;
- gimple_cond_set_condition (stmt, NE_EXPR, res,
+ gimple_cond_set_condition (cond_stmt, NE_EXPR, res,
build_zero_cst (TREE_TYPE (res)));
}
else
if (gimple_has_lhs (stmt))
{
tree lhs = gimple_get_lhs (stmt);
- maybe_push_res_to_seq (rcode, TREE_TYPE (lhs),
- ops, seq, lhs);
+ 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 ");
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));
}
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)
gimple_seq seq = NULL;
code_helper rcode;
tree ops[3] = {};
- if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq, valueize))
+ if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq,
+ valueize, valueize))
{
if (replace_stmt_with_simplification (gsi, rcode, ops, &seq, inplace))
changed = true;
}
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)));
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)))
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)
+ 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)
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));
+ 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 using VALUEIZE if not NULL.
- OP0 is expected to be valueized already. Returns the built
+ 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 (*valueize)(tree))
+ enum tree_code code, tree type, tree op0)
{
- tree res = gimple_simplify (code, type, op0, seq, valueize);
+ tree res = gimple_simplify (code, type, op0, seq, gimple_build_valueize);
if (!res)
{
if (gimple_in_ssa_p (cfun))
- res = make_ssa_name (type, NULL);
+ res = make_ssa_name (type);
else
- res = create_tmp_reg (type, NULL);
+ res = create_tmp_reg (type);
gimple stmt;
if (code == REALPART_EXPR
|| code == IMAGPART_EXPR
|| code == VIEW_CONVERT_EXPR)
- stmt = gimple_build_assign_with_ops (code, res,
- build1 (code, type, op0));
+ stmt = gimple_build_assign (res, code, build1 (code, type, op0));
else
- stmt = gimple_build_assign_with_ops (code, res, op0);
+ stmt = gimple_build_assign (res, code, op0);
gimple_set_location (stmt, loc);
gimple_seq_add_stmt_without_update (seq, stmt);
}
}
/* Build the expression OP0 CODE OP1 of type TYPE with location LOC,
- simplifying it first if possible using VALUEIZE if not NULL.
- OP0 and OP1 are expected to be valueized already. Returns the built
+ 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 (*valueize)(tree))
+ enum tree_code code, tree type, tree op0, tree op1)
{
- tree res = gimple_simplify (code, type, op0, op1, seq, valueize);
+ 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, NULL);
+ res = make_ssa_name (type);
else
- res = create_tmp_reg (type, NULL);
- gimple stmt = gimple_build_assign_with_ops (code, res, op0, op1);
+ 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);
}
}
/* Build the expression (CODE OP0 OP1 OP2) of type TYPE with location LOC,
- simplifying it first if possible using VALUEIZE if not NULL.
- OP0, OP1 and OP2 are expected to be valueized already. Returns the built
+ 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 (*valueize)(tree))
+ enum tree_code code, tree type, tree op0, tree op1, tree op2)
{
tree res = gimple_simplify (code, type, op0, op1, op2,
- seq, valueize);
+ seq, gimple_build_valueize);
if (!res)
{
if (gimple_in_ssa_p (cfun))
- res = make_ssa_name (type, NULL);
+ res = make_ssa_name (type);
else
- res = create_tmp_reg (type, NULL);
+ res = create_tmp_reg (type);
gimple stmt;
if (code == BIT_FIELD_REF)
- stmt = gimple_build_assign_with_ops (code, res,
- build3 (BIT_FIELD_REF, type,
- op0, op1, op2));
+ stmt = gimple_build_assign (res, code,
+ build3 (code, type, op0, op1, op2));
else
- stmt = gimple_build_assign_with_ops (code, res, op0, op1, op2);
+ stmt = gimple_build_assign (res, code, op0, op1, op2);
gimple_set_location (stmt, loc);
gimple_seq_add_stmt_without_update (seq, stmt);
}
/* 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 using VALUEIZE if not NULL.
- ARG0 is expected to be valueized already. Returns the built
+ 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 (*valueize)(tree))
+ enum built_in_function fn, tree type, tree arg0)
{
- tree res = gimple_simplify (fn, type, arg0, seq, valueize);
+ tree res = gimple_simplify (fn, type, arg0, seq, gimple_build_valueize);
if (!res)
{
tree decl = builtin_decl_implicit (fn);
if (!VOID_TYPE_P (type))
{
if (gimple_in_ssa_p (cfun))
- res = make_ssa_name (type, NULL);
+ res = make_ssa_name (type);
else
- res = create_tmp_reg (type, NULL);
+ res = create_tmp_reg (type);
gimple_call_set_lhs (stmt, res);
}
gimple_set_location (stmt, loc);
/* 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 using VALUEIZE if not NULL.
- ARG0 is expected to be valueized already. Returns the built
+ 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 (*valueize)(tree))
+ enum built_in_function fn, tree type, tree arg0, tree arg1)
{
- tree res = gimple_simplify (fn, type, arg0, arg1, seq, valueize);
+ tree res = gimple_simplify (fn, type, arg0, arg1, seq, gimple_build_valueize);
if (!res)
{
tree decl = builtin_decl_implicit (fn);
if (!VOID_TYPE_P (type))
{
if (gimple_in_ssa_p (cfun))
- res = make_ssa_name (type, NULL);
+ res = make_ssa_name (type);
else
- res = create_tmp_reg (type, NULL);
+ res = create_tmp_reg (type);
gimple_call_set_lhs (stmt, res);
}
gimple_set_location (stmt, loc);
/* 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 using VALUEIZE if not NULL.
- ARG0 is expected to be valueized already. Returns the built
+ 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 (*valueize)(tree))
+ tree arg0, tree arg1, tree arg2)
{
- tree res = gimple_simplify (fn, type, arg0, arg1, arg2, seq, valueize);
+ tree res = gimple_simplify (fn, type, arg0, arg1, arg2,
+ seq, gimple_build_valueize);
if (!res)
{
tree decl = builtin_decl_implicit (fn);
if (!VOID_TYPE_P (type))
{
if (gimple_in_ssa_p (cfun))
- res = make_ssa_name (type, NULL);
+ res = make_ssa_name (type);
else
- res = create_tmp_reg (type, NULL);
+ res = create_tmp_reg (type);
gimple_call_set_lhs (stmt, res);
}
gimple_set_location (stmt, loc);