61362d9d18
2019-09-30 Richard Biener <rguenther@suse.de> * gimple.c (gimple_get_lhs): For PHIs return the result. * tree-vectorizer.h (vectorizable_live_operation): Also get the SLP instance as argument. * tree-vect-loop.c (vect_analyze_loop_operations): Also handle double-reduction PHIs with vectorizable_lc_phi. (vect_analyze_loop_operations): Adjust. (vect_create_epilog_for_reduction): Remove all code not dealing with reduction LC PHI or epilogue generation. (vectorizable_live_operation): Call vect_create_epilog_for_reduction for live stmts of reductions. * tree-vect-stmts.c (vectorizable_condition): When !for_reduction do not handle defs that are not vect_internal_def. (can_vectorize_live_stmts): Adjust. (vect_analyze_stmt): When the vectorized stmt defined a value used on backedges adjust the backedge uses of vectorized PHIs. From-SVN: r276299
3410 lines
94 KiB
C
3410 lines
94 KiB
C
/* Gimple IR support functions.
|
||
|
||
Copyright (C) 2007-2019 Free Software Foundation, Inc.
|
||
Contributed by Aldy Hernandez <aldyh@redhat.com>
|
||
|
||
This file is part of GCC.
|
||
|
||
GCC is free software; you can redistribute it and/or modify it under
|
||
the terms of the GNU General Public License as published by the Free
|
||
Software Foundation; either version 3, or (at your option) any later
|
||
version.
|
||
|
||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GCC; see the file COPYING3. If not see
|
||
<http://www.gnu.org/licenses/>. */
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "coretypes.h"
|
||
#include "backend.h"
|
||
#include "tree.h"
|
||
#include "gimple.h"
|
||
#include "ssa.h"
|
||
#include "cgraph.h"
|
||
#include "diagnostic.h"
|
||
#include "alias.h"
|
||
#include "fold-const.h"
|
||
#include "calls.h"
|
||
#include "stor-layout.h"
|
||
#include "internal-fn.h"
|
||
#include "tree-eh.h"
|
||
#include "gimple-iterator.h"
|
||
#include "gimple-walk.h"
|
||
#include "gimplify.h"
|
||
#include "target.h"
|
||
#include "builtins.h"
|
||
#include "selftest.h"
|
||
#include "gimple-pretty-print.h"
|
||
#include "stringpool.h"
|
||
#include "attribs.h"
|
||
#include "asan.h"
|
||
#include "langhooks.h"
|
||
|
||
|
||
/* All the tuples have their operand vector (if present) at the very bottom
|
||
of the structure. Therefore, the offset required to find the
|
||
operands vector the size of the structure minus the size of the 1
|
||
element tree array at the end (see gimple_ops). */
|
||
#define DEFGSSTRUCT(SYM, STRUCT, HAS_TREE_OP) \
|
||
(HAS_TREE_OP ? sizeof (struct STRUCT) - sizeof (tree) : 0),
|
||
EXPORTED_CONST size_t gimple_ops_offset_[] = {
|
||
#include "gsstruct.def"
|
||
};
|
||
#undef DEFGSSTRUCT
|
||
|
||
#define DEFGSSTRUCT(SYM, STRUCT, HAS_TREE_OP) sizeof (struct STRUCT),
|
||
static const size_t gsstruct_code_size[] = {
|
||
#include "gsstruct.def"
|
||
};
|
||
#undef DEFGSSTRUCT
|
||
|
||
#define DEFGSCODE(SYM, NAME, GSSCODE) NAME,
|
||
const char *const gimple_code_name[] = {
|
||
#include "gimple.def"
|
||
};
|
||
#undef DEFGSCODE
|
||
|
||
#define DEFGSCODE(SYM, NAME, GSSCODE) GSSCODE,
|
||
EXPORTED_CONST enum gimple_statement_structure_enum gss_for_code_[] = {
|
||
#include "gimple.def"
|
||
};
|
||
#undef DEFGSCODE
|
||
|
||
/* Gimple stats. */
|
||
|
||
uint64_t gimple_alloc_counts[(int) gimple_alloc_kind_all];
|
||
uint64_t gimple_alloc_sizes[(int) gimple_alloc_kind_all];
|
||
|
||
/* Keep in sync with gimple.h:enum gimple_alloc_kind. */
|
||
static const char * const gimple_alloc_kind_names[] = {
|
||
"assignments",
|
||
"phi nodes",
|
||
"conditionals",
|
||
"everything else"
|
||
};
|
||
|
||
/* Static gimple tuple members. */
|
||
const enum gimple_code gassign::code_;
|
||
const enum gimple_code gcall::code_;
|
||
const enum gimple_code gcond::code_;
|
||
|
||
|
||
/* Gimple tuple constructors.
|
||
Note: Any constructor taking a ``gimple_seq'' as a parameter, can
|
||
be passed a NULL to start with an empty sequence. */
|
||
|
||
/* Set the code for statement G to CODE. */
|
||
|
||
static inline void
|
||
gimple_set_code (gimple *g, enum gimple_code code)
|
||
{
|
||
g->code = code;
|
||
}
|
||
|
||
/* Return the number of bytes needed to hold a GIMPLE statement with
|
||
code CODE. */
|
||
|
||
size_t
|
||
gimple_size (enum gimple_code code, unsigned num_ops)
|
||
{
|
||
size_t size = gsstruct_code_size[gss_for_code (code)];
|
||
if (num_ops > 0)
|
||
size += (sizeof (tree) * (num_ops - 1));
|
||
return size;
|
||
}
|
||
|
||
/* Initialize GIMPLE statement G with CODE and NUM_OPS. */
|
||
|
||
void
|
||
gimple_init (gimple *g, enum gimple_code code, unsigned num_ops)
|
||
{
|
||
gimple_set_code (g, code);
|
||
gimple_set_num_ops (g, num_ops);
|
||
|
||
/* Do not call gimple_set_modified here as it has other side
|
||
effects and this tuple is still not completely built. */
|
||
g->modified = 1;
|
||
gimple_init_singleton (g);
|
||
}
|
||
|
||
/* Allocate memory for a GIMPLE statement with code CODE and NUM_OPS
|
||
operands. */
|
||
|
||
gimple *
|
||
gimple_alloc (enum gimple_code code, unsigned num_ops MEM_STAT_DECL)
|
||
{
|
||
size_t size;
|
||
gimple *stmt;
|
||
|
||
size = gimple_size (code, num_ops);
|
||
if (GATHER_STATISTICS)
|
||
{
|
||
enum gimple_alloc_kind kind = gimple_alloc_kind (code);
|
||
gimple_alloc_counts[(int) kind]++;
|
||
gimple_alloc_sizes[(int) kind] += size;
|
||
}
|
||
|
||
stmt = ggc_alloc_cleared_gimple_statement_stat (size PASS_MEM_STAT);
|
||
gimple_init (stmt, code, num_ops);
|
||
return stmt;
|
||
}
|
||
|
||
/* Set SUBCODE to be the code of the expression computed by statement G. */
|
||
|
||
static inline void
|
||
gimple_set_subcode (gimple *g, unsigned subcode)
|
||
{
|
||
/* We only have 16 bits for the RHS code. Assert that we are not
|
||
overflowing it. */
|
||
gcc_assert (subcode < (1 << 16));
|
||
g->subcode = subcode;
|
||
}
|
||
|
||
|
||
|
||
/* Build a tuple with operands. CODE is the statement to build (which
|
||
must be one of the GIMPLE_WITH_OPS tuples). SUBCODE is the subcode
|
||
for the new tuple. NUM_OPS is the number of operands to allocate. */
|
||
|
||
#define gimple_build_with_ops(c, s, n) \
|
||
gimple_build_with_ops_stat (c, s, n MEM_STAT_INFO)
|
||
|
||
static gimple *
|
||
gimple_build_with_ops_stat (enum gimple_code code, unsigned subcode,
|
||
unsigned num_ops MEM_STAT_DECL)
|
||
{
|
||
gimple *s = gimple_alloc (code, num_ops PASS_MEM_STAT);
|
||
gimple_set_subcode (s, subcode);
|
||
|
||
return s;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_RETURN statement returning RETVAL. */
|
||
|
||
greturn *
|
||
gimple_build_return (tree retval)
|
||
{
|
||
greturn *s
|
||
= as_a <greturn *> (gimple_build_with_ops (GIMPLE_RETURN, ERROR_MARK,
|
||
2));
|
||
if (retval)
|
||
gimple_return_set_retval (s, retval);
|
||
return s;
|
||
}
|
||
|
||
/* Reset alias information on call S. */
|
||
|
||
void
|
||
gimple_call_reset_alias_info (gcall *s)
|
||
{
|
||
if (gimple_call_flags (s) & ECF_CONST)
|
||
memset (gimple_call_use_set (s), 0, sizeof (struct pt_solution));
|
||
else
|
||
pt_solution_reset (gimple_call_use_set (s));
|
||
if (gimple_call_flags (s) & (ECF_CONST|ECF_PURE|ECF_NOVOPS))
|
||
memset (gimple_call_clobber_set (s), 0, sizeof (struct pt_solution));
|
||
else
|
||
pt_solution_reset (gimple_call_clobber_set (s));
|
||
}
|
||
|
||
/* Helper for gimple_build_call, gimple_build_call_valist,
|
||
gimple_build_call_vec and gimple_build_call_from_tree. Build the basic
|
||
components of a GIMPLE_CALL statement to function FN with NARGS
|
||
arguments. */
|
||
|
||
static inline gcall *
|
||
gimple_build_call_1 (tree fn, unsigned nargs)
|
||
{
|
||
gcall *s
|
||
= as_a <gcall *> (gimple_build_with_ops (GIMPLE_CALL, ERROR_MARK,
|
||
nargs + 3));
|
||
if (TREE_CODE (fn) == FUNCTION_DECL)
|
||
fn = build_fold_addr_expr (fn);
|
||
gimple_set_op (s, 1, fn);
|
||
gimple_call_set_fntype (s, TREE_TYPE (TREE_TYPE (fn)));
|
||
gimple_call_reset_alias_info (s);
|
||
return s;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_CALL statement to function FN with the arguments
|
||
specified in vector ARGS. */
|
||
|
||
gcall *
|
||
gimple_build_call_vec (tree fn, vec<tree> args)
|
||
{
|
||
unsigned i;
|
||
unsigned nargs = args.length ();
|
||
gcall *call = gimple_build_call_1 (fn, nargs);
|
||
|
||
for (i = 0; i < nargs; i++)
|
||
gimple_call_set_arg (call, i, args[i]);
|
||
|
||
return call;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_CALL statement to function FN. NARGS is the number of
|
||
arguments. The ... are the arguments. */
|
||
|
||
gcall *
|
||
gimple_build_call (tree fn, unsigned nargs, ...)
|
||
{
|
||
va_list ap;
|
||
gcall *call;
|
||
unsigned i;
|
||
|
||
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL || is_gimple_call_addr (fn));
|
||
|
||
call = gimple_build_call_1 (fn, nargs);
|
||
|
||
va_start (ap, nargs);
|
||
for (i = 0; i < nargs; i++)
|
||
gimple_call_set_arg (call, i, va_arg (ap, tree));
|
||
va_end (ap);
|
||
|
||
return call;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_CALL statement to function FN. NARGS is the number of
|
||
arguments. AP contains the arguments. */
|
||
|
||
gcall *
|
||
gimple_build_call_valist (tree fn, unsigned nargs, va_list ap)
|
||
{
|
||
gcall *call;
|
||
unsigned i;
|
||
|
||
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL || is_gimple_call_addr (fn));
|
||
|
||
call = gimple_build_call_1 (fn, nargs);
|
||
|
||
for (i = 0; i < nargs; i++)
|
||
gimple_call_set_arg (call, i, va_arg (ap, tree));
|
||
|
||
return call;
|
||
}
|
||
|
||
|
||
/* Helper for gimple_build_call_internal and gimple_build_call_internal_vec.
|
||
Build the basic components of a GIMPLE_CALL statement to internal
|
||
function FN with NARGS arguments. */
|
||
|
||
static inline gcall *
|
||
gimple_build_call_internal_1 (enum internal_fn fn, unsigned nargs)
|
||
{
|
||
gcall *s
|
||
= as_a <gcall *> (gimple_build_with_ops (GIMPLE_CALL, ERROR_MARK,
|
||
nargs + 3));
|
||
s->subcode |= GF_CALL_INTERNAL;
|
||
gimple_call_set_internal_fn (s, fn);
|
||
gimple_call_reset_alias_info (s);
|
||
return s;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_CALL statement to internal function FN. NARGS is
|
||
the number of arguments. The ... are the arguments. */
|
||
|
||
gcall *
|
||
gimple_build_call_internal (enum internal_fn fn, unsigned nargs, ...)
|
||
{
|
||
va_list ap;
|
||
gcall *call;
|
||
unsigned i;
|
||
|
||
call = gimple_build_call_internal_1 (fn, nargs);
|
||
va_start (ap, nargs);
|
||
for (i = 0; i < nargs; i++)
|
||
gimple_call_set_arg (call, i, va_arg (ap, tree));
|
||
va_end (ap);
|
||
|
||
return call;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_CALL statement to internal function FN with the arguments
|
||
specified in vector ARGS. */
|
||
|
||
gcall *
|
||
gimple_build_call_internal_vec (enum internal_fn fn, vec<tree> args)
|
||
{
|
||
unsigned i, nargs;
|
||
gcall *call;
|
||
|
||
nargs = args.length ();
|
||
call = gimple_build_call_internal_1 (fn, nargs);
|
||
for (i = 0; i < nargs; i++)
|
||
gimple_call_set_arg (call, i, args[i]);
|
||
|
||
return call;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_CALL statement from CALL_EXPR T. Note that T is
|
||
assumed to be in GIMPLE form already. Minimal checking is done of
|
||
this fact. */
|
||
|
||
gcall *
|
||
gimple_build_call_from_tree (tree t, tree fnptrtype)
|
||
{
|
||
unsigned i, nargs;
|
||
gcall *call;
|
||
|
||
gcc_assert (TREE_CODE (t) == CALL_EXPR);
|
||
|
||
nargs = call_expr_nargs (t);
|
||
|
||
tree fndecl = NULL_TREE;
|
||
if (CALL_EXPR_FN (t) == NULL_TREE)
|
||
call = gimple_build_call_internal_1 (CALL_EXPR_IFN (t), nargs);
|
||
else
|
||
{
|
||
fndecl = get_callee_fndecl (t);
|
||
call = gimple_build_call_1 (fndecl ? fndecl : CALL_EXPR_FN (t), nargs);
|
||
}
|
||
|
||
for (i = 0; i < nargs; i++)
|
||
gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
|
||
|
||
gimple_set_block (call, TREE_BLOCK (t));
|
||
gimple_set_location (call, EXPR_LOCATION (t));
|
||
|
||
/* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */
|
||
gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
|
||
gimple_call_set_tail (call, CALL_EXPR_TAILCALL (t));
|
||
gimple_call_set_must_tail (call, CALL_EXPR_MUST_TAIL_CALL (t));
|
||
gimple_call_set_return_slot_opt (call, CALL_EXPR_RETURN_SLOT_OPT (t));
|
||
if (fndecl
|
||
&& fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
|
||
&& ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl)))
|
||
gimple_call_set_alloca_for_var (call, CALL_ALLOCA_FOR_VAR_P (t));
|
||
else
|
||
gimple_call_set_from_thunk (call, CALL_FROM_THUNK_P (t));
|
||
gimple_call_set_va_arg_pack (call, CALL_EXPR_VA_ARG_PACK (t));
|
||
gimple_call_set_nothrow (call, TREE_NOTHROW (t));
|
||
gimple_call_set_by_descriptor (call, CALL_EXPR_BY_DESCRIPTOR (t));
|
||
gimple_set_no_warning (call, TREE_NO_WARNING (t));
|
||
|
||
if (fnptrtype)
|
||
{
|
||
gimple_call_set_fntype (call, TREE_TYPE (fnptrtype));
|
||
|
||
/* Check if it's an indirect CALL and the type has the
|
||
nocf_check attribute. In that case propagate the information
|
||
to the gimple CALL insn. */
|
||
if (!fndecl)
|
||
{
|
||
gcc_assert (POINTER_TYPE_P (fnptrtype));
|
||
tree fntype = TREE_TYPE (fnptrtype);
|
||
|
||
if (lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (fntype)))
|
||
gimple_call_set_nocf_check (call, TRUE);
|
||
}
|
||
}
|
||
|
||
return call;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_ASSIGN statement.
|
||
|
||
LHS of the assignment.
|
||
RHS of the assignment which can be unary or binary. */
|
||
|
||
gassign *
|
||
gimple_build_assign (tree lhs, tree rhs MEM_STAT_DECL)
|
||
{
|
||
enum tree_code subcode;
|
||
tree op1, op2, op3;
|
||
|
||
extract_ops_from_tree (rhs, &subcode, &op1, &op2, &op3);
|
||
return gimple_build_assign (lhs, subcode, op1, op2, op3 PASS_MEM_STAT);
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_ASSIGN statement with subcode SUBCODE and operands
|
||
OP1, OP2 and OP3. */
|
||
|
||
static inline gassign *
|
||
gimple_build_assign_1 (tree lhs, enum tree_code subcode, tree op1,
|
||
tree op2, tree op3 MEM_STAT_DECL)
|
||
{
|
||
unsigned num_ops;
|
||
gassign *p;
|
||
|
||
/* Need 1 operand for LHS and 1 or 2 for the RHS (depending on the
|
||
code). */
|
||
num_ops = get_gimple_rhs_num_ops (subcode) + 1;
|
||
|
||
p = as_a <gassign *> (
|
||
gimple_build_with_ops_stat (GIMPLE_ASSIGN, (unsigned)subcode, num_ops
|
||
PASS_MEM_STAT));
|
||
gimple_assign_set_lhs (p, lhs);
|
||
gimple_assign_set_rhs1 (p, op1);
|
||
if (op2)
|
||
{
|
||
gcc_assert (num_ops > 2);
|
||
gimple_assign_set_rhs2 (p, op2);
|
||
}
|
||
|
||
if (op3)
|
||
{
|
||
gcc_assert (num_ops > 3);
|
||
gimple_assign_set_rhs3 (p, op3);
|
||
}
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_ASSIGN statement with subcode SUBCODE and operands
|
||
OP1, OP2 and OP3. */
|
||
|
||
gassign *
|
||
gimple_build_assign (tree lhs, enum tree_code subcode, tree op1,
|
||
tree op2, tree op3 MEM_STAT_DECL)
|
||
{
|
||
return gimple_build_assign_1 (lhs, subcode, op1, op2, op3 PASS_MEM_STAT);
|
||
}
|
||
|
||
/* Build a GIMPLE_ASSIGN statement with subcode SUBCODE and operands
|
||
OP1 and OP2. */
|
||
|
||
gassign *
|
||
gimple_build_assign (tree lhs, enum tree_code subcode, tree op1,
|
||
tree op2 MEM_STAT_DECL)
|
||
{
|
||
return gimple_build_assign_1 (lhs, subcode, op1, op2, NULL_TREE
|
||
PASS_MEM_STAT);
|
||
}
|
||
|
||
/* Build a GIMPLE_ASSIGN statement with subcode SUBCODE and operand OP1. */
|
||
|
||
gassign *
|
||
gimple_build_assign (tree lhs, enum tree_code subcode, tree op1 MEM_STAT_DECL)
|
||
{
|
||
return gimple_build_assign_1 (lhs, subcode, op1, NULL_TREE, NULL_TREE
|
||
PASS_MEM_STAT);
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_COND statement.
|
||
|
||
PRED is the condition used to compare LHS and the RHS.
|
||
T_LABEL is the label to jump to if the condition is true.
|
||
F_LABEL is the label to jump to otherwise. */
|
||
|
||
gcond *
|
||
gimple_build_cond (enum tree_code pred_code, tree lhs, tree rhs,
|
||
tree t_label, tree f_label)
|
||
{
|
||
gcond *p;
|
||
|
||
gcc_assert (TREE_CODE_CLASS (pred_code) == tcc_comparison);
|
||
p = as_a <gcond *> (gimple_build_with_ops (GIMPLE_COND, pred_code, 4));
|
||
gimple_cond_set_lhs (p, lhs);
|
||
gimple_cond_set_rhs (p, rhs);
|
||
gimple_cond_set_true_label (p, t_label);
|
||
gimple_cond_set_false_label (p, f_label);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_COND statement from the conditional expression tree
|
||
COND. T_LABEL and F_LABEL are as in gimple_build_cond. */
|
||
|
||
gcond *
|
||
gimple_build_cond_from_tree (tree cond, tree t_label, tree f_label)
|
||
{
|
||
enum tree_code code;
|
||
tree lhs, rhs;
|
||
|
||
gimple_cond_get_ops_from_tree (cond, &code, &lhs, &rhs);
|
||
return gimple_build_cond (code, lhs, rhs, t_label, f_label);
|
||
}
|
||
|
||
/* Set code, lhs, and rhs of a GIMPLE_COND from a suitable
|
||
boolean expression tree COND. */
|
||
|
||
void
|
||
gimple_cond_set_condition_from_tree (gcond *stmt, tree cond)
|
||
{
|
||
enum tree_code code;
|
||
tree lhs, rhs;
|
||
|
||
gimple_cond_get_ops_from_tree (cond, &code, &lhs, &rhs);
|
||
gimple_cond_set_condition (stmt, code, lhs, rhs);
|
||
}
|
||
|
||
/* Build a GIMPLE_LABEL statement for LABEL. */
|
||
|
||
glabel *
|
||
gimple_build_label (tree label)
|
||
{
|
||
glabel *p
|
||
= as_a <glabel *> (gimple_build_with_ops (GIMPLE_LABEL, ERROR_MARK, 1));
|
||
gimple_label_set_label (p, label);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_GOTO statement to label DEST. */
|
||
|
||
ggoto *
|
||
gimple_build_goto (tree dest)
|
||
{
|
||
ggoto *p
|
||
= as_a <ggoto *> (gimple_build_with_ops (GIMPLE_GOTO, ERROR_MARK, 1));
|
||
gimple_goto_set_dest (p, dest);
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_NOP statement. */
|
||
|
||
gimple *
|
||
gimple_build_nop (void)
|
||
{
|
||
return gimple_alloc (GIMPLE_NOP, 0);
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_BIND statement.
|
||
VARS are the variables in BODY.
|
||
BLOCK is the containing block. */
|
||
|
||
gbind *
|
||
gimple_build_bind (tree vars, gimple_seq body, tree block)
|
||
{
|
||
gbind *p = as_a <gbind *> (gimple_alloc (GIMPLE_BIND, 0));
|
||
gimple_bind_set_vars (p, vars);
|
||
if (body)
|
||
gimple_bind_set_body (p, body);
|
||
if (block)
|
||
gimple_bind_set_block (p, block);
|
||
return p;
|
||
}
|
||
|
||
/* Helper function to set the simple fields of a asm stmt.
|
||
|
||
STRING is a pointer to a string that is the asm blocks assembly code.
|
||
NINPUT is the number of register inputs.
|
||
NOUTPUT is the number of register outputs.
|
||
NCLOBBERS is the number of clobbered registers.
|
||
*/
|
||
|
||
static inline gasm *
|
||
gimple_build_asm_1 (const char *string, unsigned ninputs, unsigned noutputs,
|
||
unsigned nclobbers, unsigned nlabels)
|
||
{
|
||
gasm *p;
|
||
int size = strlen (string);
|
||
|
||
/* ASMs with labels cannot have outputs. This should have been
|
||
enforced by the front end. */
|
||
gcc_assert (nlabels == 0 || noutputs == 0);
|
||
|
||
p = as_a <gasm *> (
|
||
gimple_build_with_ops (GIMPLE_ASM, ERROR_MARK,
|
||
ninputs + noutputs + nclobbers + nlabels));
|
||
|
||
p->ni = ninputs;
|
||
p->no = noutputs;
|
||
p->nc = nclobbers;
|
||
p->nl = nlabels;
|
||
p->string = ggc_alloc_string (string, size);
|
||
|
||
if (GATHER_STATISTICS)
|
||
gimple_alloc_sizes[(int) gimple_alloc_kind (GIMPLE_ASM)] += size;
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_ASM statement.
|
||
|
||
STRING is the assembly code.
|
||
NINPUT is the number of register inputs.
|
||
NOUTPUT is the number of register outputs.
|
||
NCLOBBERS is the number of clobbered registers.
|
||
INPUTS is a vector of the input register parameters.
|
||
OUTPUTS is a vector of the output register parameters.
|
||
CLOBBERS is a vector of the clobbered register parameters.
|
||
LABELS is a vector of destination labels. */
|
||
|
||
gasm *
|
||
gimple_build_asm_vec (const char *string, vec<tree, va_gc> *inputs,
|
||
vec<tree, va_gc> *outputs, vec<tree, va_gc> *clobbers,
|
||
vec<tree, va_gc> *labels)
|
||
{
|
||
gasm *p;
|
||
unsigned i;
|
||
|
||
p = gimple_build_asm_1 (string,
|
||
vec_safe_length (inputs),
|
||
vec_safe_length (outputs),
|
||
vec_safe_length (clobbers),
|
||
vec_safe_length (labels));
|
||
|
||
for (i = 0; i < vec_safe_length (inputs); i++)
|
||
gimple_asm_set_input_op (p, i, (*inputs)[i]);
|
||
|
||
for (i = 0; i < vec_safe_length (outputs); i++)
|
||
gimple_asm_set_output_op (p, i, (*outputs)[i]);
|
||
|
||
for (i = 0; i < vec_safe_length (clobbers); i++)
|
||
gimple_asm_set_clobber_op (p, i, (*clobbers)[i]);
|
||
|
||
for (i = 0; i < vec_safe_length (labels); i++)
|
||
gimple_asm_set_label_op (p, i, (*labels)[i]);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_CATCH statement.
|
||
|
||
TYPES are the catch types.
|
||
HANDLER is the exception handler. */
|
||
|
||
gcatch *
|
||
gimple_build_catch (tree types, gimple_seq handler)
|
||
{
|
||
gcatch *p = as_a <gcatch *> (gimple_alloc (GIMPLE_CATCH, 0));
|
||
gimple_catch_set_types (p, types);
|
||
if (handler)
|
||
gimple_catch_set_handler (p, handler);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_EH_FILTER statement.
|
||
|
||
TYPES are the filter's types.
|
||
FAILURE is the filter's failure action. */
|
||
|
||
geh_filter *
|
||
gimple_build_eh_filter (tree types, gimple_seq failure)
|
||
{
|
||
geh_filter *p = as_a <geh_filter *> (gimple_alloc (GIMPLE_EH_FILTER, 0));
|
||
gimple_eh_filter_set_types (p, types);
|
||
if (failure)
|
||
gimple_eh_filter_set_failure (p, failure);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_EH_MUST_NOT_THROW statement. */
|
||
|
||
geh_mnt *
|
||
gimple_build_eh_must_not_throw (tree decl)
|
||
{
|
||
geh_mnt *p = as_a <geh_mnt *> (gimple_alloc (GIMPLE_EH_MUST_NOT_THROW, 0));
|
||
|
||
gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
|
||
gcc_assert (flags_from_decl_or_type (decl) & ECF_NORETURN);
|
||
gimple_eh_must_not_throw_set_fndecl (p, decl);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_EH_ELSE statement. */
|
||
|
||
geh_else *
|
||
gimple_build_eh_else (gimple_seq n_body, gimple_seq e_body)
|
||
{
|
||
geh_else *p = as_a <geh_else *> (gimple_alloc (GIMPLE_EH_ELSE, 0));
|
||
gimple_eh_else_set_n_body (p, n_body);
|
||
gimple_eh_else_set_e_body (p, e_body);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_TRY statement.
|
||
|
||
EVAL is the expression to evaluate.
|
||
CLEANUP is the cleanup expression.
|
||
KIND is either GIMPLE_TRY_CATCH or GIMPLE_TRY_FINALLY depending on
|
||
whether this is a try/catch or a try/finally respectively. */
|
||
|
||
gtry *
|
||
gimple_build_try (gimple_seq eval, gimple_seq cleanup,
|
||
enum gimple_try_flags kind)
|
||
{
|
||
gtry *p;
|
||
|
||
gcc_assert (kind == GIMPLE_TRY_CATCH || kind == GIMPLE_TRY_FINALLY);
|
||
p = as_a <gtry *> (gimple_alloc (GIMPLE_TRY, 0));
|
||
gimple_set_subcode (p, kind);
|
||
if (eval)
|
||
gimple_try_set_eval (p, eval);
|
||
if (cleanup)
|
||
gimple_try_set_cleanup (p, cleanup);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Construct a GIMPLE_WITH_CLEANUP_EXPR statement.
|
||
|
||
CLEANUP is the cleanup expression. */
|
||
|
||
gimple *
|
||
gimple_build_wce (gimple_seq cleanup)
|
||
{
|
||
gimple *p = gimple_alloc (GIMPLE_WITH_CLEANUP_EXPR, 0);
|
||
if (cleanup)
|
||
gimple_wce_set_cleanup (p, cleanup);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_RESX statement. */
|
||
|
||
gresx *
|
||
gimple_build_resx (int region)
|
||
{
|
||
gresx *p
|
||
= as_a <gresx *> (gimple_build_with_ops (GIMPLE_RESX, ERROR_MARK, 0));
|
||
p->region = region;
|
||
return p;
|
||
}
|
||
|
||
|
||
/* The helper for constructing a gimple switch statement.
|
||
INDEX is the switch's index.
|
||
NLABELS is the number of labels in the switch excluding the default.
|
||
DEFAULT_LABEL is the default label for the switch statement. */
|
||
|
||
gswitch *
|
||
gimple_build_switch_nlabels (unsigned nlabels, tree index, tree default_label)
|
||
{
|
||
/* nlabels + 1 default label + 1 index. */
|
||
gcc_checking_assert (default_label);
|
||
gswitch *p = as_a <gswitch *> (gimple_build_with_ops (GIMPLE_SWITCH,
|
||
ERROR_MARK,
|
||
1 + 1 + nlabels));
|
||
gimple_switch_set_index (p, index);
|
||
gimple_switch_set_default_label (p, default_label);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_SWITCH statement.
|
||
|
||
INDEX is the switch's index.
|
||
DEFAULT_LABEL is the default label
|
||
ARGS is a vector of labels excluding the default. */
|
||
|
||
gswitch *
|
||
gimple_build_switch (tree index, tree default_label, vec<tree> args)
|
||
{
|
||
unsigned i, nlabels = args.length ();
|
||
|
||
gswitch *p = gimple_build_switch_nlabels (nlabels, index, default_label);
|
||
|
||
/* Copy the labels from the vector to the switch statement. */
|
||
for (i = 0; i < nlabels; i++)
|
||
gimple_switch_set_label (p, i + 1, args[i]);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_EH_DISPATCH statement. */
|
||
|
||
geh_dispatch *
|
||
gimple_build_eh_dispatch (int region)
|
||
{
|
||
geh_dispatch *p
|
||
= as_a <geh_dispatch *> (
|
||
gimple_build_with_ops (GIMPLE_EH_DISPATCH, ERROR_MARK, 0));
|
||
p->region = region;
|
||
return p;
|
||
}
|
||
|
||
/* Build a new GIMPLE_DEBUG_BIND statement.
|
||
|
||
VAR is bound to VALUE; block and location are taken from STMT. */
|
||
|
||
gdebug *
|
||
gimple_build_debug_bind (tree var, tree value, gimple *stmt MEM_STAT_DECL)
|
||
{
|
||
gdebug *p
|
||
= as_a <gdebug *> (gimple_build_with_ops_stat (GIMPLE_DEBUG,
|
||
(unsigned)GIMPLE_DEBUG_BIND, 2
|
||
PASS_MEM_STAT));
|
||
gimple_debug_bind_set_var (p, var);
|
||
gimple_debug_bind_set_value (p, value);
|
||
if (stmt)
|
||
gimple_set_location (p, gimple_location (stmt));
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a new GIMPLE_DEBUG_SOURCE_BIND statement.
|
||
|
||
VAR is bound to VALUE; block and location are taken from STMT. */
|
||
|
||
gdebug *
|
||
gimple_build_debug_source_bind (tree var, tree value,
|
||
gimple *stmt MEM_STAT_DECL)
|
||
{
|
||
gdebug *p
|
||
= as_a <gdebug *> (
|
||
gimple_build_with_ops_stat (GIMPLE_DEBUG,
|
||
(unsigned)GIMPLE_DEBUG_SOURCE_BIND, 2
|
||
PASS_MEM_STAT));
|
||
|
||
gimple_debug_source_bind_set_var (p, var);
|
||
gimple_debug_source_bind_set_value (p, value);
|
||
if (stmt)
|
||
gimple_set_location (p, gimple_location (stmt));
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a new GIMPLE_DEBUG_BEGIN_STMT statement in BLOCK at
|
||
LOCATION. */
|
||
|
||
gdebug *
|
||
gimple_build_debug_begin_stmt (tree block, location_t location
|
||
MEM_STAT_DECL)
|
||
{
|
||
gdebug *p
|
||
= as_a <gdebug *> (
|
||
gimple_build_with_ops_stat (GIMPLE_DEBUG,
|
||
(unsigned)GIMPLE_DEBUG_BEGIN_STMT, 0
|
||
PASS_MEM_STAT));
|
||
|
||
gimple_set_location (p, location);
|
||
gimple_set_block (p, block);
|
||
cfun->debug_marker_count++;
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a new GIMPLE_DEBUG_INLINE_ENTRY statement in BLOCK at
|
||
LOCATION. The BLOCK links to the inlined function. */
|
||
|
||
gdebug *
|
||
gimple_build_debug_inline_entry (tree block, location_t location
|
||
MEM_STAT_DECL)
|
||
{
|
||
gdebug *p
|
||
= as_a <gdebug *> (
|
||
gimple_build_with_ops_stat (GIMPLE_DEBUG,
|
||
(unsigned)GIMPLE_DEBUG_INLINE_ENTRY, 0
|
||
PASS_MEM_STAT));
|
||
|
||
gimple_set_location (p, location);
|
||
gimple_set_block (p, block);
|
||
cfun->debug_marker_count++;
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_CRITICAL statement.
|
||
|
||
BODY is the sequence of statements for which only one thread can execute.
|
||
NAME is optional identifier for this critical block.
|
||
CLAUSES are clauses for this critical block. */
|
||
|
||
gomp_critical *
|
||
gimple_build_omp_critical (gimple_seq body, tree name, tree clauses)
|
||
{
|
||
gomp_critical *p
|
||
= as_a <gomp_critical *> (gimple_alloc (GIMPLE_OMP_CRITICAL, 0));
|
||
gimple_omp_critical_set_name (p, name);
|
||
gimple_omp_critical_set_clauses (p, clauses);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_OMP_FOR statement.
|
||
|
||
BODY is sequence of statements inside the for loop.
|
||
KIND is the `for' variant.
|
||
CLAUSES are any of the construct's clauses.
|
||
COLLAPSE is the collapse count.
|
||
PRE_BODY is the sequence of statements that are loop invariant. */
|
||
|
||
gomp_for *
|
||
gimple_build_omp_for (gimple_seq body, int kind, tree clauses, size_t collapse,
|
||
gimple_seq pre_body)
|
||
{
|
||
gomp_for *p = as_a <gomp_for *> (gimple_alloc (GIMPLE_OMP_FOR, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_for_set_clauses (p, clauses);
|
||
gimple_omp_for_set_kind (p, kind);
|
||
p->collapse = collapse;
|
||
p->iter = ggc_cleared_vec_alloc<gimple_omp_for_iter> (collapse);
|
||
|
||
if (pre_body)
|
||
gimple_omp_for_set_pre_body (p, pre_body);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_PARALLEL statement.
|
||
|
||
BODY is sequence of statements which are executed in parallel.
|
||
CLAUSES are the OMP parallel construct's clauses.
|
||
CHILD_FN is the function created for the parallel threads to execute.
|
||
DATA_ARG are the shared data argument(s). */
|
||
|
||
gomp_parallel *
|
||
gimple_build_omp_parallel (gimple_seq body, tree clauses, tree child_fn,
|
||
tree data_arg)
|
||
{
|
||
gomp_parallel *p
|
||
= as_a <gomp_parallel *> (gimple_alloc (GIMPLE_OMP_PARALLEL, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_parallel_set_clauses (p, clauses);
|
||
gimple_omp_parallel_set_child_fn (p, child_fn);
|
||
gimple_omp_parallel_set_data_arg (p, data_arg);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_TASK statement.
|
||
|
||
BODY is sequence of statements which are executed by the explicit task.
|
||
CLAUSES are the OMP task construct's clauses.
|
||
CHILD_FN is the function created for the parallel threads to execute.
|
||
DATA_ARG are the shared data argument(s).
|
||
COPY_FN is the optional function for firstprivate initialization.
|
||
ARG_SIZE and ARG_ALIGN are size and alignment of the data block. */
|
||
|
||
gomp_task *
|
||
gimple_build_omp_task (gimple_seq body, tree clauses, tree child_fn,
|
||
tree data_arg, tree copy_fn, tree arg_size,
|
||
tree arg_align)
|
||
{
|
||
gomp_task *p = as_a <gomp_task *> (gimple_alloc (GIMPLE_OMP_TASK, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_task_set_clauses (p, clauses);
|
||
gimple_omp_task_set_child_fn (p, child_fn);
|
||
gimple_omp_task_set_data_arg (p, data_arg);
|
||
gimple_omp_task_set_copy_fn (p, copy_fn);
|
||
gimple_omp_task_set_arg_size (p, arg_size);
|
||
gimple_omp_task_set_arg_align (p, arg_align);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_SECTION statement for a sections statement.
|
||
|
||
BODY is the sequence of statements in the section. */
|
||
|
||
gimple *
|
||
gimple_build_omp_section (gimple_seq body)
|
||
{
|
||
gimple *p = gimple_alloc (GIMPLE_OMP_SECTION, 0);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_MASTER statement.
|
||
|
||
BODY is the sequence of statements to be executed by just the master. */
|
||
|
||
gimple *
|
||
gimple_build_omp_master (gimple_seq body)
|
||
{
|
||
gimple *p = gimple_alloc (GIMPLE_OMP_MASTER, 0);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_OMP_GRID_BODY statement.
|
||
|
||
BODY is the sequence of statements to be executed by the kernel. */
|
||
|
||
gimple *
|
||
gimple_build_omp_grid_body (gimple_seq body)
|
||
{
|
||
gimple *p = gimple_alloc (GIMPLE_OMP_GRID_BODY, 0);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_OMP_TASKGROUP statement.
|
||
|
||
BODY is the sequence of statements to be executed by the taskgroup
|
||
construct.
|
||
CLAUSES are any of the construct's clauses. */
|
||
|
||
gimple *
|
||
gimple_build_omp_taskgroup (gimple_seq body, tree clauses)
|
||
{
|
||
gimple *p = gimple_alloc (GIMPLE_OMP_TASKGROUP, 0);
|
||
gimple_omp_taskgroup_set_clauses (p, clauses);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_CONTINUE statement.
|
||
|
||
CONTROL_DEF is the definition of the control variable.
|
||
CONTROL_USE is the use of the control variable. */
|
||
|
||
gomp_continue *
|
||
gimple_build_omp_continue (tree control_def, tree control_use)
|
||
{
|
||
gomp_continue *p
|
||
= as_a <gomp_continue *> (gimple_alloc (GIMPLE_OMP_CONTINUE, 0));
|
||
gimple_omp_continue_set_control_def (p, control_def);
|
||
gimple_omp_continue_set_control_use (p, control_use);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_OMP_ORDERED statement.
|
||
|
||
BODY is the sequence of statements inside a loop that will executed in
|
||
sequence.
|
||
CLAUSES are clauses for this statement. */
|
||
|
||
gomp_ordered *
|
||
gimple_build_omp_ordered (gimple_seq body, tree clauses)
|
||
{
|
||
gomp_ordered *p
|
||
= as_a <gomp_ordered *> (gimple_alloc (GIMPLE_OMP_ORDERED, 0));
|
||
gimple_omp_ordered_set_clauses (p, clauses);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_RETURN statement.
|
||
WAIT_P is true if this is a non-waiting return. */
|
||
|
||
gimple *
|
||
gimple_build_omp_return (bool wait_p)
|
||
{
|
||
gimple *p = gimple_alloc (GIMPLE_OMP_RETURN, 0);
|
||
if (wait_p)
|
||
gimple_omp_return_set_nowait (p);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_SCAN statement.
|
||
|
||
BODY is the sequence of statements to be executed by the scan
|
||
construct.
|
||
CLAUSES are any of the construct's clauses. */
|
||
|
||
gomp_scan *
|
||
gimple_build_omp_scan (gimple_seq body, tree clauses)
|
||
{
|
||
gomp_scan *p
|
||
= as_a <gomp_scan *> (gimple_alloc (GIMPLE_OMP_SCAN, 0));
|
||
gimple_omp_scan_set_clauses (p, clauses);
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_SECTIONS statement.
|
||
|
||
BODY is a sequence of section statements.
|
||
CLAUSES are any of the OMP sections contsruct's clauses: private,
|
||
firstprivate, lastprivate, reduction, and nowait. */
|
||
|
||
gomp_sections *
|
||
gimple_build_omp_sections (gimple_seq body, tree clauses)
|
||
{
|
||
gomp_sections *p
|
||
= as_a <gomp_sections *> (gimple_alloc (GIMPLE_OMP_SECTIONS, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_sections_set_clauses (p, clauses);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_SECTIONS_SWITCH. */
|
||
|
||
gimple *
|
||
gimple_build_omp_sections_switch (void)
|
||
{
|
||
return gimple_alloc (GIMPLE_OMP_SECTIONS_SWITCH, 0);
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_SINGLE statement.
|
||
|
||
BODY is the sequence of statements that will be executed once.
|
||
CLAUSES are any of the OMP single construct's clauses: private, firstprivate,
|
||
copyprivate, nowait. */
|
||
|
||
gomp_single *
|
||
gimple_build_omp_single (gimple_seq body, tree clauses)
|
||
{
|
||
gomp_single *p
|
||
= as_a <gomp_single *> (gimple_alloc (GIMPLE_OMP_SINGLE, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_single_set_clauses (p, clauses);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_TARGET statement.
|
||
|
||
BODY is the sequence of statements that will be executed.
|
||
KIND is the kind of the region.
|
||
CLAUSES are any of the construct's clauses. */
|
||
|
||
gomp_target *
|
||
gimple_build_omp_target (gimple_seq body, int kind, tree clauses)
|
||
{
|
||
gomp_target *p
|
||
= as_a <gomp_target *> (gimple_alloc (GIMPLE_OMP_TARGET, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_target_set_clauses (p, clauses);
|
||
gimple_omp_target_set_kind (p, kind);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_TEAMS statement.
|
||
|
||
BODY is the sequence of statements that will be executed.
|
||
CLAUSES are any of the OMP teams construct's clauses. */
|
||
|
||
gomp_teams *
|
||
gimple_build_omp_teams (gimple_seq body, tree clauses)
|
||
{
|
||
gomp_teams *p = as_a <gomp_teams *> (gimple_alloc (GIMPLE_OMP_TEAMS, 0));
|
||
if (body)
|
||
gimple_omp_set_body (p, body);
|
||
gimple_omp_teams_set_clauses (p, clauses);
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
/* Build a GIMPLE_OMP_ATOMIC_LOAD statement. */
|
||
|
||
gomp_atomic_load *
|
||
gimple_build_omp_atomic_load (tree lhs, tree rhs, enum omp_memory_order mo)
|
||
{
|
||
gomp_atomic_load *p
|
||
= as_a <gomp_atomic_load *> (gimple_alloc (GIMPLE_OMP_ATOMIC_LOAD, 0));
|
||
gimple_omp_atomic_load_set_lhs (p, lhs);
|
||
gimple_omp_atomic_load_set_rhs (p, rhs);
|
||
gimple_omp_atomic_set_memory_order (p, mo);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_OMP_ATOMIC_STORE statement.
|
||
|
||
VAL is the value we are storing. */
|
||
|
||
gomp_atomic_store *
|
||
gimple_build_omp_atomic_store (tree val, enum omp_memory_order mo)
|
||
{
|
||
gomp_atomic_store *p
|
||
= as_a <gomp_atomic_store *> (gimple_alloc (GIMPLE_OMP_ATOMIC_STORE, 0));
|
||
gimple_omp_atomic_store_set_val (p, val);
|
||
gimple_omp_atomic_set_memory_order (p, mo);
|
||
return p;
|
||
}
|
||
|
||
/* Build a GIMPLE_TRANSACTION statement. */
|
||
|
||
gtransaction *
|
||
gimple_build_transaction (gimple_seq body)
|
||
{
|
||
gtransaction *p
|
||
= as_a <gtransaction *> (gimple_alloc (GIMPLE_TRANSACTION, 0));
|
||
gimple_transaction_set_body (p, body);
|
||
gimple_transaction_set_label_norm (p, 0);
|
||
gimple_transaction_set_label_uninst (p, 0);
|
||
gimple_transaction_set_label_over (p, 0);
|
||
return p;
|
||
}
|
||
|
||
#if defined ENABLE_GIMPLE_CHECKING
|
||
/* Complain of a gimple type mismatch and die. */
|
||
|
||
void
|
||
gimple_check_failed (const gimple *gs, const char *file, int line,
|
||
const char *function, enum gimple_code code,
|
||
enum tree_code subcode)
|
||
{
|
||
internal_error ("gimple check: expected %s(%s), have %s(%s) in %s, at %s:%d",
|
||
gimple_code_name[code],
|
||
get_tree_code_name (subcode),
|
||
gimple_code_name[gimple_code (gs)],
|
||
gs->subcode > 0
|
||
? get_tree_code_name ((enum tree_code) gs->subcode)
|
||
: "",
|
||
function, trim_filename (file), line);
|
||
}
|
||
#endif /* ENABLE_GIMPLE_CHECKING */
|
||
|
||
|
||
/* Link gimple statement GS to the end of the sequence *SEQ_P. If
|
||
*SEQ_P is NULL, a new sequence is allocated. */
|
||
|
||
void
|
||
gimple_seq_add_stmt (gimple_seq *seq_p, gimple *gs)
|
||
{
|
||
gimple_stmt_iterator si;
|
||
if (gs == NULL)
|
||
return;
|
||
|
||
si = gsi_last (*seq_p);
|
||
gsi_insert_after (&si, gs, GSI_NEW_STMT);
|
||
}
|
||
|
||
/* Link gimple statement GS to the end of the sequence *SEQ_P. If
|
||
*SEQ_P is NULL, a new sequence is allocated. This function is
|
||
similar to gimple_seq_add_stmt, but does not scan the operands.
|
||
During gimplification, we need to manipulate statement sequences
|
||
before the def/use vectors have been constructed. */
|
||
|
||
void
|
||
gimple_seq_add_stmt_without_update (gimple_seq *seq_p, gimple *gs)
|
||
{
|
||
gimple_stmt_iterator si;
|
||
|
||
if (gs == NULL)
|
||
return;
|
||
|
||
si = gsi_last (*seq_p);
|
||
gsi_insert_after_without_update (&si, gs, GSI_NEW_STMT);
|
||
}
|
||
|
||
/* Append sequence SRC to the end of sequence *DST_P. If *DST_P is
|
||
NULL, a new sequence is allocated. */
|
||
|
||
void
|
||
gimple_seq_add_seq (gimple_seq *dst_p, gimple_seq src)
|
||
{
|
||
gimple_stmt_iterator si;
|
||
if (src == NULL)
|
||
return;
|
||
|
||
si = gsi_last (*dst_p);
|
||
gsi_insert_seq_after (&si, src, GSI_NEW_STMT);
|
||
}
|
||
|
||
/* Append sequence SRC to the end of sequence *DST_P. If *DST_P is
|
||
NULL, a new sequence is allocated. This function is
|
||
similar to gimple_seq_add_seq, but does not scan the operands. */
|
||
|
||
void
|
||
gimple_seq_add_seq_without_update (gimple_seq *dst_p, gimple_seq src)
|
||
{
|
||
gimple_stmt_iterator si;
|
||
if (src == NULL)
|
||
return;
|
||
|
||
si = gsi_last (*dst_p);
|
||
gsi_insert_seq_after_without_update (&si, src, GSI_NEW_STMT);
|
||
}
|
||
|
||
/* Determine whether to assign a location to the statement GS. */
|
||
|
||
static bool
|
||
should_carry_location_p (gimple *gs)
|
||
{
|
||
/* Don't emit a line note for a label. We particularly don't want to
|
||
emit one for the break label, since it doesn't actually correspond
|
||
to the beginning of the loop/switch. */
|
||
if (gimple_code (gs) == GIMPLE_LABEL)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Set the location for gimple statement GS to LOCATION. */
|
||
|
||
static void
|
||
annotate_one_with_location (gimple *gs, location_t location)
|
||
{
|
||
if (!gimple_has_location (gs)
|
||
&& !gimple_do_not_emit_location_p (gs)
|
||
&& should_carry_location_p (gs))
|
||
gimple_set_location (gs, location);
|
||
}
|
||
|
||
/* Set LOCATION for all the statements after iterator GSI in sequence
|
||
SEQ. If GSI is pointing to the end of the sequence, start with the
|
||
first statement in SEQ. */
|
||
|
||
void
|
||
annotate_all_with_location_after (gimple_seq seq, gimple_stmt_iterator gsi,
|
||
location_t location)
|
||
{
|
||
if (gsi_end_p (gsi))
|
||
gsi = gsi_start (seq);
|
||
else
|
||
gsi_next (&gsi);
|
||
|
||
for (; !gsi_end_p (gsi); gsi_next (&gsi))
|
||
annotate_one_with_location (gsi_stmt (gsi), location);
|
||
}
|
||
|
||
/* Set the location for all the statements in a sequence STMT_P to LOCATION. */
|
||
|
||
void
|
||
annotate_all_with_location (gimple_seq stmt_p, location_t location)
|
||
{
|
||
gimple_stmt_iterator i;
|
||
|
||
if (gimple_seq_empty_p (stmt_p))
|
||
return;
|
||
|
||
for (i = gsi_start (stmt_p); !gsi_end_p (i); gsi_next (&i))
|
||
{
|
||
gimple *gs = gsi_stmt (i);
|
||
annotate_one_with_location (gs, location);
|
||
}
|
||
}
|
||
|
||
/* Helper function of empty_body_p. Return true if STMT is an empty
|
||
statement. */
|
||
|
||
static bool
|
||
empty_stmt_p (gimple *stmt)
|
||
{
|
||
if (gimple_code (stmt) == GIMPLE_NOP)
|
||
return true;
|
||
if (gbind *bind_stmt = dyn_cast <gbind *> (stmt))
|
||
return empty_body_p (gimple_bind_body (bind_stmt));
|
||
return false;
|
||
}
|
||
|
||
|
||
/* Return true if BODY contains nothing but empty statements. */
|
||
|
||
bool
|
||
empty_body_p (gimple_seq body)
|
||
{
|
||
gimple_stmt_iterator i;
|
||
|
||
if (gimple_seq_empty_p (body))
|
||
return true;
|
||
for (i = gsi_start (body); !gsi_end_p (i); gsi_next (&i))
|
||
if (!empty_stmt_p (gsi_stmt (i))
|
||
&& !is_gimple_debug (gsi_stmt (i)))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
/* Perform a deep copy of sequence SRC and return the result. */
|
||
|
||
gimple_seq
|
||
gimple_seq_copy (gimple_seq src)
|
||
{
|
||
gimple_stmt_iterator gsi;
|
||
gimple_seq new_seq = NULL;
|
||
gimple *stmt;
|
||
|
||
for (gsi = gsi_start (src); !gsi_end_p (gsi); gsi_next (&gsi))
|
||
{
|
||
stmt = gimple_copy (gsi_stmt (gsi));
|
||
gimple_seq_add_stmt (&new_seq, stmt);
|
||
}
|
||
|
||
return new_seq;
|
||
}
|
||
|
||
|
||
|
||
/* Return true if calls C1 and C2 are known to go to the same function. */
|
||
|
||
bool
|
||
gimple_call_same_target_p (const gimple *c1, const gimple *c2)
|
||
{
|
||
if (gimple_call_internal_p (c1))
|
||
return (gimple_call_internal_p (c2)
|
||
&& gimple_call_internal_fn (c1) == gimple_call_internal_fn (c2)
|
||
&& (!gimple_call_internal_unique_p (as_a <const gcall *> (c1))
|
||
|| c1 == c2));
|
||
else
|
||
return (gimple_call_fn (c1) == gimple_call_fn (c2)
|
||
|| (gimple_call_fndecl (c1)
|
||
&& gimple_call_fndecl (c1) == gimple_call_fndecl (c2)));
|
||
}
|
||
|
||
/* Detect flags from a GIMPLE_CALL. This is just like
|
||
call_expr_flags, but for gimple tuples. */
|
||
|
||
int
|
||
gimple_call_flags (const gimple *stmt)
|
||
{
|
||
int flags = 0;
|
||
|
||
if (gimple_call_internal_p (stmt))
|
||
flags = internal_fn_flags (gimple_call_internal_fn (stmt));
|
||
else
|
||
{
|
||
tree decl = gimple_call_fndecl (stmt);
|
||
if (decl)
|
||
flags = flags_from_decl_or_type (decl);
|
||
flags |= flags_from_decl_or_type (gimple_call_fntype (stmt));
|
||
}
|
||
|
||
if (stmt->subcode & GF_CALL_NOTHROW)
|
||
flags |= ECF_NOTHROW;
|
||
|
||
if (stmt->subcode & GF_CALL_BY_DESCRIPTOR)
|
||
flags |= ECF_BY_DESCRIPTOR;
|
||
|
||
return flags;
|
||
}
|
||
|
||
/* Return the "fn spec" string for call STMT. */
|
||
|
||
static const_tree
|
||
gimple_call_fnspec (const gcall *stmt)
|
||
{
|
||
tree type, attr;
|
||
|
||
if (gimple_call_internal_p (stmt))
|
||
return internal_fn_fnspec (gimple_call_internal_fn (stmt));
|
||
|
||
type = gimple_call_fntype (stmt);
|
||
if (!type)
|
||
return NULL_TREE;
|
||
|
||
attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
|
||
if (!attr)
|
||
return NULL_TREE;
|
||
|
||
return TREE_VALUE (TREE_VALUE (attr));
|
||
}
|
||
|
||
/* Detects argument flags for argument number ARG on call STMT. */
|
||
|
||
int
|
||
gimple_call_arg_flags (const gcall *stmt, unsigned arg)
|
||
{
|
||
const_tree attr = gimple_call_fnspec (stmt);
|
||
|
||
if (!attr || 1 + arg >= (unsigned) TREE_STRING_LENGTH (attr))
|
||
return 0;
|
||
|
||
switch (TREE_STRING_POINTER (attr)[1 + arg])
|
||
{
|
||
case 'x':
|
||
case 'X':
|
||
return EAF_UNUSED;
|
||
|
||
case 'R':
|
||
return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE;
|
||
|
||
case 'r':
|
||
return EAF_NOCLOBBER | EAF_NOESCAPE;
|
||
|
||
case 'W':
|
||
return EAF_DIRECT | EAF_NOESCAPE;
|
||
|
||
case 'w':
|
||
return EAF_NOESCAPE;
|
||
|
||
case '.':
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* Detects return flags for the call STMT. */
|
||
|
||
int
|
||
gimple_call_return_flags (const gcall *stmt)
|
||
{
|
||
const_tree attr;
|
||
|
||
if (gimple_call_flags (stmt) & ECF_MALLOC)
|
||
return ERF_NOALIAS;
|
||
|
||
attr = gimple_call_fnspec (stmt);
|
||
if (!attr || TREE_STRING_LENGTH (attr) < 1)
|
||
return 0;
|
||
|
||
switch (TREE_STRING_POINTER (attr)[0])
|
||
{
|
||
case '1':
|
||
case '2':
|
||
case '3':
|
||
case '4':
|
||
return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');
|
||
|
||
case 'm':
|
||
return ERF_NOALIAS;
|
||
|
||
case '.':
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
|
||
/* Return true if call STMT is known to return a non-zero result. */
|
||
|
||
bool
|
||
gimple_call_nonnull_result_p (gcall *call)
|
||
{
|
||
tree fndecl = gimple_call_fndecl (call);
|
||
if (!fndecl)
|
||
return false;
|
||
if (flag_delete_null_pointer_checks && !flag_check_new
|
||
&& DECL_IS_OPERATOR_NEW_P (fndecl)
|
||
&& !TREE_NOTHROW (fndecl))
|
||
return true;
|
||
|
||
/* References are always non-NULL. */
|
||
if (flag_delete_null_pointer_checks
|
||
&& TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE)
|
||
return true;
|
||
|
||
if (flag_delete_null_pointer_checks
|
||
&& lookup_attribute ("returns_nonnull",
|
||
TYPE_ATTRIBUTES (gimple_call_fntype (call))))
|
||
return true;
|
||
return gimple_alloca_call_p (call);
|
||
}
|
||
|
||
|
||
/* If CALL returns a non-null result in an argument, return that arg. */
|
||
|
||
tree
|
||
gimple_call_nonnull_arg (gcall *call)
|
||
{
|
||
tree fndecl = gimple_call_fndecl (call);
|
||
if (!fndecl)
|
||
return NULL_TREE;
|
||
|
||
unsigned rf = gimple_call_return_flags (call);
|
||
if (rf & ERF_RETURNS_ARG)
|
||
{
|
||
unsigned argnum = rf & ERF_RETURN_ARG_MASK;
|
||
if (argnum < gimple_call_num_args (call))
|
||
{
|
||
tree arg = gimple_call_arg (call, argnum);
|
||
if (SSA_VAR_P (arg)
|
||
&& infer_nonnull_range_by_attribute (call, arg))
|
||
return arg;
|
||
}
|
||
}
|
||
return NULL_TREE;
|
||
}
|
||
|
||
|
||
/* Return true if GS is a copy assignment. */
|
||
|
||
bool
|
||
gimple_assign_copy_p (gimple *gs)
|
||
{
|
||
return (gimple_assign_single_p (gs)
|
||
&& is_gimple_val (gimple_op (gs, 1)));
|
||
}
|
||
|
||
|
||
/* Return true if GS is a SSA_NAME copy assignment. */
|
||
|
||
bool
|
||
gimple_assign_ssa_name_copy_p (gimple *gs)
|
||
{
|
||
return (gimple_assign_single_p (gs)
|
||
&& TREE_CODE (gimple_assign_lhs (gs)) == SSA_NAME
|
||
&& TREE_CODE (gimple_assign_rhs1 (gs)) == SSA_NAME);
|
||
}
|
||
|
||
|
||
/* Return true if GS is an assignment with a unary RHS, but the
|
||
operator has no effect on the assigned value. The logic is adapted
|
||
from STRIP_NOPS. This predicate is intended to be used in tuplifying
|
||
instances in which STRIP_NOPS was previously applied to the RHS of
|
||
an assignment.
|
||
|
||
NOTE: In the use cases that led to the creation of this function
|
||
and of gimple_assign_single_p, it is typical to test for either
|
||
condition and to proceed in the same manner. In each case, the
|
||
assigned value is represented by the single RHS operand of the
|
||
assignment. I suspect there may be cases where gimple_assign_copy_p,
|
||
gimple_assign_single_p, or equivalent logic is used where a similar
|
||
treatment of unary NOPs is appropriate. */
|
||
|
||
bool
|
||
gimple_assign_unary_nop_p (gimple *gs)
|
||
{
|
||
return (is_gimple_assign (gs)
|
||
&& (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (gs))
|
||
|| gimple_assign_rhs_code (gs) == NON_LVALUE_EXPR)
|
||
&& gimple_assign_rhs1 (gs) != error_mark_node
|
||
&& (TYPE_MODE (TREE_TYPE (gimple_assign_lhs (gs)))
|
||
== TYPE_MODE (TREE_TYPE (gimple_assign_rhs1 (gs)))));
|
||
}
|
||
|
||
/* Set BB to be the basic block holding G. */
|
||
|
||
void
|
||
gimple_set_bb (gimple *stmt, basic_block bb)
|
||
{
|
||
stmt->bb = bb;
|
||
|
||
if (gimple_code (stmt) != GIMPLE_LABEL)
|
||
return;
|
||
|
||
/* If the statement is a label, add the label to block-to-labels map
|
||
so that we can speed up edge creation for GIMPLE_GOTOs. */
|
||
if (cfun->cfg)
|
||
{
|
||
tree t;
|
||
int uid;
|
||
|
||
t = gimple_label_label (as_a <glabel *> (stmt));
|
||
uid = LABEL_DECL_UID (t);
|
||
if (uid == -1)
|
||
{
|
||
unsigned old_len =
|
||
vec_safe_length (label_to_block_map_for_fn (cfun));
|
||
LABEL_DECL_UID (t) = uid = cfun->cfg->last_label_uid++;
|
||
if (old_len <= (unsigned) uid)
|
||
{
|
||
unsigned new_len = 3 * uid / 2 + 1;
|
||
|
||
vec_safe_grow_cleared (label_to_block_map_for_fn (cfun),
|
||
new_len);
|
||
}
|
||
}
|
||
|
||
(*label_to_block_map_for_fn (cfun))[uid] = bb;
|
||
}
|
||
}
|
||
|
||
|
||
/* Modify the RHS of the assignment pointed-to by GSI using the
|
||
operands in the expression tree EXPR.
|
||
|
||
NOTE: The statement pointed-to by GSI may be reallocated if it
|
||
did not have enough operand slots.
|
||
|
||
This function is useful to convert an existing tree expression into
|
||
the flat representation used for the RHS of a GIMPLE assignment.
|
||
It will reallocate memory as needed to expand or shrink the number
|
||
of operand slots needed to represent EXPR.
|
||
|
||
NOTE: If you find yourself building a tree and then calling this
|
||
function, you are most certainly doing it the slow way. It is much
|
||
better to build a new assignment or to use the function
|
||
gimple_assign_set_rhs_with_ops, which does not require an
|
||
expression tree to be built. */
|
||
|
||
void
|
||
gimple_assign_set_rhs_from_tree (gimple_stmt_iterator *gsi, tree expr)
|
||
{
|
||
enum tree_code subcode;
|
||
tree op1, op2, op3;
|
||
|
||
extract_ops_from_tree (expr, &subcode, &op1, &op2, &op3);
|
||
gimple_assign_set_rhs_with_ops (gsi, subcode, op1, op2, op3);
|
||
}
|
||
|
||
|
||
/* Set the RHS of assignment statement pointed-to by GSI to CODE with
|
||
operands OP1, OP2 and OP3.
|
||
|
||
NOTE: The statement pointed-to by GSI may be reallocated if it
|
||
did not have enough operand slots. */
|
||
|
||
void
|
||
gimple_assign_set_rhs_with_ops (gimple_stmt_iterator *gsi, enum tree_code code,
|
||
tree op1, tree op2, tree op3)
|
||
{
|
||
unsigned new_rhs_ops = get_gimple_rhs_num_ops (code);
|
||
gimple *stmt = gsi_stmt (*gsi);
|
||
gimple *old_stmt = stmt;
|
||
|
||
/* If the new CODE needs more operands, allocate a new statement. */
|
||
if (gimple_num_ops (stmt) < new_rhs_ops + 1)
|
||
{
|
||
tree lhs = gimple_assign_lhs (old_stmt);
|
||
stmt = gimple_alloc (gimple_code (old_stmt), new_rhs_ops + 1);
|
||
memcpy (stmt, old_stmt, gimple_size (gimple_code (old_stmt)));
|
||
gimple_init_singleton (stmt);
|
||
|
||
/* The LHS needs to be reset as this also changes the SSA name
|
||
on the LHS. */
|
||
gimple_assign_set_lhs (stmt, lhs);
|
||
}
|
||
|
||
gimple_set_num_ops (stmt, new_rhs_ops + 1);
|
||
gimple_set_subcode (stmt, code);
|
||
gimple_assign_set_rhs1 (stmt, op1);
|
||
if (new_rhs_ops > 1)
|
||
gimple_assign_set_rhs2 (stmt, op2);
|
||
if (new_rhs_ops > 2)
|
||
gimple_assign_set_rhs3 (stmt, op3);
|
||
if (stmt != old_stmt)
|
||
gsi_replace (gsi, stmt, false);
|
||
}
|
||
|
||
|
||
/* Return the LHS of a statement that performs an assignment,
|
||
either a GIMPLE_ASSIGN or a GIMPLE_CALL. Returns NULL_TREE
|
||
for a call to a function that returns no value, or for a
|
||
statement other than an assignment or a call. */
|
||
|
||
tree
|
||
gimple_get_lhs (const gimple *stmt)
|
||
{
|
||
enum gimple_code code = gimple_code (stmt);
|
||
|
||
if (code == GIMPLE_ASSIGN)
|
||
return gimple_assign_lhs (stmt);
|
||
else if (code == GIMPLE_CALL)
|
||
return gimple_call_lhs (stmt);
|
||
else if (code == GIMPLE_PHI)
|
||
return gimple_phi_result (stmt);
|
||
else
|
||
return NULL_TREE;
|
||
}
|
||
|
||
|
||
/* Set the LHS of a statement that performs an assignment,
|
||
either a GIMPLE_ASSIGN or a GIMPLE_CALL. */
|
||
|
||
void
|
||
gimple_set_lhs (gimple *stmt, tree lhs)
|
||
{
|
||
enum gimple_code code = gimple_code (stmt);
|
||
|
||
if (code == GIMPLE_ASSIGN)
|
||
gimple_assign_set_lhs (stmt, lhs);
|
||
else if (code == GIMPLE_CALL)
|
||
gimple_call_set_lhs (stmt, lhs);
|
||
else
|
||
gcc_unreachable ();
|
||
}
|
||
|
||
|
||
/* Return a deep copy of statement STMT. All the operands from STMT
|
||
are reallocated and copied using unshare_expr. The DEF, USE, VDEF
|
||
and VUSE operand arrays are set to empty in the new copy. The new
|
||
copy isn't part of any sequence. */
|
||
|
||
gimple *
|
||
gimple_copy (gimple *stmt)
|
||
{
|
||
enum gimple_code code = gimple_code (stmt);
|
||
unsigned num_ops = gimple_num_ops (stmt);
|
||
gimple *copy = gimple_alloc (code, num_ops);
|
||
unsigned i;
|
||
|
||
/* Shallow copy all the fields from STMT. */
|
||
memcpy (copy, stmt, gimple_size (code));
|
||
gimple_init_singleton (copy);
|
||
|
||
/* If STMT has sub-statements, deep-copy them as well. */
|
||
if (gimple_has_substatements (stmt))
|
||
{
|
||
gimple_seq new_seq;
|
||
tree t;
|
||
|
||
switch (gimple_code (stmt))
|
||
{
|
||
case GIMPLE_BIND:
|
||
{
|
||
gbind *bind_stmt = as_a <gbind *> (stmt);
|
||
gbind *bind_copy = as_a <gbind *> (copy);
|
||
new_seq = gimple_seq_copy (gimple_bind_body (bind_stmt));
|
||
gimple_bind_set_body (bind_copy, new_seq);
|
||
gimple_bind_set_vars (bind_copy,
|
||
unshare_expr (gimple_bind_vars (bind_stmt)));
|
||
gimple_bind_set_block (bind_copy, gimple_bind_block (bind_stmt));
|
||
}
|
||
break;
|
||
|
||
case GIMPLE_CATCH:
|
||
{
|
||
gcatch *catch_stmt = as_a <gcatch *> (stmt);
|
||
gcatch *catch_copy = as_a <gcatch *> (copy);
|
||
new_seq = gimple_seq_copy (gimple_catch_handler (catch_stmt));
|
||
gimple_catch_set_handler (catch_copy, new_seq);
|
||
t = unshare_expr (gimple_catch_types (catch_stmt));
|
||
gimple_catch_set_types (catch_copy, t);
|
||
}
|
||
break;
|
||
|
||
case GIMPLE_EH_FILTER:
|
||
{
|
||
geh_filter *eh_filter_stmt = as_a <geh_filter *> (stmt);
|
||
geh_filter *eh_filter_copy = as_a <geh_filter *> (copy);
|
||
new_seq
|
||
= gimple_seq_copy (gimple_eh_filter_failure (eh_filter_stmt));
|
||
gimple_eh_filter_set_failure (eh_filter_copy, new_seq);
|
||
t = unshare_expr (gimple_eh_filter_types (eh_filter_stmt));
|
||
gimple_eh_filter_set_types (eh_filter_copy, t);
|
||
}
|
||
break;
|
||
|
||
case GIMPLE_EH_ELSE:
|
||
{
|
||
geh_else *eh_else_stmt = as_a <geh_else *> (stmt);
|
||
geh_else *eh_else_copy = as_a <geh_else *> (copy);
|
||
new_seq = gimple_seq_copy (gimple_eh_else_n_body (eh_else_stmt));
|
||
gimple_eh_else_set_n_body (eh_else_copy, new_seq);
|
||
new_seq = gimple_seq_copy (gimple_eh_else_e_body (eh_else_stmt));
|
||
gimple_eh_else_set_e_body (eh_else_copy, new_seq);
|
||
}
|
||
break;
|
||
|
||
case GIMPLE_TRY:
|
||
{
|
||
gtry *try_stmt = as_a <gtry *> (stmt);
|
||
gtry *try_copy = as_a <gtry *> (copy);
|
||
new_seq = gimple_seq_copy (gimple_try_eval (try_stmt));
|
||
gimple_try_set_eval (try_copy, new_seq);
|
||
new_seq = gimple_seq_copy (gimple_try_cleanup (try_stmt));
|
||
gimple_try_set_cleanup (try_copy, new_seq);
|
||
}
|
||
break;
|
||
|
||
case GIMPLE_OMP_FOR:
|
||
new_seq = gimple_seq_copy (gimple_omp_for_pre_body (stmt));
|
||
gimple_omp_for_set_pre_body (copy, new_seq);
|
||
t = unshare_expr (gimple_omp_for_clauses (stmt));
|
||
gimple_omp_for_set_clauses (copy, t);
|
||
{
|
||
gomp_for *omp_for_copy = as_a <gomp_for *> (copy);
|
||
omp_for_copy->iter = ggc_vec_alloc<gimple_omp_for_iter>
|
||
( gimple_omp_for_collapse (stmt));
|
||
}
|
||
for (i = 0; i < gimple_omp_for_collapse (stmt); i++)
|
||
{
|
||
gimple_omp_for_set_cond (copy, i,
|
||
gimple_omp_for_cond (stmt, i));
|
||
gimple_omp_for_set_index (copy, i,
|
||
gimple_omp_for_index (stmt, i));
|
||
t = unshare_expr (gimple_omp_for_initial (stmt, i));
|
||
gimple_omp_for_set_initial (copy, i, t);
|
||
t = unshare_expr (gimple_omp_for_final (stmt, i));
|
||
gimple_omp_for_set_final (copy, i, t);
|
||
t = unshare_expr (gimple_omp_for_incr (stmt, i));
|
||
gimple_omp_for_set_incr (copy, i, t);
|
||
}
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_PARALLEL:
|
||
{
|
||
gomp_parallel *omp_par_stmt = as_a <gomp_parallel *> (stmt);
|
||
gomp_parallel *omp_par_copy = as_a <gomp_parallel *> (copy);
|
||
t = unshare_expr (gimple_omp_parallel_clauses (omp_par_stmt));
|
||
gimple_omp_parallel_set_clauses (omp_par_copy, t);
|
||
t = unshare_expr (gimple_omp_parallel_child_fn (omp_par_stmt));
|
||
gimple_omp_parallel_set_child_fn (omp_par_copy, t);
|
||
t = unshare_expr (gimple_omp_parallel_data_arg (omp_par_stmt));
|
||
gimple_omp_parallel_set_data_arg (omp_par_copy, t);
|
||
}
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_TASK:
|
||
t = unshare_expr (gimple_omp_task_clauses (stmt));
|
||
gimple_omp_task_set_clauses (copy, t);
|
||
t = unshare_expr (gimple_omp_task_child_fn (stmt));
|
||
gimple_omp_task_set_child_fn (copy, t);
|
||
t = unshare_expr (gimple_omp_task_data_arg (stmt));
|
||
gimple_omp_task_set_data_arg (copy, t);
|
||
t = unshare_expr (gimple_omp_task_copy_fn (stmt));
|
||
gimple_omp_task_set_copy_fn (copy, t);
|
||
t = unshare_expr (gimple_omp_task_arg_size (stmt));
|
||
gimple_omp_task_set_arg_size (copy, t);
|
||
t = unshare_expr (gimple_omp_task_arg_align (stmt));
|
||
gimple_omp_task_set_arg_align (copy, t);
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_CRITICAL:
|
||
t = unshare_expr (gimple_omp_critical_name
|
||
(as_a <gomp_critical *> (stmt)));
|
||
gimple_omp_critical_set_name (as_a <gomp_critical *> (copy), t);
|
||
t = unshare_expr (gimple_omp_critical_clauses
|
||
(as_a <gomp_critical *> (stmt)));
|
||
gimple_omp_critical_set_clauses (as_a <gomp_critical *> (copy), t);
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_ORDERED:
|
||
t = unshare_expr (gimple_omp_ordered_clauses
|
||
(as_a <gomp_ordered *> (stmt)));
|
||
gimple_omp_ordered_set_clauses (as_a <gomp_ordered *> (copy), t);
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_SCAN:
|
||
t = gimple_omp_scan_clauses (as_a <gomp_scan *> (stmt));
|
||
t = unshare_expr (t);
|
||
gimple_omp_scan_set_clauses (as_a <gomp_scan *> (copy), t);
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_TASKGROUP:
|
||
t = unshare_expr (gimple_omp_taskgroup_clauses (stmt));
|
||
gimple_omp_taskgroup_set_clauses (copy, t);
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_SECTIONS:
|
||
t = unshare_expr (gimple_omp_sections_clauses (stmt));
|
||
gimple_omp_sections_set_clauses (copy, t);
|
||
t = unshare_expr (gimple_omp_sections_control (stmt));
|
||
gimple_omp_sections_set_control (copy, t);
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_SINGLE:
|
||
{
|
||
gomp_single *omp_single_copy = as_a <gomp_single *> (copy);
|
||
t = unshare_expr (gimple_omp_single_clauses (stmt));
|
||
gimple_omp_single_set_clauses (omp_single_copy, t);
|
||
}
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_TARGET:
|
||
{
|
||
gomp_target *omp_target_stmt = as_a <gomp_target *> (stmt);
|
||
gomp_target *omp_target_copy = as_a <gomp_target *> (copy);
|
||
t = unshare_expr (gimple_omp_target_clauses (omp_target_stmt));
|
||
gimple_omp_target_set_clauses (omp_target_copy, t);
|
||
t = unshare_expr (gimple_omp_target_data_arg (omp_target_stmt));
|
||
gimple_omp_target_set_data_arg (omp_target_copy, t);
|
||
}
|
||
goto copy_omp_body;
|
||
|
||
case GIMPLE_OMP_TEAMS:
|
||
{
|
||
gomp_teams *omp_teams_copy = as_a <gomp_teams *> (copy);
|
||
t = unshare_expr (gimple_omp_teams_clauses (stmt));
|
||
gimple_omp_teams_set_clauses (omp_teams_copy, t);
|
||
}
|
||
/* FALLTHRU */
|
||
|
||
case GIMPLE_OMP_SECTION:
|
||
case GIMPLE_OMP_MASTER:
|
||
case GIMPLE_OMP_GRID_BODY:
|
||
copy_omp_body:
|
||
new_seq = gimple_seq_copy (gimple_omp_body (stmt));
|
||
gimple_omp_set_body (copy, new_seq);
|
||
break;
|
||
|
||
case GIMPLE_TRANSACTION:
|
||
new_seq = gimple_seq_copy (gimple_transaction_body (
|
||
as_a <gtransaction *> (stmt)));
|
||
gimple_transaction_set_body (as_a <gtransaction *> (copy),
|
||
new_seq);
|
||
break;
|
||
|
||
case GIMPLE_WITH_CLEANUP_EXPR:
|
||
new_seq = gimple_seq_copy (gimple_wce_cleanup (stmt));
|
||
gimple_wce_set_cleanup (copy, new_seq);
|
||
break;
|
||
|
||
default:
|
||
gcc_unreachable ();
|
||
}
|
||
}
|
||
|
||
/* Make copy of operands. */
|
||
for (i = 0; i < num_ops; i++)
|
||
gimple_set_op (copy, i, unshare_expr (gimple_op (stmt, i)));
|
||
|
||
if (gimple_has_mem_ops (stmt))
|
||
{
|
||
gimple_set_vdef (copy, gimple_vdef (stmt));
|
||
gimple_set_vuse (copy, gimple_vuse (stmt));
|
||
}
|
||
|
||
/* Clear out SSA operand vectors on COPY. */
|
||
if (gimple_has_ops (stmt))
|
||
{
|
||
gimple_set_use_ops (copy, NULL);
|
||
|
||
/* SSA operands need to be updated. */
|
||
gimple_set_modified (copy, true);
|
||
}
|
||
|
||
if (gimple_debug_nonbind_marker_p (stmt))
|
||
cfun->debug_marker_count++;
|
||
|
||
return copy;
|
||
}
|
||
|
||
/* Move OLD_STMT's vuse and vdef operands to NEW_STMT, on the assumption
|
||
that OLD_STMT is about to be removed. */
|
||
|
||
void
|
||
gimple_move_vops (gimple *new_stmt, gimple *old_stmt)
|
||
{
|
||
tree vdef = gimple_vdef (old_stmt);
|
||
gimple_set_vuse (new_stmt, gimple_vuse (old_stmt));
|
||
gimple_set_vdef (new_stmt, vdef);
|
||
if (vdef && TREE_CODE (vdef) == SSA_NAME)
|
||
SSA_NAME_DEF_STMT (vdef) = new_stmt;
|
||
}
|
||
|
||
/* Return true if statement S has side-effects. We consider a
|
||
statement to have side effects if:
|
||
|
||
- It is a GIMPLE_CALL not marked with ECF_PURE or ECF_CONST.
|
||
- Any of its operands are marked TREE_THIS_VOLATILE or TREE_SIDE_EFFECTS. */
|
||
|
||
bool
|
||
gimple_has_side_effects (const gimple *s)
|
||
{
|
||
if (is_gimple_debug (s))
|
||
return false;
|
||
|
||
/* We don't have to scan the arguments to check for
|
||
volatile arguments, though, at present, we still
|
||
do a scan to check for TREE_SIDE_EFFECTS. */
|
||
if (gimple_has_volatile_ops (s))
|
||
return true;
|
||
|
||
if (gimple_code (s) == GIMPLE_ASM
|
||
&& gimple_asm_volatile_p (as_a <const gasm *> (s)))
|
||
return true;
|
||
|
||
if (is_gimple_call (s))
|
||
{
|
||
int flags = gimple_call_flags (s);
|
||
|
||
/* An infinite loop is considered a side effect. */
|
||
if (!(flags & (ECF_CONST | ECF_PURE))
|
||
|| (flags & ECF_LOOPING_CONST_OR_PURE))
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Helper for gimple_could_trap_p and gimple_assign_rhs_could_trap_p.
|
||
Return true if S can trap. When INCLUDE_MEM is true, check whether
|
||
the memory operations could trap. When INCLUDE_STORES is true and
|
||
S is a GIMPLE_ASSIGN, the LHS of the assignment is also checked. */
|
||
|
||
bool
|
||
gimple_could_trap_p_1 (gimple *s, bool include_mem, bool include_stores)
|
||
{
|
||
tree t, div = NULL_TREE;
|
||
enum tree_code op;
|
||
|
||
if (include_mem)
|
||
{
|
||
unsigned i, start = (is_gimple_assign (s) && !include_stores) ? 1 : 0;
|
||
|
||
for (i = start; i < gimple_num_ops (s); i++)
|
||
if (tree_could_trap_p (gimple_op (s, i)))
|
||
return true;
|
||
}
|
||
|
||
switch (gimple_code (s))
|
||
{
|
||
case GIMPLE_ASM:
|
||
return gimple_asm_volatile_p (as_a <gasm *> (s));
|
||
|
||
case GIMPLE_CALL:
|
||
t = gimple_call_fndecl (s);
|
||
/* Assume that calls to weak functions may trap. */
|
||
if (!t || !DECL_P (t) || DECL_WEAK (t))
|
||
return true;
|
||
return false;
|
||
|
||
case GIMPLE_ASSIGN:
|
||
t = gimple_expr_type (s);
|
||
op = gimple_assign_rhs_code (s);
|
||
if (get_gimple_rhs_class (op) == GIMPLE_BINARY_RHS)
|
||
div = gimple_assign_rhs2 (s);
|
||
return (operation_could_trap_p (op, FLOAT_TYPE_P (t),
|
||
(INTEGRAL_TYPE_P (t)
|
||
&& TYPE_OVERFLOW_TRAPS (t)),
|
||
div));
|
||
|
||
case GIMPLE_COND:
|
||
t = TREE_TYPE (gimple_cond_lhs (s));
|
||
return operation_could_trap_p (gimple_cond_code (s),
|
||
FLOAT_TYPE_P (t), false, NULL_TREE);
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Return true if statement S can trap. */
|
||
|
||
bool
|
||
gimple_could_trap_p (gimple *s)
|
||
{
|
||
return gimple_could_trap_p_1 (s, true, true);
|
||
}
|
||
|
||
/* Return true if RHS of a GIMPLE_ASSIGN S can trap. */
|
||
|
||
bool
|
||
gimple_assign_rhs_could_trap_p (gimple *s)
|
||
{
|
||
gcc_assert (is_gimple_assign (s));
|
||
return gimple_could_trap_p_1 (s, true, false);
|
||
}
|
||
|
||
|
||
/* Print debugging information for gimple stmts generated. */
|
||
|
||
void
|
||
dump_gimple_statistics (void)
|
||
{
|
||
int i;
|
||
uint64_t total_tuples = 0, total_bytes = 0;
|
||
|
||
if (! GATHER_STATISTICS)
|
||
{
|
||
fprintf (stderr, "No GIMPLE statistics\n");
|
||
return;
|
||
}
|
||
|
||
fprintf (stderr, "\nGIMPLE statements\n");
|
||
fprintf (stderr, "Kind Stmts Bytes\n");
|
||
fprintf (stderr, "---------------------------------------\n");
|
||
for (i = 0; i < (int) gimple_alloc_kind_all; ++i)
|
||
{
|
||
fprintf (stderr, "%-20s %7" PRIu64 "%c %10" PRIu64 "%c\n",
|
||
gimple_alloc_kind_names[i],
|
||
SIZE_AMOUNT (gimple_alloc_counts[i]),
|
||
SIZE_AMOUNT (gimple_alloc_sizes[i]));
|
||
total_tuples += gimple_alloc_counts[i];
|
||
total_bytes += gimple_alloc_sizes[i];
|
||
}
|
||
fprintf (stderr, "---------------------------------------\n");
|
||
fprintf (stderr, "%-20s %7" PRIu64 "%c %10" PRIu64 "%c\n", "Total",
|
||
SIZE_AMOUNT (total_tuples), SIZE_AMOUNT (total_bytes));
|
||
fprintf (stderr, "---------------------------------------\n");
|
||
}
|
||
|
||
|
||
/* Return the number of operands needed on the RHS of a GIMPLE
|
||
assignment for an expression with tree code CODE. */
|
||
|
||
unsigned
|
||
get_gimple_rhs_num_ops (enum tree_code code)
|
||
{
|
||
switch (get_gimple_rhs_class (code))
|
||
{
|
||
case GIMPLE_UNARY_RHS:
|
||
case GIMPLE_SINGLE_RHS:
|
||
return 1;
|
||
case GIMPLE_BINARY_RHS:
|
||
return 2;
|
||
case GIMPLE_TERNARY_RHS:
|
||
return 3;
|
||
default:
|
||
gcc_unreachable ();
|
||
}
|
||
}
|
||
|
||
#define DEFTREECODE(SYM, STRING, TYPE, NARGS) \
|
||
(unsigned char) \
|
||
((TYPE) == tcc_unary ? GIMPLE_UNARY_RHS \
|
||
: ((TYPE) == tcc_binary \
|
||
|| (TYPE) == tcc_comparison) ? GIMPLE_BINARY_RHS \
|
||
: ((TYPE) == tcc_constant \
|
||
|| (TYPE) == tcc_declaration \
|
||
|| (TYPE) == tcc_reference) ? GIMPLE_SINGLE_RHS \
|
||
: ((SYM) == TRUTH_AND_EXPR \
|
||
|| (SYM) == TRUTH_OR_EXPR \
|
||
|| (SYM) == TRUTH_XOR_EXPR) ? GIMPLE_BINARY_RHS \
|
||
: (SYM) == TRUTH_NOT_EXPR ? GIMPLE_UNARY_RHS \
|
||
: ((SYM) == COND_EXPR \
|
||
|| (SYM) == WIDEN_MULT_PLUS_EXPR \
|
||
|| (SYM) == WIDEN_MULT_MINUS_EXPR \
|
||
|| (SYM) == DOT_PROD_EXPR \
|
||
|| (SYM) == SAD_EXPR \
|
||
|| (SYM) == REALIGN_LOAD_EXPR \
|
||
|| (SYM) == VEC_COND_EXPR \
|
||
|| (SYM) == VEC_PERM_EXPR \
|
||
|| (SYM) == BIT_INSERT_EXPR) ? GIMPLE_TERNARY_RHS \
|
||
: ((SYM) == CONSTRUCTOR \
|
||
|| (SYM) == OBJ_TYPE_REF \
|
||
|| (SYM) == ASSERT_EXPR \
|
||
|| (SYM) == ADDR_EXPR \
|
||
|| (SYM) == WITH_SIZE_EXPR \
|
||
|| (SYM) == SSA_NAME) ? GIMPLE_SINGLE_RHS \
|
||
: GIMPLE_INVALID_RHS),
|
||
#define END_OF_BASE_TREE_CODES (unsigned char) GIMPLE_INVALID_RHS,
|
||
|
||
const unsigned char gimple_rhs_class_table[] = {
|
||
#include "all-tree.def"
|
||
};
|
||
|
||
#undef DEFTREECODE
|
||
#undef END_OF_BASE_TREE_CODES
|
||
|
||
/* Canonicalize a tree T for use in a COND_EXPR as conditional. Returns
|
||
a canonicalized tree that is valid for a COND_EXPR or NULL_TREE, if
|
||
we failed to create one. */
|
||
|
||
tree
|
||
canonicalize_cond_expr_cond (tree t)
|
||
{
|
||
/* Strip conversions around boolean operations. */
|
||
if (CONVERT_EXPR_P (t)
|
||
&& (truth_value_p (TREE_CODE (TREE_OPERAND (t, 0)))
|
||
|| TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
|
||
== BOOLEAN_TYPE))
|
||
t = TREE_OPERAND (t, 0);
|
||
|
||
/* For !x use x == 0. */
|
||
if (TREE_CODE (t) == TRUTH_NOT_EXPR)
|
||
{
|
||
tree top0 = TREE_OPERAND (t, 0);
|
||
t = build2 (EQ_EXPR, TREE_TYPE (t),
|
||
top0, build_int_cst (TREE_TYPE (top0), 0));
|
||
}
|
||
/* For cmp ? 1 : 0 use cmp. */
|
||
else if (TREE_CODE (t) == COND_EXPR
|
||
&& COMPARISON_CLASS_P (TREE_OPERAND (t, 0))
|
||
&& integer_onep (TREE_OPERAND (t, 1))
|
||
&& integer_zerop (TREE_OPERAND (t, 2)))
|
||
{
|
||
tree top0 = TREE_OPERAND (t, 0);
|
||
t = build2 (TREE_CODE (top0), TREE_TYPE (t),
|
||
TREE_OPERAND (top0, 0), TREE_OPERAND (top0, 1));
|
||
}
|
||
/* For x ^ y use x != y. */
|
||
else if (TREE_CODE (t) == BIT_XOR_EXPR)
|
||
t = build2 (NE_EXPR, TREE_TYPE (t),
|
||
TREE_OPERAND (t, 0), TREE_OPERAND (t, 1));
|
||
|
||
if (is_gimple_condexpr (t))
|
||
return t;
|
||
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Build a GIMPLE_CALL identical to STMT but skipping the arguments in
|
||
the positions marked by the set ARGS_TO_SKIP. */
|
||
|
||
gcall *
|
||
gimple_call_copy_skip_args (gcall *stmt, bitmap args_to_skip)
|
||
{
|
||
int i;
|
||
int nargs = gimple_call_num_args (stmt);
|
||
auto_vec<tree> vargs (nargs);
|
||
gcall *new_stmt;
|
||
|
||
for (i = 0; i < nargs; i++)
|
||
if (!bitmap_bit_p (args_to_skip, i))
|
||
vargs.quick_push (gimple_call_arg (stmt, i));
|
||
|
||
if (gimple_call_internal_p (stmt))
|
||
new_stmt = gimple_build_call_internal_vec (gimple_call_internal_fn (stmt),
|
||
vargs);
|
||
else
|
||
new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs);
|
||
|
||
if (gimple_call_lhs (stmt))
|
||
gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
|
||
|
||
gimple_set_vuse (new_stmt, gimple_vuse (stmt));
|
||
gimple_set_vdef (new_stmt, gimple_vdef (stmt));
|
||
|
||
if (gimple_has_location (stmt))
|
||
gimple_set_location (new_stmt, gimple_location (stmt));
|
||
gimple_call_copy_flags (new_stmt, stmt);
|
||
gimple_call_set_chain (new_stmt, gimple_call_chain (stmt));
|
||
|
||
gimple_set_modified (new_stmt, true);
|
||
|
||
return new_stmt;
|
||
}
|
||
|
||
|
||
|
||
/* Return true if the field decls F1 and F2 are at the same offset.
|
||
|
||
This is intended to be used on GIMPLE types only. */
|
||
|
||
bool
|
||
gimple_compare_field_offset (tree f1, tree f2)
|
||
{
|
||
if (DECL_OFFSET_ALIGN (f1) == DECL_OFFSET_ALIGN (f2))
|
||
{
|
||
tree offset1 = DECL_FIELD_OFFSET (f1);
|
||
tree offset2 = DECL_FIELD_OFFSET (f2);
|
||
return ((offset1 == offset2
|
||
/* Once gimplification is done, self-referential offsets are
|
||
instantiated as operand #2 of the COMPONENT_REF built for
|
||
each access and reset. Therefore, they are not relevant
|
||
anymore and fields are interchangeable provided that they
|
||
represent the same access. */
|
||
|| (TREE_CODE (offset1) == PLACEHOLDER_EXPR
|
||
&& TREE_CODE (offset2) == PLACEHOLDER_EXPR
|
||
&& (DECL_SIZE (f1) == DECL_SIZE (f2)
|
||
|| (TREE_CODE (DECL_SIZE (f1)) == PLACEHOLDER_EXPR
|
||
&& TREE_CODE (DECL_SIZE (f2)) == PLACEHOLDER_EXPR)
|
||
|| operand_equal_p (DECL_SIZE (f1), DECL_SIZE (f2), 0))
|
||
&& DECL_ALIGN (f1) == DECL_ALIGN (f2))
|
||
|| operand_equal_p (offset1, offset2, 0))
|
||
&& tree_int_cst_equal (DECL_FIELD_BIT_OFFSET (f1),
|
||
DECL_FIELD_BIT_OFFSET (f2)));
|
||
}
|
||
|
||
/* Fortran and C do not always agree on what DECL_OFFSET_ALIGN
|
||
should be, so handle differing ones specially by decomposing
|
||
the offset into a byte and bit offset manually. */
|
||
if (tree_fits_shwi_p (DECL_FIELD_OFFSET (f1))
|
||
&& tree_fits_shwi_p (DECL_FIELD_OFFSET (f2)))
|
||
{
|
||
unsigned HOST_WIDE_INT byte_offset1, byte_offset2;
|
||
unsigned HOST_WIDE_INT bit_offset1, bit_offset2;
|
||
bit_offset1 = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (f1));
|
||
byte_offset1 = (TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f1))
|
||
+ bit_offset1 / BITS_PER_UNIT);
|
||
bit_offset2 = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (f2));
|
||
byte_offset2 = (TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f2))
|
||
+ bit_offset2 / BITS_PER_UNIT);
|
||
if (byte_offset1 != byte_offset2)
|
||
return false;
|
||
return bit_offset1 % BITS_PER_UNIT == bit_offset2 % BITS_PER_UNIT;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
/* Return a type the same as TYPE except unsigned or
|
||
signed according to UNSIGNEDP. */
|
||
|
||
static tree
|
||
gimple_signed_or_unsigned_type (bool unsignedp, tree type)
|
||
{
|
||
tree type1;
|
||
int i;
|
||
|
||
type1 = TYPE_MAIN_VARIANT (type);
|
||
if (type1 == signed_char_type_node
|
||
|| type1 == char_type_node
|
||
|| type1 == unsigned_char_type_node)
|
||
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
|
||
if (type1 == integer_type_node || type1 == unsigned_type_node)
|
||
return unsignedp ? unsigned_type_node : integer_type_node;
|
||
if (type1 == short_integer_type_node || type1 == short_unsigned_type_node)
|
||
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
|
||
if (type1 == long_integer_type_node || type1 == long_unsigned_type_node)
|
||
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
||
if (type1 == long_long_integer_type_node
|
||
|| type1 == long_long_unsigned_type_node)
|
||
return unsignedp
|
||
? long_long_unsigned_type_node
|
||
: long_long_integer_type_node;
|
||
|
||
for (i = 0; i < NUM_INT_N_ENTS; i ++)
|
||
if (int_n_enabled_p[i]
|
||
&& (type1 == int_n_trees[i].unsigned_type
|
||
|| type1 == int_n_trees[i].signed_type))
|
||
return unsignedp
|
||
? int_n_trees[i].unsigned_type
|
||
: int_n_trees[i].signed_type;
|
||
|
||
#if HOST_BITS_PER_WIDE_INT >= 64
|
||
if (type1 == intTI_type_node || type1 == unsigned_intTI_type_node)
|
||
return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
|
||
#endif
|
||
if (type1 == intDI_type_node || type1 == unsigned_intDI_type_node)
|
||
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
|
||
if (type1 == intSI_type_node || type1 == unsigned_intSI_type_node)
|
||
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
|
||
if (type1 == intHI_type_node || type1 == unsigned_intHI_type_node)
|
||
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
|
||
if (type1 == intQI_type_node || type1 == unsigned_intQI_type_node)
|
||
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
|
||
|
||
#define GIMPLE_FIXED_TYPES(NAME) \
|
||
if (type1 == short_ ## NAME ## _type_node \
|
||
|| type1 == unsigned_short_ ## NAME ## _type_node) \
|
||
return unsignedp ? unsigned_short_ ## NAME ## _type_node \
|
||
: short_ ## NAME ## _type_node; \
|
||
if (type1 == NAME ## _type_node \
|
||
|| type1 == unsigned_ ## NAME ## _type_node) \
|
||
return unsignedp ? unsigned_ ## NAME ## _type_node \
|
||
: NAME ## _type_node; \
|
||
if (type1 == long_ ## NAME ## _type_node \
|
||
|| type1 == unsigned_long_ ## NAME ## _type_node) \
|
||
return unsignedp ? unsigned_long_ ## NAME ## _type_node \
|
||
: long_ ## NAME ## _type_node; \
|
||
if (type1 == long_long_ ## NAME ## _type_node \
|
||
|| type1 == unsigned_long_long_ ## NAME ## _type_node) \
|
||
return unsignedp ? unsigned_long_long_ ## NAME ## _type_node \
|
||
: long_long_ ## NAME ## _type_node;
|
||
|
||
#define GIMPLE_FIXED_MODE_TYPES(NAME) \
|
||
if (type1 == NAME ## _type_node \
|
||
|| type1 == u ## NAME ## _type_node) \
|
||
return unsignedp ? u ## NAME ## _type_node \
|
||
: NAME ## _type_node;
|
||
|
||
#define GIMPLE_FIXED_TYPES_SAT(NAME) \
|
||
if (type1 == sat_ ## short_ ## NAME ## _type_node \
|
||
|| type1 == sat_ ## unsigned_short_ ## NAME ## _type_node) \
|
||
return unsignedp ? sat_ ## unsigned_short_ ## NAME ## _type_node \
|
||
: sat_ ## short_ ## NAME ## _type_node; \
|
||
if (type1 == sat_ ## NAME ## _type_node \
|
||
|| type1 == sat_ ## unsigned_ ## NAME ## _type_node) \
|
||
return unsignedp ? sat_ ## unsigned_ ## NAME ## _type_node \
|
||
: sat_ ## NAME ## _type_node; \
|
||
if (type1 == sat_ ## long_ ## NAME ## _type_node \
|
||
|| type1 == sat_ ## unsigned_long_ ## NAME ## _type_node) \
|
||
return unsignedp ? sat_ ## unsigned_long_ ## NAME ## _type_node \
|
||
: sat_ ## long_ ## NAME ## _type_node; \
|
||
if (type1 == sat_ ## long_long_ ## NAME ## _type_node \
|
||
|| type1 == sat_ ## unsigned_long_long_ ## NAME ## _type_node) \
|
||
return unsignedp ? sat_ ## unsigned_long_long_ ## NAME ## _type_node \
|
||
: sat_ ## long_long_ ## NAME ## _type_node;
|
||
|
||
#define GIMPLE_FIXED_MODE_TYPES_SAT(NAME) \
|
||
if (type1 == sat_ ## NAME ## _type_node \
|
||
|| type1 == sat_ ## u ## NAME ## _type_node) \
|
||
return unsignedp ? sat_ ## u ## NAME ## _type_node \
|
||
: sat_ ## NAME ## _type_node;
|
||
|
||
GIMPLE_FIXED_TYPES (fract);
|
||
GIMPLE_FIXED_TYPES_SAT (fract);
|
||
GIMPLE_FIXED_TYPES (accum);
|
||
GIMPLE_FIXED_TYPES_SAT (accum);
|
||
|
||
GIMPLE_FIXED_MODE_TYPES (qq);
|
||
GIMPLE_FIXED_MODE_TYPES (hq);
|
||
GIMPLE_FIXED_MODE_TYPES (sq);
|
||
GIMPLE_FIXED_MODE_TYPES (dq);
|
||
GIMPLE_FIXED_MODE_TYPES (tq);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (qq);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (hq);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (sq);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (dq);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (tq);
|
||
GIMPLE_FIXED_MODE_TYPES (ha);
|
||
GIMPLE_FIXED_MODE_TYPES (sa);
|
||
GIMPLE_FIXED_MODE_TYPES (da);
|
||
GIMPLE_FIXED_MODE_TYPES (ta);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (ha);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (sa);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (da);
|
||
GIMPLE_FIXED_MODE_TYPES_SAT (ta);
|
||
|
||
/* For ENUMERAL_TYPEs in C++, must check the mode of the types, not
|
||
the precision; they have precision set to match their range, but
|
||
may use a wider mode to match an ABI. If we change modes, we may
|
||
wind up with bad conversions. For INTEGER_TYPEs in C, must check
|
||
the precision as well, so as to yield correct results for
|
||
bit-field types. C++ does not have these separate bit-field
|
||
types, and producing a signed or unsigned variant of an
|
||
ENUMERAL_TYPE may cause other problems as well. */
|
||
if (!INTEGRAL_TYPE_P (type)
|
||
|| TYPE_UNSIGNED (type) == unsignedp)
|
||
return type;
|
||
|
||
#define TYPE_OK(node) \
|
||
(TYPE_MODE (type) == TYPE_MODE (node) \
|
||
&& TYPE_PRECISION (type) == TYPE_PRECISION (node))
|
||
if (TYPE_OK (signed_char_type_node))
|
||
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
|
||
if (TYPE_OK (integer_type_node))
|
||
return unsignedp ? unsigned_type_node : integer_type_node;
|
||
if (TYPE_OK (short_integer_type_node))
|
||
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
|
||
if (TYPE_OK (long_integer_type_node))
|
||
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
||
if (TYPE_OK (long_long_integer_type_node))
|
||
return (unsignedp
|
||
? long_long_unsigned_type_node
|
||
: long_long_integer_type_node);
|
||
|
||
for (i = 0; i < NUM_INT_N_ENTS; i ++)
|
||
if (int_n_enabled_p[i]
|
||
&& TYPE_MODE (type) == int_n_data[i].m
|
||
&& TYPE_PRECISION (type) == int_n_data[i].bitsize)
|
||
return unsignedp
|
||
? int_n_trees[i].unsigned_type
|
||
: int_n_trees[i].signed_type;
|
||
|
||
#if HOST_BITS_PER_WIDE_INT >= 64
|
||
if (TYPE_OK (intTI_type_node))
|
||
return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
|
||
#endif
|
||
if (TYPE_OK (intDI_type_node))
|
||
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
|
||
if (TYPE_OK (intSI_type_node))
|
||
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
|
||
if (TYPE_OK (intHI_type_node))
|
||
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
|
||
if (TYPE_OK (intQI_type_node))
|
||
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
|
||
|
||
#undef GIMPLE_FIXED_TYPES
|
||
#undef GIMPLE_FIXED_MODE_TYPES
|
||
#undef GIMPLE_FIXED_TYPES_SAT
|
||
#undef GIMPLE_FIXED_MODE_TYPES_SAT
|
||
#undef TYPE_OK
|
||
|
||
return build_nonstandard_integer_type (TYPE_PRECISION (type), unsignedp);
|
||
}
|
||
|
||
|
||
/* Return an unsigned type the same as TYPE in other respects. */
|
||
|
||
tree
|
||
gimple_unsigned_type (tree type)
|
||
{
|
||
return gimple_signed_or_unsigned_type (true, type);
|
||
}
|
||
|
||
|
||
/* Return a signed type the same as TYPE in other respects. */
|
||
|
||
tree
|
||
gimple_signed_type (tree type)
|
||
{
|
||
return gimple_signed_or_unsigned_type (false, type);
|
||
}
|
||
|
||
|
||
/* Return the typed-based alias set for T, which may be an expression
|
||
or a type. Return -1 if we don't do anything special. */
|
||
|
||
alias_set_type
|
||
gimple_get_alias_set (tree t)
|
||
{
|
||
/* That's all the expressions we handle specially. */
|
||
if (!TYPE_P (t))
|
||
return -1;
|
||
|
||
/* For convenience, follow the C standard when dealing with
|
||
character types. Any object may be accessed via an lvalue that
|
||
has character type. */
|
||
if (t == char_type_node
|
||
|| t == signed_char_type_node
|
||
|| t == unsigned_char_type_node)
|
||
return 0;
|
||
|
||
/* Allow aliasing between signed and unsigned variants of the same
|
||
type. We treat the signed variant as canonical. */
|
||
if (TREE_CODE (t) == INTEGER_TYPE && TYPE_UNSIGNED (t))
|
||
{
|
||
tree t1 = gimple_signed_type (t);
|
||
|
||
/* t1 == t can happen for boolean nodes which are always unsigned. */
|
||
if (t1 != t)
|
||
return get_alias_set (t1);
|
||
}
|
||
|
||
/* Allow aliasing between enumeral types and the underlying
|
||
integer type. This is required for C since those are
|
||
compatible types. */
|
||
else if (TREE_CODE (t) == ENUMERAL_TYPE)
|
||
{
|
||
tree t1 = lang_hooks.types.type_for_size (tree_to_uhwi (TYPE_SIZE (t)),
|
||
false /* short-cut above */);
|
||
return get_alias_set (t1);
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
/* Helper for gimple_ior_addresses_taken_1. */
|
||
|
||
static bool
|
||
gimple_ior_addresses_taken_1 (gimple *, tree addr, tree, void *data)
|
||
{
|
||
bitmap addresses_taken = (bitmap)data;
|
||
addr = get_base_address (addr);
|
||
if (addr
|
||
&& DECL_P (addr))
|
||
{
|
||
bitmap_set_bit (addresses_taken, DECL_UID (addr));
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Set the bit for the uid of all decls that have their address taken
|
||
in STMT in the ADDRESSES_TAKEN bitmap. Returns true if there
|
||
were any in this stmt. */
|
||
|
||
bool
|
||
gimple_ior_addresses_taken (bitmap addresses_taken, gimple *stmt)
|
||
{
|
||
return walk_stmt_load_store_addr_ops (stmt, addresses_taken, NULL, NULL,
|
||
gimple_ior_addresses_taken_1);
|
||
}
|
||
|
||
|
||
/* Return true when STMTs arguments and return value match those of FNDECL,
|
||
a decl of a builtin function. */
|
||
|
||
bool
|
||
gimple_builtin_call_types_compatible_p (const gimple *stmt, tree fndecl)
|
||
{
|
||
gcc_checking_assert (DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN);
|
||
|
||
tree ret = gimple_call_lhs (stmt);
|
||
if (ret
|
||
&& !useless_type_conversion_p (TREE_TYPE (ret),
|
||
TREE_TYPE (TREE_TYPE (fndecl))))
|
||
return false;
|
||
|
||
tree targs = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
|
||
unsigned nargs = gimple_call_num_args (stmt);
|
||
for (unsigned i = 0; i < nargs; ++i)
|
||
{
|
||
/* Variadic args follow. */
|
||
if (!targs)
|
||
return true;
|
||
tree arg = gimple_call_arg (stmt, i);
|
||
tree type = TREE_VALUE (targs);
|
||
if (!useless_type_conversion_p (type, TREE_TYPE (arg))
|
||
/* char/short integral arguments are promoted to int
|
||
by several frontends if targetm.calls.promote_prototypes
|
||
is true. Allow such promotion too. */
|
||
&& !(INTEGRAL_TYPE_P (type)
|
||
&& TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)
|
||
&& targetm.calls.promote_prototypes (TREE_TYPE (fndecl))
|
||
&& useless_type_conversion_p (integer_type_node,
|
||
TREE_TYPE (arg))))
|
||
return false;
|
||
targs = TREE_CHAIN (targs);
|
||
}
|
||
if (targs && !VOID_TYPE_P (TREE_VALUE (targs)))
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
/* Return true when STMT is operator delete call. */
|
||
|
||
bool
|
||
gimple_call_operator_delete_p (const gcall *stmt)
|
||
{
|
||
tree fndecl;
|
||
|
||
if ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE)
|
||
return DECL_IS_OPERATOR_DELETE_P (fndecl);
|
||
return false;
|
||
}
|
||
|
||
/* Return true when STMT is builtins call. */
|
||
|
||
bool
|
||
gimple_call_builtin_p (const gimple *stmt)
|
||
{
|
||
tree fndecl;
|
||
if (is_gimple_call (stmt)
|
||
&& (fndecl = gimple_call_fndecl (stmt)) != NULL_TREE
|
||
&& DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN)
|
||
return gimple_builtin_call_types_compatible_p (stmt, fndecl);
|
||
return false;
|
||
}
|
||
|
||
/* Return true when STMT is builtins call to CLASS. */
|
||
|
||
bool
|
||
gimple_call_builtin_p (const gimple *stmt, enum built_in_class klass)
|
||
{
|
||
tree fndecl;
|
||
if (is_gimple_call (stmt)
|
||
&& (fndecl = gimple_call_fndecl (stmt)) != NULL_TREE
|
||
&& DECL_BUILT_IN_CLASS (fndecl) == klass)
|
||
return gimple_builtin_call_types_compatible_p (stmt, fndecl);
|
||
return false;
|
||
}
|
||
|
||
/* Return true when STMT is builtins call to CODE of CLASS. */
|
||
|
||
bool
|
||
gimple_call_builtin_p (const gimple *stmt, enum built_in_function code)
|
||
{
|
||
tree fndecl;
|
||
if (is_gimple_call (stmt)
|
||
&& (fndecl = gimple_call_fndecl (stmt)) != NULL_TREE
|
||
&& fndecl_built_in_p (fndecl, code))
|
||
return gimple_builtin_call_types_compatible_p (stmt, fndecl);
|
||
return false;
|
||
}
|
||
|
||
/* If CALL is a call to a combined_fn (i.e. an internal function or
|
||
a normal built-in function), return its code, otherwise return
|
||
CFN_LAST. */
|
||
|
||
combined_fn
|
||
gimple_call_combined_fn (const gimple *stmt)
|
||
{
|
||
if (const gcall *call = dyn_cast <const gcall *> (stmt))
|
||
{
|
||
if (gimple_call_internal_p (call))
|
||
return as_combined_fn (gimple_call_internal_fn (call));
|
||
|
||
tree fndecl = gimple_call_fndecl (stmt);
|
||
if (fndecl
|
||
&& fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
|
||
&& gimple_builtin_call_types_compatible_p (stmt, fndecl))
|
||
return as_combined_fn (DECL_FUNCTION_CODE (fndecl));
|
||
}
|
||
return CFN_LAST;
|
||
}
|
||
|
||
/* Return true if STMT clobbers memory. STMT is required to be a
|
||
GIMPLE_ASM. */
|
||
|
||
bool
|
||
gimple_asm_clobbers_memory_p (const gasm *stmt)
|
||
{
|
||
unsigned i;
|
||
|
||
for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
|
||
{
|
||
tree op = gimple_asm_clobber_op (stmt, i);
|
||
if (strcmp (TREE_STRING_POINTER (TREE_VALUE (op)), "memory") == 0)
|
||
return true;
|
||
}
|
||
|
||
/* Non-empty basic ASM implicitly clobbers memory. */
|
||
if (gimple_asm_input_p (stmt) && strlen (gimple_asm_string (stmt)) != 0)
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Dump bitmap SET (assumed to contain VAR_DECLs) to FILE. */
|
||
|
||
void
|
||
dump_decl_set (FILE *file, bitmap set)
|
||
{
|
||
if (set)
|
||
{
|
||
bitmap_iterator bi;
|
||
unsigned i;
|
||
|
||
fprintf (file, "{ ");
|
||
|
||
EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
|
||
{
|
||
fprintf (file, "D.%u", i);
|
||
fprintf (file, " ");
|
||
}
|
||
|
||
fprintf (file, "}");
|
||
}
|
||
else
|
||
fprintf (file, "NIL");
|
||
}
|
||
|
||
/* Return true when CALL is a call stmt that definitely doesn't
|
||
free any memory or makes it unavailable otherwise. */
|
||
bool
|
||
nonfreeing_call_p (gimple *call)
|
||
{
|
||
if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
|
||
&& gimple_call_flags (call) & ECF_LEAF)
|
||
switch (DECL_FUNCTION_CODE (gimple_call_fndecl (call)))
|
||
{
|
||
/* Just in case these become ECF_LEAF in the future. */
|
||
case BUILT_IN_FREE:
|
||
case BUILT_IN_TM_FREE:
|
||
case BUILT_IN_REALLOC:
|
||
case BUILT_IN_STACK_RESTORE:
|
||
return false;
|
||
default:
|
||
return true;
|
||
}
|
||
else if (gimple_call_internal_p (call))
|
||
switch (gimple_call_internal_fn (call))
|
||
{
|
||
case IFN_ABNORMAL_DISPATCHER:
|
||
return true;
|
||
case IFN_ASAN_MARK:
|
||
return tree_to_uhwi (gimple_call_arg (call, 0)) == ASAN_MARK_UNPOISON;
|
||
default:
|
||
if (gimple_call_flags (call) & ECF_LEAF)
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
tree fndecl = gimple_call_fndecl (call);
|
||
if (!fndecl)
|
||
return false;
|
||
struct cgraph_node *n = cgraph_node::get (fndecl);
|
||
if (!n)
|
||
return false;
|
||
enum availability availability;
|
||
n = n->function_symbol (&availability);
|
||
if (!n || availability <= AVAIL_INTERPOSABLE)
|
||
return false;
|
||
return n->nonfreeing_fn;
|
||
}
|
||
|
||
/* Return true when CALL is a call stmt that definitely need not
|
||
be considered to be a memory barrier. */
|
||
bool
|
||
nonbarrier_call_p (gimple *call)
|
||
{
|
||
if (gimple_call_flags (call) & (ECF_PURE | ECF_CONST))
|
||
return true;
|
||
/* Should extend this to have a nonbarrier_fn flag, just as above in
|
||
the nonfreeing case. */
|
||
return false;
|
||
}
|
||
|
||
/* Callback for walk_stmt_load_store_ops.
|
||
|
||
Return TRUE if OP will dereference the tree stored in DATA, FALSE
|
||
otherwise.
|
||
|
||
This routine only makes a superficial check for a dereference. Thus
|
||
it must only be used if it is safe to return a false negative. */
|
||
static bool
|
||
check_loadstore (gimple *, tree op, tree, void *data)
|
||
{
|
||
if (TREE_CODE (op) == MEM_REF || TREE_CODE (op) == TARGET_MEM_REF)
|
||
{
|
||
/* Some address spaces may legitimately dereference zero. */
|
||
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (op));
|
||
if (targetm.addr_space.zero_address_valid (as))
|
||
return false;
|
||
|
||
return operand_equal_p (TREE_OPERAND (op, 0), (tree)data, 0);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
/* Return true if OP can be inferred to be non-NULL after STMT executes,
|
||
either by using a pointer dereference or attributes. */
|
||
bool
|
||
infer_nonnull_range (gimple *stmt, tree op)
|
||
{
|
||
return infer_nonnull_range_by_dereference (stmt, op)
|
||
|| infer_nonnull_range_by_attribute (stmt, op);
|
||
}
|
||
|
||
/* Return true if OP can be inferred to be non-NULL after STMT
|
||
executes by using a pointer dereference. */
|
||
bool
|
||
infer_nonnull_range_by_dereference (gimple *stmt, tree op)
|
||
{
|
||
/* We can only assume that a pointer dereference will yield
|
||
non-NULL if -fdelete-null-pointer-checks is enabled. */
|
||
if (!flag_delete_null_pointer_checks
|
||
|| !POINTER_TYPE_P (TREE_TYPE (op))
|
||
|| gimple_code (stmt) == GIMPLE_ASM)
|
||
return false;
|
||
|
||
if (walk_stmt_load_store_ops (stmt, (void *)op,
|
||
check_loadstore, check_loadstore))
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Return true if OP can be inferred to be a non-NULL after STMT
|
||
executes by using attributes. */
|
||
bool
|
||
infer_nonnull_range_by_attribute (gimple *stmt, tree op)
|
||
{
|
||
/* We can only assume that a pointer dereference will yield
|
||
non-NULL if -fdelete-null-pointer-checks is enabled. */
|
||
if (!flag_delete_null_pointer_checks
|
||
|| !POINTER_TYPE_P (TREE_TYPE (op))
|
||
|| gimple_code (stmt) == GIMPLE_ASM)
|
||
return false;
|
||
|
||
if (is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
|
||
{
|
||
tree fntype = gimple_call_fntype (stmt);
|
||
tree attrs = TYPE_ATTRIBUTES (fntype);
|
||
for (; attrs; attrs = TREE_CHAIN (attrs))
|
||
{
|
||
attrs = lookup_attribute ("nonnull", attrs);
|
||
|
||
/* If "nonnull" wasn't specified, we know nothing about
|
||
the argument. */
|
||
if (attrs == NULL_TREE)
|
||
return false;
|
||
|
||
/* If "nonnull" applies to all the arguments, then ARG
|
||
is non-null if it's in the argument list. */
|
||
if (TREE_VALUE (attrs) == NULL_TREE)
|
||
{
|
||
for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
|
||
{
|
||
if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (stmt, i)))
|
||
&& operand_equal_p (op, gimple_call_arg (stmt, i), 0))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Now see if op appears in the nonnull list. */
|
||
for (tree t = TREE_VALUE (attrs); t; t = TREE_CHAIN (t))
|
||
{
|
||
unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (t)) - 1;
|
||
if (idx < gimple_call_num_args (stmt))
|
||
{
|
||
tree arg = gimple_call_arg (stmt, idx);
|
||
if (operand_equal_p (op, arg, 0))
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* If this function is marked as returning non-null, then we can
|
||
infer OP is non-null if it is used in the return statement. */
|
||
if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
|
||
if (gimple_return_retval (return_stmt)
|
||
&& operand_equal_p (gimple_return_retval (return_stmt), op, 0)
|
||
&& lookup_attribute ("returns_nonnull",
|
||
TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Compare two case labels. Because the front end should already have
|
||
made sure that case ranges do not overlap, it is enough to only compare
|
||
the CASE_LOW values of each case label. */
|
||
|
||
static int
|
||
compare_case_labels (const void *p1, const void *p2)
|
||
{
|
||
const_tree const case1 = *(const_tree const*)p1;
|
||
const_tree const case2 = *(const_tree const*)p2;
|
||
|
||
/* The 'default' case label always goes first. */
|
||
if (!CASE_LOW (case1))
|
||
return -1;
|
||
else if (!CASE_LOW (case2))
|
||
return 1;
|
||
else
|
||
return tree_int_cst_compare (CASE_LOW (case1), CASE_LOW (case2));
|
||
}
|
||
|
||
/* Sort the case labels in LABEL_VEC in place in ascending order. */
|
||
|
||
void
|
||
sort_case_labels (vec<tree> label_vec)
|
||
{
|
||
label_vec.qsort (compare_case_labels);
|
||
}
|
||
|
||
/* Prepare a vector of case labels to be used in a GIMPLE_SWITCH statement.
|
||
|
||
LABELS is a vector that contains all case labels to look at.
|
||
|
||
INDEX_TYPE is the type of the switch index expression. Case labels
|
||
in LABELS are discarded if their values are not in the value range
|
||
covered by INDEX_TYPE. The remaining case label values are folded
|
||
to INDEX_TYPE.
|
||
|
||
If a default case exists in LABELS, it is removed from LABELS and
|
||
returned in DEFAULT_CASEP. If no default case exists, but the
|
||
case labels already cover the whole range of INDEX_TYPE, a default
|
||
case is returned pointing to one of the existing case labels.
|
||
Otherwise DEFAULT_CASEP is set to NULL_TREE.
|
||
|
||
DEFAULT_CASEP may be NULL, in which case the above comment doesn't
|
||
apply and no action is taken regardless of whether a default case is
|
||
found or not. */
|
||
|
||
void
|
||
preprocess_case_label_vec_for_gimple (vec<tree> labels,
|
||
tree index_type,
|
||
tree *default_casep)
|
||
{
|
||
tree min_value, max_value;
|
||
tree default_case = NULL_TREE;
|
||
size_t i, len;
|
||
|
||
i = 0;
|
||
min_value = TYPE_MIN_VALUE (index_type);
|
||
max_value = TYPE_MAX_VALUE (index_type);
|
||
while (i < labels.length ())
|
||
{
|
||
tree elt = labels[i];
|
||
tree low = CASE_LOW (elt);
|
||
tree high = CASE_HIGH (elt);
|
||
bool remove_element = FALSE;
|
||
|
||
if (low)
|
||
{
|
||
gcc_checking_assert (TREE_CODE (low) == INTEGER_CST);
|
||
gcc_checking_assert (!high || TREE_CODE (high) == INTEGER_CST);
|
||
|
||
/* This is a non-default case label, i.e. it has a value.
|
||
|
||
See if the case label is reachable within the range of
|
||
the index type. Remove out-of-range case values. Turn
|
||
case ranges into a canonical form (high > low strictly)
|
||
and convert the case label values to the index type.
|
||
|
||
NB: The type of gimple_switch_index() may be the promoted
|
||
type, but the case labels retain the original type. */
|
||
|
||
if (high)
|
||
{
|
||
/* This is a case range. Discard empty ranges.
|
||
If the bounds or the range are equal, turn this
|
||
into a simple (one-value) case. */
|
||
int cmp = tree_int_cst_compare (high, low);
|
||
if (cmp < 0)
|
||
remove_element = TRUE;
|
||
else if (cmp == 0)
|
||
high = NULL_TREE;
|
||
}
|
||
|
||
if (! high)
|
||
{
|
||
/* If the simple case value is unreachable, ignore it. */
|
||
if ((TREE_CODE (min_value) == INTEGER_CST
|
||
&& tree_int_cst_compare (low, min_value) < 0)
|
||
|| (TREE_CODE (max_value) == INTEGER_CST
|
||
&& tree_int_cst_compare (low, max_value) > 0))
|
||
remove_element = TRUE;
|
||
else
|
||
low = fold_convert (index_type, low);
|
||
}
|
||
else
|
||
{
|
||
/* If the entire case range is unreachable, ignore it. */
|
||
if ((TREE_CODE (min_value) == INTEGER_CST
|
||
&& tree_int_cst_compare (high, min_value) < 0)
|
||
|| (TREE_CODE (max_value) == INTEGER_CST
|
||
&& tree_int_cst_compare (low, max_value) > 0))
|
||
remove_element = TRUE;
|
||
else
|
||
{
|
||
/* If the lower bound is less than the index type's
|
||
minimum value, truncate the range bounds. */
|
||
if (TREE_CODE (min_value) == INTEGER_CST
|
||
&& tree_int_cst_compare (low, min_value) < 0)
|
||
low = min_value;
|
||
low = fold_convert (index_type, low);
|
||
|
||
/* If the upper bound is greater than the index type's
|
||
maximum value, truncate the range bounds. */
|
||
if (TREE_CODE (max_value) == INTEGER_CST
|
||
&& tree_int_cst_compare (high, max_value) > 0)
|
||
high = max_value;
|
||
high = fold_convert (index_type, high);
|
||
|
||
/* We may have folded a case range to a one-value case. */
|
||
if (tree_int_cst_equal (low, high))
|
||
high = NULL_TREE;
|
||
}
|
||
}
|
||
|
||
CASE_LOW (elt) = low;
|
||
CASE_HIGH (elt) = high;
|
||
}
|
||
else
|
||
{
|
||
gcc_assert (!default_case);
|
||
default_case = elt;
|
||
/* The default case must be passed separately to the
|
||
gimple_build_switch routine. But if DEFAULT_CASEP
|
||
is NULL, we do not remove the default case (it would
|
||
be completely lost). */
|
||
if (default_casep)
|
||
remove_element = TRUE;
|
||
}
|
||
|
||
if (remove_element)
|
||
labels.ordered_remove (i);
|
||
else
|
||
i++;
|
||
}
|
||
len = i;
|
||
|
||
if (!labels.is_empty ())
|
||
sort_case_labels (labels);
|
||
|
||
if (default_casep && !default_case)
|
||
{
|
||
/* If the switch has no default label, add one, so that we jump
|
||
around the switch body. If the labels already cover the whole
|
||
range of the switch index_type, add the default label pointing
|
||
to one of the existing labels. */
|
||
if (len
|
||
&& TYPE_MIN_VALUE (index_type)
|
||
&& TYPE_MAX_VALUE (index_type)
|
||
&& tree_int_cst_equal (CASE_LOW (labels[0]),
|
||
TYPE_MIN_VALUE (index_type)))
|
||
{
|
||
tree low, high = CASE_HIGH (labels[len - 1]);
|
||
if (!high)
|
||
high = CASE_LOW (labels[len - 1]);
|
||
if (tree_int_cst_equal (high, TYPE_MAX_VALUE (index_type)))
|
||
{
|
||
tree widest_label = labels[0];
|
||
for (i = 1; i < len; i++)
|
||
{
|
||
high = CASE_LOW (labels[i]);
|
||
low = CASE_HIGH (labels[i - 1]);
|
||
if (!low)
|
||
low = CASE_LOW (labels[i - 1]);
|
||
|
||
if (CASE_HIGH (labels[i]) != NULL_TREE
|
||
&& (CASE_HIGH (widest_label) == NULL_TREE
|
||
|| (wi::gtu_p
|
||
(wi::to_wide (CASE_HIGH (labels[i]))
|
||
- wi::to_wide (CASE_LOW (labels[i])),
|
||
wi::to_wide (CASE_HIGH (widest_label))
|
||
- wi::to_wide (CASE_LOW (widest_label))))))
|
||
widest_label = labels[i];
|
||
|
||
if (wi::to_wide (low) + 1 != wi::to_wide (high))
|
||
break;
|
||
}
|
||
if (i == len)
|
||
{
|
||
/* Designate the label with the widest range to be the
|
||
default label. */
|
||
tree label = CASE_LABEL (widest_label);
|
||
default_case = build_case_label (NULL_TREE, NULL_TREE,
|
||
label);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (default_casep)
|
||
*default_casep = default_case;
|
||
}
|
||
|
||
/* Set the location of all statements in SEQ to LOC. */
|
||
|
||
void
|
||
gimple_seq_set_location (gimple_seq seq, location_t loc)
|
||
{
|
||
for (gimple_stmt_iterator i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i))
|
||
gimple_set_location (gsi_stmt (i), loc);
|
||
}
|
||
|
||
/* Release SSA_NAMEs in SEQ as well as the GIMPLE statements. */
|
||
|
||
void
|
||
gimple_seq_discard (gimple_seq seq)
|
||
{
|
||
gimple_stmt_iterator gsi;
|
||
|
||
for (gsi = gsi_start (seq); !gsi_end_p (gsi); )
|
||
{
|
||
gimple *stmt = gsi_stmt (gsi);
|
||
gsi_remove (&gsi, true);
|
||
release_defs (stmt);
|
||
ggc_free (stmt);
|
||
}
|
||
}
|
||
|
||
/* See if STMT now calls function that takes no parameters and if so, drop
|
||
call arguments. This is used when devirtualization machinery redirects
|
||
to __builtin_unreachable or __cxa_pure_virtual. */
|
||
|
||
void
|
||
maybe_remove_unused_call_args (struct function *fn, gimple *stmt)
|
||
{
|
||
tree decl = gimple_call_fndecl (stmt);
|
||
if (TYPE_ARG_TYPES (TREE_TYPE (decl))
|
||
&& TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))) == void_type_node
|
||
&& gimple_call_num_args (stmt))
|
||
{
|
||
gimple_set_num_ops (stmt, 3);
|
||
update_stmt_fn (fn, stmt);
|
||
}
|
||
}
|
||
|
||
/* Return false if STMT will likely expand to real function call. */
|
||
|
||
bool
|
||
gimple_inexpensive_call_p (gcall *stmt)
|
||
{
|
||
if (gimple_call_internal_p (stmt))
|
||
return true;
|
||
tree decl = gimple_call_fndecl (stmt);
|
||
if (decl && is_inexpensive_builtin (decl))
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
#if CHECKING_P
|
||
|
||
namespace selftest {
|
||
|
||
/* Selftests for core gimple structures. */
|
||
|
||
/* Verify that STMT is pretty-printed as EXPECTED.
|
||
Helper function for selftests. */
|
||
|
||
static void
|
||
verify_gimple_pp (const char *expected, gimple *stmt)
|
||
{
|
||
pretty_printer pp;
|
||
pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, TDF_NONE /* flags */);
|
||
ASSERT_STREQ (expected, pp_formatted_text (&pp));
|
||
}
|
||
|
||
/* Build a GIMPLE_ASSIGN equivalent to
|
||
tmp = 5;
|
||
and verify various properties of it. */
|
||
|
||
static void
|
||
test_assign_single ()
|
||
{
|
||
tree type = integer_type_node;
|
||
tree lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL,
|
||
get_identifier ("tmp"),
|
||
type);
|
||
tree rhs = build_int_cst (type, 5);
|
||
gassign *stmt = gimple_build_assign (lhs, rhs);
|
||
verify_gimple_pp ("tmp = 5;", stmt);
|
||
|
||
ASSERT_TRUE (is_gimple_assign (stmt));
|
||
ASSERT_EQ (lhs, gimple_assign_lhs (stmt));
|
||
ASSERT_EQ (lhs, gimple_get_lhs (stmt));
|
||
ASSERT_EQ (rhs, gimple_assign_rhs1 (stmt));
|
||
ASSERT_EQ (NULL, gimple_assign_rhs2 (stmt));
|
||
ASSERT_EQ (NULL, gimple_assign_rhs3 (stmt));
|
||
ASSERT_TRUE (gimple_assign_single_p (stmt));
|
||
ASSERT_EQ (INTEGER_CST, gimple_assign_rhs_code (stmt));
|
||
}
|
||
|
||
/* Build a GIMPLE_ASSIGN equivalent to
|
||
tmp = a * b;
|
||
and verify various properties of it. */
|
||
|
||
static void
|
||
test_assign_binop ()
|
||
{
|
||
tree type = integer_type_node;
|
||
tree lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL,
|
||
get_identifier ("tmp"),
|
||
type);
|
||
tree a = build_decl (UNKNOWN_LOCATION, VAR_DECL,
|
||
get_identifier ("a"),
|
||
type);
|
||
tree b = build_decl (UNKNOWN_LOCATION, VAR_DECL,
|
||
get_identifier ("b"),
|
||
type);
|
||
gassign *stmt = gimple_build_assign (lhs, MULT_EXPR, a, b);
|
||
verify_gimple_pp ("tmp = a * b;", stmt);
|
||
|
||
ASSERT_TRUE (is_gimple_assign (stmt));
|
||
ASSERT_EQ (lhs, gimple_assign_lhs (stmt));
|
||
ASSERT_EQ (lhs, gimple_get_lhs (stmt));
|
||
ASSERT_EQ (a, gimple_assign_rhs1 (stmt));
|
||
ASSERT_EQ (b, gimple_assign_rhs2 (stmt));
|
||
ASSERT_EQ (NULL, gimple_assign_rhs3 (stmt));
|
||
ASSERT_FALSE (gimple_assign_single_p (stmt));
|
||
ASSERT_EQ (MULT_EXPR, gimple_assign_rhs_code (stmt));
|
||
}
|
||
|
||
/* Build a GIMPLE_NOP and verify various properties of it. */
|
||
|
||
static void
|
||
test_nop_stmt ()
|
||
{
|
||
gimple *stmt = gimple_build_nop ();
|
||
verify_gimple_pp ("GIMPLE_NOP", stmt);
|
||
ASSERT_EQ (GIMPLE_NOP, gimple_code (stmt));
|
||
ASSERT_EQ (NULL, gimple_get_lhs (stmt));
|
||
ASSERT_FALSE (gimple_assign_single_p (stmt));
|
||
}
|
||
|
||
/* Build a GIMPLE_RETURN equivalent to
|
||
return 7;
|
||
and verify various properties of it. */
|
||
|
||
static void
|
||
test_return_stmt ()
|
||
{
|
||
tree type = integer_type_node;
|
||
tree val = build_int_cst (type, 7);
|
||
greturn *stmt = gimple_build_return (val);
|
||
verify_gimple_pp ("return 7;", stmt);
|
||
|
||
ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt));
|
||
ASSERT_EQ (NULL, gimple_get_lhs (stmt));
|
||
ASSERT_EQ (val, gimple_return_retval (stmt));
|
||
ASSERT_FALSE (gimple_assign_single_p (stmt));
|
||
}
|
||
|
||
/* Build a GIMPLE_RETURN equivalent to
|
||
return;
|
||
and verify various properties of it. */
|
||
|
||
static void
|
||
test_return_without_value ()
|
||
{
|
||
greturn *stmt = gimple_build_return (NULL);
|
||
verify_gimple_pp ("return;", stmt);
|
||
|
||
ASSERT_EQ (GIMPLE_RETURN, gimple_code (stmt));
|
||
ASSERT_EQ (NULL, gimple_get_lhs (stmt));
|
||
ASSERT_EQ (NULL, gimple_return_retval (stmt));
|
||
ASSERT_FALSE (gimple_assign_single_p (stmt));
|
||
}
|
||
|
||
/* Run all of the selftests within this file. */
|
||
|
||
void
|
||
gimple_c_tests ()
|
||
{
|
||
test_assign_single ();
|
||
test_assign_binop ();
|
||
test_nop_stmt ();
|
||
test_return_stmt ();
|
||
test_return_without_value ();
|
||
}
|
||
|
||
} // namespace selftest
|
||
|
||
|
||
#endif /* CHECKING_P */
|