365 lines
14 KiB
C++
365 lines
14 KiB
C++
/* Expand builtin functions.
|
|
Copyright (C) 1988-2021 Free Software Foundation, Inc.
|
|
|
|
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/>. */
|
|
|
|
#ifndef GCC_BUILTINS_H
|
|
#define GCC_BUILTINS_H
|
|
|
|
#include <mpc.h>
|
|
|
|
/* Target-dependent globals. */
|
|
struct target_builtins {
|
|
/* For each register that may be used for calling a function, this
|
|
gives a mode used to copy the register's value. VOIDmode indicates
|
|
the register is not used for calling a function. If the machine
|
|
has register windows, this gives only the outbound registers.
|
|
INCOMING_REGNO gives the corresponding inbound register. */
|
|
fixed_size_mode_pod x_apply_args_mode[FIRST_PSEUDO_REGISTER];
|
|
|
|
/* For each register that may be used for returning values, this gives
|
|
a mode used to copy the register's value. VOIDmode indicates the
|
|
register is not used for returning values. If the machine has
|
|
register windows, this gives only the outbound registers.
|
|
INCOMING_REGNO gives the corresponding inbound register. */
|
|
fixed_size_mode_pod x_apply_result_mode[FIRST_PSEUDO_REGISTER];
|
|
};
|
|
|
|
extern struct target_builtins default_target_builtins;
|
|
#if SWITCHABLE_TARGET
|
|
extern struct target_builtins *this_target_builtins;
|
|
#else
|
|
#define this_target_builtins (&default_target_builtins)
|
|
#endif
|
|
|
|
/* Non-zero if __builtin_constant_p should be folded right away. */
|
|
extern bool force_folding_builtin_constant_p;
|
|
|
|
extern bool called_as_built_in (tree);
|
|
extern bool get_object_alignment_1 (tree, unsigned int *,
|
|
unsigned HOST_WIDE_INT *);
|
|
extern unsigned int get_object_alignment (tree);
|
|
extern bool get_pointer_alignment_1 (tree, unsigned int *,
|
|
unsigned HOST_WIDE_INT *);
|
|
extern unsigned int get_pointer_alignment (tree);
|
|
extern unsigned string_length (const void*, unsigned, unsigned);
|
|
|
|
struct c_strlen_data
|
|
{
|
|
/* [MINLEN, MAXBOUND, MAXLEN] is a range describing the length of
|
|
one or more strings of possibly unknown length. For a single
|
|
string of known length the range is a constant where
|
|
MINLEN == MAXBOUND == MAXLEN holds.
|
|
For other strings, MINLEN is the length of the shortest known
|
|
string. MAXBOUND is the length of a string that could be stored
|
|
in the largest array referenced by the expression. MAXLEN is
|
|
the length of the longest sequence of non-zero bytes
|
|
in an object referenced by the expression. For such strings,
|
|
MINLEN <= MAXBOUND <= MAXLEN holds. For example, given:
|
|
struct A { char a[7], b[]; };
|
|
extern struct A *p;
|
|
n = strlen (p->a);
|
|
the computed range will be [0, 6, ALL_ONES].
|
|
However, for a conditional expression involving a string
|
|
of known length and an array of unknown bound such as
|
|
n = strlen (i ? p->b : "123");
|
|
the range will be [3, 3, ALL_ONES].
|
|
MINLEN != 0 && MAXLEN == ALL_ONES indicates that MINLEN is
|
|
the length of the shortest known string and implies that
|
|
the shortest possible string referenced by the expression may
|
|
actually be the empty string. This distinction is useful for
|
|
diagnostics. get_range_strlen() return value distinguishes
|
|
between these two cases.
|
|
As the tighter (and more optimistic) bound, MAXBOUND is suitable
|
|
for diagnostics but not for optimization.
|
|
As the more conservative bound, MAXLEN is intended to be used
|
|
for optimization. */
|
|
tree minlen;
|
|
tree maxlen;
|
|
tree maxbound;
|
|
/* When non-null, DECL refers to the declaration known to store
|
|
an unterminated constant character array, as in:
|
|
const char s[] = { 'a', 'b', 'c' };
|
|
It is used to diagnose uses of such arrays in functions such as
|
|
strlen() that expect a nul-terminated string as an argument. */
|
|
tree decl;
|
|
/* Non-constant offset from the beginning of a string not accounted
|
|
for in the length range. Used to improve diagnostics. */
|
|
tree off;
|
|
};
|
|
|
|
extern tree c_strlen (tree, int, c_strlen_data * = NULL, unsigned = 1);
|
|
extern rtx c_readstr (const char *, scalar_int_mode, bool = true);
|
|
extern void expand_builtin_setjmp_setup (rtx, rtx);
|
|
extern void expand_builtin_setjmp_receiver (rtx);
|
|
extern void expand_builtin_update_setjmp_buf (rtx);
|
|
extern tree mathfn_built_in (tree, enum built_in_function fn);
|
|
extern tree mathfn_built_in (tree, combined_fn);
|
|
extern tree mathfn_built_in_type (combined_fn);
|
|
extern rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, scalar_int_mode);
|
|
extern rtx builtin_memset_read_str (void *, HOST_WIDE_INT, scalar_int_mode);
|
|
extern rtx expand_builtin_saveregs (void);
|
|
extern tree std_build_builtin_va_list (void);
|
|
extern tree std_fn_abi_va_list (tree);
|
|
extern tree std_canonical_va_list_type (tree);
|
|
extern void std_expand_builtin_va_start (tree, rtx);
|
|
extern void expand_builtin_trap (void);
|
|
extern void expand_ifn_atomic_bit_test_and (gcall *);
|
|
extern void expand_ifn_atomic_compare_exchange (gcall *);
|
|
extern rtx expand_builtin (tree, rtx, rtx, machine_mode, int);
|
|
extern enum built_in_function builtin_mathfn_code (const_tree);
|
|
extern tree fold_builtin_expect (location_t, tree, tree, tree, tree);
|
|
extern bool avoid_folding_inline_builtin (tree);
|
|
extern tree fold_call_expr (location_t, tree, bool);
|
|
extern tree fold_builtin_call_array (location_t, tree, tree, int, tree *);
|
|
extern bool validate_gimple_arglist (const gcall *, ...);
|
|
extern rtx default_expand_builtin (tree, rtx, rtx, machine_mode, int);
|
|
extern void maybe_emit_call_builtin___clear_cache (rtx, rtx);
|
|
extern bool fold_builtin_next_arg (tree, bool);
|
|
extern tree do_mpc_arg2 (tree, tree, tree, int, int (*)(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t));
|
|
extern tree fold_call_stmt (gcall *, bool);
|
|
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
|
|
extern bool is_simple_builtin (tree);
|
|
extern bool is_inexpensive_builtin (tree);
|
|
extern bool readonly_data_expr (tree exp);
|
|
extern bool init_target_chars (void);
|
|
extern unsigned HOST_WIDE_INT target_newline;
|
|
extern unsigned HOST_WIDE_INT target_percent;
|
|
extern char target_percent_s[3];
|
|
extern char target_percent_c[3];
|
|
extern char target_percent_s_newline[4];
|
|
extern bool target_char_cst_p (tree t, char *p);
|
|
|
|
extern internal_fn associated_internal_fn (tree);
|
|
extern internal_fn replacement_internal_fn (gcall *);
|
|
|
|
extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
|
|
extern void warn_string_no_nul (location_t, tree, const char *, tree,
|
|
tree, tree = NULL_TREE, bool = false,
|
|
const wide_int[2] = NULL);
|
|
extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
|
|
extern bool builtin_with_linkage_p (tree);
|
|
|
|
/* Describes recursion limits used by functions that follow use-def
|
|
chains of SSA_NAMEs. */
|
|
|
|
class ssa_name_limit_t
|
|
{
|
|
bitmap visited; /* Bitmap of visited SSA_NAMEs. */
|
|
unsigned ssa_def_max; /* Longest chain of SSA_NAMEs to follow. */
|
|
|
|
/* Not copyable or assignable. */
|
|
DISABLE_COPY_AND_ASSIGN (ssa_name_limit_t);
|
|
|
|
public:
|
|
|
|
ssa_name_limit_t ()
|
|
: visited (),
|
|
ssa_def_max (param_ssa_name_def_chain_limit) { }
|
|
|
|
/* Set a bit for the PHI in VISITED and return true if it wasn't
|
|
already set. */
|
|
bool visit_phi (tree);
|
|
/* Clear a bit for the PHI in VISITED. */
|
|
void leave_phi (tree);
|
|
/* Return false if the SSA_NAME chain length counter has reached
|
|
the limit, otherwise increment the counter and return true. */
|
|
bool next ();
|
|
|
|
/* If the SSA_NAME has already been "seen" return a positive value.
|
|
Otherwise add it to VISITED. If the SSA_NAME limit has been
|
|
reached, return a negative value. Otherwise return zero. */
|
|
int next_phi (tree);
|
|
|
|
~ssa_name_limit_t ();
|
|
};
|
|
|
|
class pointer_query;
|
|
|
|
/* Describes a reference to an object used in an access. */
|
|
struct access_ref
|
|
{
|
|
/* Set the bounds of the reference to at most as many bytes
|
|
as the first argument or unknown when null, and at least
|
|
one when the second argument is true unless the first one
|
|
is a constant zero. */
|
|
access_ref (tree = NULL_TREE, bool = false);
|
|
|
|
/* Return the PHI node REF refers to or null if it doesn't. */
|
|
gphi *phi () const;
|
|
|
|
/* Return the object to which REF refers. */
|
|
tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
|
|
ssa_name_limit_t * = NULL, pointer_query * = NULL) const;
|
|
|
|
/* Return true if OFFRNG is the constant zero. */
|
|
bool offset_zero () const
|
|
{
|
|
return offrng[0] == 0 && offrng[1] == 0;
|
|
}
|
|
|
|
/* Return true if OFFRNG is bounded to a subrange of offset values
|
|
valid for the largest possible object. */
|
|
bool offset_bounded () const;
|
|
|
|
/* Return the maximum amount of space remaining and if non-null, set
|
|
argument to the minimum. */
|
|
offset_int size_remaining (offset_int * = NULL) const;
|
|
|
|
/* Return true if *THIS is an access to a declared object. */
|
|
bool ref_declared () const
|
|
{
|
|
return DECL_P (ref) && base0 && deref < 1;
|
|
}
|
|
|
|
/* Set the size range to the maximum. */
|
|
void set_max_size_range ()
|
|
{
|
|
sizrng[0] = 0;
|
|
sizrng[1] = wi::to_offset (max_object_size ());
|
|
}
|
|
|
|
/* Add OFF to the offset range. */
|
|
void add_offset (const offset_int &off)
|
|
{
|
|
add_offset (off, off);
|
|
}
|
|
|
|
/* Add the range [MIN, MAX] to the offset range. */
|
|
void add_offset (const offset_int &, const offset_int &);
|
|
|
|
/* Add the maximum representable offset to the offset range. */
|
|
void add_max_offset ()
|
|
{
|
|
offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
|
|
add_offset (-maxoff - 1, maxoff);
|
|
}
|
|
|
|
/* Issue an informational message describing the target of an access
|
|
with the given mode. */
|
|
void inform_access (access_mode) const;
|
|
|
|
/* Reference to the accessed object(s). */
|
|
tree ref;
|
|
|
|
/* Range of byte offsets into and sizes of the object(s). */
|
|
offset_int offrng[2];
|
|
offset_int sizrng[2];
|
|
/* Range of the bound of the access: denotes that the access
|
|
is at least BNDRNG[0] bytes but no more than BNDRNG[1].
|
|
For string functions the size of the actual access is
|
|
further constrained by the length of the string. */
|
|
offset_int bndrng[2];
|
|
|
|
/* Used to fold integer expressions when called from front ends. */
|
|
tree (*eval)(tree);
|
|
/* Positive when REF is dereferenced, negative when its address is
|
|
taken. */
|
|
int deref;
|
|
/* Set if trailing one-element arrays should be treated as flexible
|
|
array members. */
|
|
bool trail1special;
|
|
/* Set if valid offsets must start at zero (for declared and allocated
|
|
objects but not for others referenced by pointers). */
|
|
bool base0;
|
|
/* Set if REF refers to a function array parameter not declared
|
|
static. */
|
|
bool parmarray;
|
|
};
|
|
|
|
class range_query;
|
|
|
|
/* Queries and caches compute_objsize results. */
|
|
class pointer_query
|
|
{
|
|
DISABLE_COPY_AND_ASSIGN (pointer_query);
|
|
|
|
public:
|
|
/* Type of the two-level cache object defined by clients of the class
|
|
to have pointer SSA_NAMEs cached for speedy access. */
|
|
struct cache_type
|
|
{
|
|
/* 1-based indices into cache. */
|
|
vec<unsigned> indices;
|
|
/* The cache itself. */
|
|
vec<access_ref> access_refs;
|
|
};
|
|
|
|
/* Construct an object with the given Ranger instance and cache. */
|
|
explicit pointer_query (range_query * = NULL, cache_type * = NULL);
|
|
|
|
/* Retrieve the access_ref for a variable from cache if it's there. */
|
|
const access_ref* get_ref (tree, int = 1) const;
|
|
|
|
/* Retrieve the access_ref for a variable from cache or compute it. */
|
|
bool get_ref (tree, access_ref*, int = 1);
|
|
|
|
/* Add an access_ref for the SSA_NAME to the cache. */
|
|
void put_ref (tree, const access_ref&, int = 1);
|
|
|
|
/* Flush the cache. */
|
|
void flush_cache ();
|
|
|
|
/* A Ranger instance. May be null to use global ranges. */
|
|
range_query *rvals;
|
|
/* Cache of SSA_NAMEs. May be null to disable caching. */
|
|
cache_type *var_cache;
|
|
|
|
/* Cache performance counters. */
|
|
mutable unsigned hits;
|
|
mutable unsigned misses;
|
|
mutable unsigned failures;
|
|
mutable unsigned depth;
|
|
mutable unsigned max_depth;
|
|
};
|
|
|
|
/* Describes a pair of references used in an access by built-in
|
|
functions like memcpy. */
|
|
struct access_data
|
|
{
|
|
/* Set the access to at most MAXWRITE and MAXREAD bytes, and
|
|
at least 1 when MINWRITE or MINREAD, respectively, is set. */
|
|
access_data (tree expr, access_mode mode,
|
|
tree maxwrite = NULL_TREE, bool minwrite = false,
|
|
tree maxread = NULL_TREE, bool minread = false)
|
|
: call (expr),
|
|
dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
|
|
|
|
/* Built-in function call. */
|
|
tree call;
|
|
/* Destination and source of the access. */
|
|
access_ref dst, src;
|
|
/* Read-only for functions like memcmp or strlen, write-only
|
|
for memset, read-write for memcpy or strcat. */
|
|
access_mode mode;
|
|
};
|
|
|
|
extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
|
|
range_query * = NULL);
|
|
extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
|
|
|
|
extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
|
|
/* Legacy/transitional API. Should not be used in new code. */
|
|
extern tree compute_objsize (tree, int, access_ref *, pointer_query *);
|
|
extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
|
|
range_query * = NULL);
|
|
extern bool check_access (tree, tree, tree, tree, tree,
|
|
access_mode, const access_data * = NULL);
|
|
extern void maybe_emit_free_warning (tree);
|
|
|
|
#endif /* GCC_BUILTINS_H */
|