tree-core.h (built_in_class): Add builtin codes to be used by Pointer Bounds Checker for instrumented builtin...
* tree-core.h (built_in_class): Add builtin codes to be used by Pointer Bounds Checker for instrumented builtin functions. * tree-streamer-in.c: Include ipa-chkp.h. (streamer_get_builtin_tree): Created instrumented decl if required. * ipa-chkp.h (chkp_maybe_clone_builtin_fndecl): New. * ipa-chkp.c (chkp_build_instrumented_fndecl): Support builtin function decls. (chkp_maybe_clone_builtin_fndecl): New. (chkp_maybe_create_clone): Support builtin function decls. (chkp_versioning): Clone builtin functions. * tree-chkp.c (chkp_instrument_normal_builtin): New. (chkp_add_bounds_to_call_stmt): Support builtin functions. (chkp_replace_function_pointer): Likewise. * builtins.c (expand_builtin_memcpy_args): New. (expand_builtin_memcpy): Call expand_builtin_memcpy_args. (expand_builtin_memcpy_with_bounds): New. (expand_builtin_mempcpy_with_bounds): New. (expand_builtin_mempcpy_args): Add orig_exp arg. Support BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK (expand_builtin_memset_with_bounds): New. (expand_builtin_memset_args): Support BUILT_IN_CHKP_MEMSET_NOBND_NOCHK. (expand_builtin_with_bounds): New. * builtins.h (expand_builtin_with_bounds): New. * expr.c (expand_expr_real_1): Support instrumented builtin calls. From-SVN: r217655
This commit is contained in:
parent
5134529b25
commit
edcf72f3c9
|
@ -1,3 +1,31 @@
|
|||
2014-11-17 Ilya Enkovich <ilya.enkovich@intel.com>
|
||||
|
||||
* tree-core.h (built_in_class): Add builtin codes to be used
|
||||
by Pointer Bounds Checker for instrumented builtin functions.
|
||||
* tree-streamer-in.c: Include ipa-chkp.h.
|
||||
(streamer_get_builtin_tree): Created instrumented decl if
|
||||
required.
|
||||
* ipa-chkp.h (chkp_maybe_clone_builtin_fndecl): New.
|
||||
* ipa-chkp.c (chkp_build_instrumented_fndecl): Support builtin
|
||||
function decls.
|
||||
(chkp_maybe_clone_builtin_fndecl): New.
|
||||
(chkp_maybe_create_clone): Support builtin function decls.
|
||||
(chkp_versioning): Clone builtin functions.
|
||||
* tree-chkp.c (chkp_instrument_normal_builtin): New.
|
||||
(chkp_add_bounds_to_call_stmt): Support builtin functions.
|
||||
(chkp_replace_function_pointer): Likewise.
|
||||
* builtins.c (expand_builtin_memcpy_args): New.
|
||||
(expand_builtin_memcpy): Call expand_builtin_memcpy_args.
|
||||
(expand_builtin_memcpy_with_bounds): New.
|
||||
(expand_builtin_mempcpy_with_bounds): New.
|
||||
(expand_builtin_mempcpy_args): Add orig_exp arg. Support
|
||||
BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK
|
||||
(expand_builtin_memset_with_bounds): New.
|
||||
(expand_builtin_memset_args): Support BUILT_IN_CHKP_MEMSET_NOBND_NOCHK.
|
||||
(expand_builtin_with_bounds): New.
|
||||
* builtins.h (expand_builtin_with_bounds): New.
|
||||
* expr.c (expand_expr_real_1): Support instrumented builtin calls.
|
||||
|
||||
2014-11-17 Dodji Seketeli <dodji@redhat.com>
|
||||
|
||||
* gimple.h (gimple_set_visited, gimple_visited_p)
|
||||
|
|
307
gcc/builtins.c
307
gcc/builtins.c
|
@ -132,15 +132,19 @@ static rtx expand_builtin_strcmp (tree, rtx);
|
|||
static rtx expand_builtin_strncmp (tree, rtx, machine_mode);
|
||||
static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, machine_mode);
|
||||
static rtx expand_builtin_memcpy (tree, rtx);
|
||||
static rtx expand_builtin_memcpy_with_bounds (tree, rtx);
|
||||
static rtx expand_builtin_memcpy_args (tree, tree, tree, rtx, tree);
|
||||
static rtx expand_builtin_mempcpy (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx,
|
||||
machine_mode, int);
|
||||
machine_mode, int, tree);
|
||||
static rtx expand_builtin_strcpy (tree, rtx);
|
||||
static rtx expand_builtin_strcpy_args (tree, tree, rtx);
|
||||
static rtx expand_builtin_stpcpy (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_strncpy (tree, rtx);
|
||||
static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode);
|
||||
static rtx expand_builtin_memset (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_memset_with_bounds (tree, rtx, machine_mode);
|
||||
static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree);
|
||||
static rtx expand_builtin_bzero (tree);
|
||||
static rtx expand_builtin_strlen (tree, rtx, machine_mode);
|
||||
|
@ -3175,6 +3179,81 @@ determine_block_size (tree len, rtx len_rtx,
|
|||
GET_MODE_MASK (GET_MODE (len_rtx)));
|
||||
}
|
||||
|
||||
/* Helper function to do the actual work for expand_builtin_memcpy. */
|
||||
|
||||
static rtx
|
||||
expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
|
||||
{
|
||||
const char *src_str;
|
||||
unsigned int src_align = get_pointer_alignment (src);
|
||||
unsigned int dest_align = get_pointer_alignment (dest);
|
||||
rtx dest_mem, src_mem, dest_addr, len_rtx;
|
||||
HOST_WIDE_INT expected_size = -1;
|
||||
unsigned int expected_align = 0;
|
||||
unsigned HOST_WIDE_INT min_size;
|
||||
unsigned HOST_WIDE_INT max_size;
|
||||
unsigned HOST_WIDE_INT probable_max_size;
|
||||
|
||||
/* If DEST is not a pointer type, call the normal function. */
|
||||
if (dest_align == 0)
|
||||
return NULL_RTX;
|
||||
|
||||
/* If either SRC is not a pointer type, don't do this
|
||||
operation in-line. */
|
||||
if (src_align == 0)
|
||||
return NULL_RTX;
|
||||
|
||||
if (currently_expanding_gimple_stmt)
|
||||
stringop_block_profile (currently_expanding_gimple_stmt,
|
||||
&expected_align, &expected_size);
|
||||
|
||||
if (expected_align < dest_align)
|
||||
expected_align = dest_align;
|
||||
dest_mem = get_memory_rtx (dest, len);
|
||||
set_mem_align (dest_mem, dest_align);
|
||||
len_rtx = expand_normal (len);
|
||||
determine_block_size (len, len_rtx, &min_size, &max_size,
|
||||
&probable_max_size);
|
||||
src_str = c_getstr (src);
|
||||
|
||||
/* If SRC is a string constant and block move would be done
|
||||
by pieces, we can avoid loading the string from memory
|
||||
and only stored the computed constants. */
|
||||
if (src_str
|
||||
&& CONST_INT_P (len_rtx)
|
||||
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
|
||||
&& can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
|
||||
CONST_CAST (char *, src_str),
|
||||
dest_align, false))
|
||||
{
|
||||
dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
|
||||
builtin_memcpy_read_str,
|
||||
CONST_CAST (char *, src_str),
|
||||
dest_align, false, 0);
|
||||
dest_mem = force_operand (XEXP (dest_mem, 0), target);
|
||||
dest_mem = convert_memory_address (ptr_mode, dest_mem);
|
||||
return dest_mem;
|
||||
}
|
||||
|
||||
src_mem = get_memory_rtx (src, len);
|
||||
set_mem_align (src_mem, src_align);
|
||||
|
||||
/* Copy word part most expediently. */
|
||||
dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx,
|
||||
CALL_EXPR_TAILCALL (exp)
|
||||
? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL,
|
||||
expected_align, expected_size,
|
||||
min_size, max_size, probable_max_size);
|
||||
|
||||
if (dest_addr == 0)
|
||||
{
|
||||
dest_addr = force_operand (XEXP (dest_mem, 0), target);
|
||||
dest_addr = convert_memory_address (ptr_mode, dest_addr);
|
||||
}
|
||||
|
||||
return dest_addr;
|
||||
}
|
||||
|
||||
/* Expand a call EXP to the memcpy builtin.
|
||||
Return NULL_RTX if we failed, the caller should emit a normal call,
|
||||
otherwise try to get the result in TARGET, if convenient (and in
|
||||
|
@ -3191,73 +3270,38 @@ expand_builtin_memcpy (tree exp, rtx target)
|
|||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
const char *src_str;
|
||||
unsigned int src_align = get_pointer_alignment (src);
|
||||
unsigned int dest_align = get_pointer_alignment (dest);
|
||||
rtx dest_mem, src_mem, dest_addr, len_rtx;
|
||||
HOST_WIDE_INT expected_size = -1;
|
||||
unsigned int expected_align = 0;
|
||||
unsigned HOST_WIDE_INT min_size;
|
||||
unsigned HOST_WIDE_INT max_size;
|
||||
unsigned HOST_WIDE_INT probable_max_size;
|
||||
return expand_builtin_memcpy_args (dest, src, len, target, exp);
|
||||
}
|
||||
}
|
||||
|
||||
/* If DEST is not a pointer type, call the normal function. */
|
||||
if (dest_align == 0)
|
||||
return NULL_RTX;
|
||||
/* Expand an instrumented call EXP to the memcpy builtin.
|
||||
Return NULL_RTX if we failed, the caller should emit a normal call,
|
||||
otherwise try to get the result in TARGET, if convenient (and in
|
||||
mode MODE if that's convenient). */
|
||||
|
||||
/* If either SRC is not a pointer type, don't do this
|
||||
operation in-line. */
|
||||
if (src_align == 0)
|
||||
return NULL_RTX;
|
||||
static rtx
|
||||
expand_builtin_memcpy_with_bounds (tree exp, rtx target)
|
||||
{
|
||||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_BOUNDS_TYPE,
|
||||
POINTER_TYPE, POINTER_BOUNDS_TYPE,
|
||||
INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
else
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 2);
|
||||
tree len = CALL_EXPR_ARG (exp, 4);
|
||||
rtx res = expand_builtin_memcpy_args (dest, src, len, target, exp);
|
||||
|
||||
if (currently_expanding_gimple_stmt)
|
||||
stringop_block_profile (currently_expanding_gimple_stmt,
|
||||
&expected_align, &expected_size);
|
||||
|
||||
if (expected_align < dest_align)
|
||||
expected_align = dest_align;
|
||||
dest_mem = get_memory_rtx (dest, len);
|
||||
set_mem_align (dest_mem, dest_align);
|
||||
len_rtx = expand_normal (len);
|
||||
determine_block_size (len, len_rtx, &min_size, &max_size,
|
||||
&probable_max_size);
|
||||
src_str = c_getstr (src);
|
||||
|
||||
/* If SRC is a string constant and block move would be done
|
||||
by pieces, we can avoid loading the string from memory
|
||||
and only stored the computed constants. */
|
||||
if (src_str
|
||||
&& CONST_INT_P (len_rtx)
|
||||
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
|
||||
&& can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
|
||||
CONST_CAST (char *, src_str),
|
||||
dest_align, false))
|
||||
/* Return src bounds with the result. */
|
||||
if (res)
|
||||
{
|
||||
dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
|
||||
builtin_memcpy_read_str,
|
||||
CONST_CAST (char *, src_str),
|
||||
dest_align, false, 0);
|
||||
dest_mem = force_operand (XEXP (dest_mem, 0), target);
|
||||
dest_mem = convert_memory_address (ptr_mode, dest_mem);
|
||||
return dest_mem;
|
||||
rtx bnd = force_reg (BNDmode,
|
||||
expand_normal (CALL_EXPR_ARG (exp, 1)));
|
||||
res = chkp_join_splitted_slot (res, bnd);
|
||||
}
|
||||
|
||||
src_mem = get_memory_rtx (src, len);
|
||||
set_mem_align (src_mem, src_align);
|
||||
|
||||
/* Copy word part most expediently. */
|
||||
dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx,
|
||||
CALL_EXPR_TAILCALL (exp)
|
||||
? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL,
|
||||
expected_align, expected_size,
|
||||
min_size, max_size, probable_max_size);
|
||||
|
||||
if (dest_addr == 0)
|
||||
{
|
||||
dest_addr = force_operand (XEXP (dest_mem, 0), target);
|
||||
dest_addr = convert_memory_address (ptr_mode, dest_addr);
|
||||
}
|
||||
return dest_addr;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3281,7 +3325,40 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
|
|||
tree src = CALL_EXPR_ARG (exp, 1);
|
||||
tree len = CALL_EXPR_ARG (exp, 2);
|
||||
return expand_builtin_mempcpy_args (dest, src, len,
|
||||
target, mode, /*endp=*/ 1);
|
||||
target, mode, /*endp=*/ 1,
|
||||
exp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand an instrumented call EXP to the mempcpy builtin.
|
||||
Return NULL_RTX if we failed, the caller should emit a normal call,
|
||||
otherwise try to get the result in TARGET, if convenient (and in
|
||||
mode MODE if that's convenient). */
|
||||
|
||||
static rtx
|
||||
expand_builtin_mempcpy_with_bounds (tree exp, rtx target, machine_mode mode)
|
||||
{
|
||||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_BOUNDS_TYPE,
|
||||
POINTER_TYPE, POINTER_BOUNDS_TYPE,
|
||||
INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
else
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree src = CALL_EXPR_ARG (exp, 2);
|
||||
tree len = CALL_EXPR_ARG (exp, 4);
|
||||
rtx res = expand_builtin_mempcpy_args (dest, src, len, target,
|
||||
mode, 1, exp);
|
||||
|
||||
/* Return src bounds with the result. */
|
||||
if (res)
|
||||
{
|
||||
rtx bnd = force_reg (BNDmode,
|
||||
expand_normal (CALL_EXPR_ARG (exp, 1)));
|
||||
res = chkp_join_splitted_slot (res, bnd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3293,10 +3370,23 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
|
|||
|
||||
static rtx
|
||||
expand_builtin_mempcpy_args (tree dest, tree src, tree len,
|
||||
rtx target, machine_mode mode, int endp)
|
||||
rtx target, machine_mode mode, int endp,
|
||||
tree orig_exp)
|
||||
{
|
||||
tree fndecl = get_callee_fndecl (orig_exp);
|
||||
|
||||
/* If return value is ignored, transform mempcpy into memcpy. */
|
||||
if (target == const0_rtx && builtin_decl_implicit_p (BUILT_IN_MEMCPY))
|
||||
if (target == const0_rtx
|
||||
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK_CHKP
|
||||
&& builtin_decl_implicit_p (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP))
|
||||
{
|
||||
tree fn = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP);
|
||||
tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3,
|
||||
dest, src, len);
|
||||
return expand_expr (result, target, mode, EXPAND_NORMAL);
|
||||
}
|
||||
else if (target == const0_rtx
|
||||
&& builtin_decl_implicit_p (BUILT_IN_MEMCPY))
|
||||
{
|
||||
tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
|
||||
tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3,
|
||||
|
@ -3481,7 +3571,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
|
|||
|
||||
lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
|
||||
ret = expand_builtin_mempcpy_args (dst, src, lenp1,
|
||||
target, mode, /*endp=*/2);
|
||||
target, mode, /*endp=*/2,
|
||||
exp);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -3647,6 +3738,36 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
|
|||
}
|
||||
}
|
||||
|
||||
/* Expand expression EXP, which is an instrumented call to the memset builtin.
|
||||
Return NULL_RTX if we failed the caller should emit a normal call, otherwise
|
||||
try to get the result in TARGET, if convenient (and in mode MODE if that's
|
||||
convenient). */
|
||||
|
||||
static rtx
|
||||
expand_builtin_memset_with_bounds (tree exp, rtx target, machine_mode mode)
|
||||
{
|
||||
if (!validate_arglist (exp,
|
||||
POINTER_TYPE, POINTER_BOUNDS_TYPE,
|
||||
INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
|
||||
return NULL_RTX;
|
||||
else
|
||||
{
|
||||
tree dest = CALL_EXPR_ARG (exp, 0);
|
||||
tree val = CALL_EXPR_ARG (exp, 2);
|
||||
tree len = CALL_EXPR_ARG (exp, 3);
|
||||
rtx res = expand_builtin_memset_args (dest, val, len, target, mode, exp);
|
||||
|
||||
/* Return src bounds with the result. */
|
||||
if (res)
|
||||
{
|
||||
rtx bnd = force_reg (BNDmode,
|
||||
expand_normal (CALL_EXPR_ARG (exp, 1)));
|
||||
res = chkp_join_splitted_slot (res, bnd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to do the actual work for expand_builtin_memset. The
|
||||
arguments to the builtin_memset call DEST, VAL, and LEN are broken out
|
||||
so that this can also be called without constructing an actual CALL_EXPR.
|
||||
|
@ -3775,7 +3896,8 @@ expand_builtin_memset_args (tree dest, tree val, tree len,
|
|||
do_libcall:
|
||||
fndecl = get_callee_fndecl (orig_exp);
|
||||
fcode = DECL_FUNCTION_CODE (fndecl);
|
||||
if (fcode == BUILT_IN_MEMSET)
|
||||
if (fcode == BUILT_IN_MEMSET
|
||||
|| fcode == BUILT_IN_CHKP_MEMSET_NOBND_NOCHK_CHKP)
|
||||
fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 3,
|
||||
dest, val, len);
|
||||
else if (fcode == BUILT_IN_BZERO)
|
||||
|
@ -5848,6 +5970,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
|||
}
|
||||
}
|
||||
|
||||
/* expand_builtin_with_bounds is supposed to be used for
|
||||
instrumented builtin calls. */
|
||||
gcc_assert (!CALL_WITH_BOUNDS_P (exp));
|
||||
|
||||
switch (fcode)
|
||||
|
@ -6908,6 +7032,53 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
|||
return expand_call (exp, target, ignore);
|
||||
}
|
||||
|
||||
/* Similar to expand_builtin but is used for instrumented calls. */
|
||||
|
||||
rtx
|
||||
expand_builtin_with_bounds (tree exp, rtx target,
|
||||
rtx subtarget ATTRIBUTE_UNUSED,
|
||||
machine_mode mode, int ignore)
|
||||
{
|
||||
tree fndecl = get_callee_fndecl (exp);
|
||||
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
|
||||
|
||||
gcc_assert (CALL_WITH_BOUNDS_P (exp));
|
||||
|
||||
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
|
||||
return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
|
||||
|
||||
gcc_assert (fcode > BEGIN_CHKP_BUILTINS
|
||||
&& fcode < END_CHKP_BUILTINS);
|
||||
|
||||
switch (fcode)
|
||||
{
|
||||
case BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP:
|
||||
target = expand_builtin_memcpy_with_bounds (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK_CHKP:
|
||||
target = expand_builtin_mempcpy_with_bounds (exp, target, mode);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
case BUILT_IN_CHKP_MEMSET_NOBND_NOCHK_CHKP:
|
||||
target = expand_builtin_memset_with_bounds (exp, target, mode);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* The switch statement above can drop through to cause the function
|
||||
to be called normally. */
|
||||
return expand_call (exp, target, ignore);
|
||||
}
|
||||
|
||||
/* Determine whether a tree node represents a call to a built-in
|
||||
function. If the tree T is a call to a built-in function with
|
||||
the right number of arguments of the appropriate types, return
|
||||
|
|
|
@ -69,6 +69,7 @@ extern tree std_canonical_va_list_type (tree);
|
|||
extern void std_expand_builtin_va_start (tree, rtx);
|
||||
extern void expand_builtin_trap (void);
|
||||
extern rtx expand_builtin (tree, rtx, rtx, machine_mode, int);
|
||||
extern rtx expand_builtin_with_bounds (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);
|
||||
extern tree fold_fma (location_t, tree, tree, tree, tree);
|
||||
|
|
|
@ -10462,7 +10462,11 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
|
|||
if (fndecl && DECL_BUILT_IN (fndecl))
|
||||
{
|
||||
gcc_assert (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_FRONTEND);
|
||||
return expand_builtin (exp, target, subtarget, tmode, ignore);
|
||||
if (CALL_WITH_BOUNDS_P (exp))
|
||||
return expand_builtin_with_bounds (exp, target, subtarget,
|
||||
tmode, ignore);
|
||||
else
|
||||
return expand_builtin (exp, target, subtarget, tmode, ignore);
|
||||
}
|
||||
}
|
||||
return expand_call (exp, target, ignore);
|
||||
|
|
|
@ -129,6 +129,16 @@ chkp_build_instrumented_fndecl (tree fndecl)
|
|||
make own copy. */
|
||||
DECL_ATTRIBUTES (new_decl) = copy_list (DECL_ATTRIBUTES (fndecl));
|
||||
|
||||
/* Change builtin function code. */
|
||||
if (DECL_BUILT_IN (new_decl))
|
||||
{
|
||||
gcc_assert (DECL_BUILT_IN_CLASS (new_decl) == BUILT_IN_NORMAL);
|
||||
gcc_assert (DECL_FUNCTION_CODE (new_decl) < BEGIN_CHKP_BUILTINS);
|
||||
DECL_FUNCTION_CODE (new_decl)
|
||||
= (enum built_in_function)(DECL_FUNCTION_CODE (new_decl)
|
||||
+ BEGIN_CHKP_BUILTINS + 1);
|
||||
}
|
||||
|
||||
return new_decl;
|
||||
}
|
||||
|
||||
|
@ -354,6 +364,33 @@ chkp_add_bounds_params_to_function (tree fndecl)
|
|||
chkp_copy_function_type_adding_bounds (TREE_TYPE (fndecl));
|
||||
}
|
||||
|
||||
/* Return an instrumentation clone for builtin function
|
||||
FNDECL. Create one if needed. */
|
||||
|
||||
tree
|
||||
chkp_maybe_clone_builtin_fndecl (tree fndecl)
|
||||
{
|
||||
tree clone;
|
||||
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
|
||||
|
||||
gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
|
||||
&& fcode < BEGIN_CHKP_BUILTINS);
|
||||
|
||||
fcode = (enum built_in_function) (fcode + BEGIN_CHKP_BUILTINS + 1);
|
||||
clone = builtin_decl_explicit (fcode);
|
||||
if (clone)
|
||||
return clone;
|
||||
|
||||
clone = chkp_build_instrumented_fndecl (fndecl);
|
||||
chkp_add_bounds_params_to_function (clone);
|
||||
|
||||
gcc_assert (DECL_FUNCTION_CODE (clone) == fcode);
|
||||
|
||||
set_builtin_decl (fcode, clone, false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/* Return clone created for instrumentation of NODE or NULL. */
|
||||
|
||||
cgraph_node *
|
||||
|
@ -364,6 +401,54 @@ chkp_maybe_create_clone (tree fndecl)
|
|||
|
||||
gcc_assert (!node->instrumentation_clone);
|
||||
|
||||
if (DECL_BUILT_IN (fndecl)
|
||||
&& (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL
|
||||
|| DECL_FUNCTION_CODE (fndecl) >= BEGIN_CHKP_BUILTINS))
|
||||
return NULL;
|
||||
|
||||
clone = node->instrumented_version;
|
||||
|
||||
/* Some instrumented builtin function calls may be optimized and
|
||||
cgraph nodes may be removed as unreachable. Later optimizations
|
||||
may generate new calls to removed functions and in this case
|
||||
we have to recreate cgraph node. FUNCTION_DECL for instrumented
|
||||
builtin still exists and should be reused in such case. */
|
||||
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
|
||||
&& fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl))
|
||||
&& !clone)
|
||||
{
|
||||
enum built_in_function fncode = DECL_FUNCTION_CODE (fndecl);
|
||||
tree new_decl;
|
||||
|
||||
fncode = (enum built_in_function) (fncode + BEGIN_CHKP_BUILTINS + 1);
|
||||
new_decl = builtin_decl_explicit (fncode);
|
||||
|
||||
/* We've actually already created an instrumented clone once.
|
||||
Restore it. */
|
||||
if (new_decl)
|
||||
{
|
||||
clone = cgraph_node::get (new_decl);
|
||||
|
||||
if (!clone)
|
||||
{
|
||||
gcc_assert (!gimple_has_body_p (fndecl));
|
||||
clone = cgraph_node::get_create (new_decl);
|
||||
clone->externally_visible = node->externally_visible;
|
||||
clone->local = node->local;
|
||||
clone->address_taken = node->address_taken;
|
||||
clone->thunk = node->thunk;
|
||||
clone->alias = node->alias;
|
||||
clone->weakref = node->weakref;
|
||||
clone->cpp_implicit_alias = node->cpp_implicit_alias;
|
||||
clone->orig_decl = fndecl;
|
||||
clone->instrumentation_clone = true;
|
||||
}
|
||||
|
||||
clone->instrumented_version = node;
|
||||
node->instrumented_version = clone;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clone)
|
||||
{
|
||||
tree new_decl = chkp_build_instrumented_fndecl (fndecl);
|
||||
|
@ -408,6 +493,15 @@ chkp_maybe_create_clone (tree fndecl)
|
|||
actually copies args list from the original decl. */
|
||||
chkp_add_bounds_params_to_function (new_decl);
|
||||
|
||||
/* Remember builtin fndecl. */
|
||||
if (DECL_BUILT_IN_CLASS (clone->decl) == BUILT_IN_NORMAL
|
||||
&& fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)))
|
||||
{
|
||||
gcc_assert (!builtin_decl_explicit (DECL_FUNCTION_CODE (clone->decl)));
|
||||
set_builtin_decl (DECL_FUNCTION_CODE (clone->decl),
|
||||
clone->decl, false);
|
||||
}
|
||||
|
||||
/* Clones have the same comdat group as originals. */
|
||||
if (node->same_comdat_group
|
||||
|| DECL_ONE_ONLY (node->decl))
|
||||
|
@ -487,8 +581,9 @@ chkp_versioning (void)
|
|||
&& (!flag_chkp_instrument_marked_only
|
||||
|| lookup_attribute ("bnd_instrument",
|
||||
DECL_ATTRIBUTES (node->decl)))
|
||||
/* No builtins instrumentation for now. */
|
||||
&& DECL_BUILT_IN_CLASS (node->decl) == NOT_BUILT_IN)
|
||||
&& (!DECL_BUILT_IN (node->decl)
|
||||
|| (DECL_BUILT_IN_CLASS (node->decl) == BUILT_IN_NORMAL
|
||||
&& DECL_FUNCTION_CODE (node->decl) < BEGIN_CHKP_BUILTINS)))
|
||||
chkp_maybe_create_clone (node->decl);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#define GCC_IPA_CHKP_H
|
||||
|
||||
extern tree chkp_copy_function_type_adding_bounds (tree orig_type);
|
||||
extern tree chkp_maybe_clone_builtin_fndecl (tree fndecl);
|
||||
extern cgraph_node *chkp_maybe_create_clone (tree fndecl);
|
||||
|
||||
#endif /* GCC_IPA_CHKP_H */
|
||||
|
|
|
@ -1586,6 +1586,50 @@ chkp_find_bound_slots (const_tree type, bitmap res)
|
|||
chkp_find_bound_slots_1 (type, res, 0);
|
||||
}
|
||||
|
||||
/* Return 1 if call to FNDECL should be instrumented
|
||||
and 0 otherwise. */
|
||||
|
||||
static bool
|
||||
chkp_instrument_normal_builtin (tree fndecl)
|
||||
{
|
||||
switch (DECL_FUNCTION_CODE (fndecl))
|
||||
{
|
||||
case BUILT_IN_STRLEN:
|
||||
case BUILT_IN_STRCPY:
|
||||
case BUILT_IN_STRNCPY:
|
||||
case BUILT_IN_STPCPY:
|
||||
case BUILT_IN_STPNCPY:
|
||||
case BUILT_IN_STRCAT:
|
||||
case BUILT_IN_STRNCAT:
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMPCPY:
|
||||
case BUILT_IN_MEMSET:
|
||||
case BUILT_IN_MEMMOVE:
|
||||
case BUILT_IN_BZERO:
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_BCMP:
|
||||
case BUILT_IN_MEMCMP:
|
||||
case BUILT_IN_MEMCPY_CHK:
|
||||
case BUILT_IN_MEMPCPY_CHK:
|
||||
case BUILT_IN_MEMMOVE_CHK:
|
||||
case BUILT_IN_MEMSET_CHK:
|
||||
case BUILT_IN_STRCPY_CHK:
|
||||
case BUILT_IN_STRNCPY_CHK:
|
||||
case BUILT_IN_STPCPY_CHK:
|
||||
case BUILT_IN_STPNCPY_CHK:
|
||||
case BUILT_IN_STRCAT_CHK:
|
||||
case BUILT_IN_STRNCAT_CHK:
|
||||
case BUILT_IN_MALLOC:
|
||||
case BUILT_IN_CALLOC:
|
||||
case BUILT_IN_REALLOC:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add bound arguments to call statement pointed by GSI.
|
||||
Also performs a replacement of user checker builtins calls
|
||||
with internal ones. */
|
||||
|
@ -1619,7 +1663,7 @@ chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
|
|||
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE)
|
||||
return;
|
||||
|
||||
/* Donothing for calls to legacy functions. */
|
||||
/* Do nothing for calls to legacy functions. */
|
||||
if (fndecl
|
||||
&& lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl)))
|
||||
return;
|
||||
|
@ -1686,11 +1730,20 @@ chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
|
|||
if (!flag_chkp_instrument_calls)
|
||||
return;
|
||||
|
||||
/* Avoid instrumented builtin functions for now. Due to IPA
|
||||
it also means we have to avoid instrumentation of indirect
|
||||
calls. */
|
||||
if (fndecl && DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN)
|
||||
return;
|
||||
/* We instrument only some subset of builtins. We also instrument
|
||||
builtin calls to be inlined. */
|
||||
if (fndecl
|
||||
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
|
||||
&& !chkp_instrument_normal_builtin (fndecl))
|
||||
{
|
||||
if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)))
|
||||
return;
|
||||
|
||||
struct cgraph_node *clone = chkp_maybe_create_clone (fndecl);
|
||||
if (!clone
|
||||
|| !gimple_has_body_p (clone->decl))
|
||||
return;
|
||||
}
|
||||
|
||||
/* If function decl is available then use it for
|
||||
formal arguments list. Otherwise use function type. */
|
||||
|
@ -1764,14 +1817,6 @@ chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
|
|||
}
|
||||
new_args.release ();
|
||||
|
||||
/* If we call built-in function and pass no bounds then
|
||||
we do not need to change anything. */
|
||||
if (new_call == call
|
||||
&& fndecl
|
||||
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
|
||||
&& fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)))
|
||||
return;
|
||||
|
||||
/* For direct calls fndecl is replaced with instrumented version. */
|
||||
if (fndecl)
|
||||
{
|
||||
|
@ -3905,15 +3950,21 @@ chkp_replace_function_pointer (tree *op, int *walk_subtrees,
|
|||
{
|
||||
if (TREE_CODE (*op) == FUNCTION_DECL
|
||||
&& !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (*op))
|
||||
/* Do not replace builtins for now. */
|
||||
&& DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN)
|
||||
&& (DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN
|
||||
/* For builtins we replace pointers only for selected
|
||||
function and functions having definitions. */
|
||||
|| (DECL_BUILT_IN_CLASS (*op) == BUILT_IN_NORMAL
|
||||
&& (chkp_instrument_normal_builtin (*op)
|
||||
|| gimple_has_body_p (*op)))))
|
||||
{
|
||||
struct cgraph_node *node = cgraph_node::get_create (*op);
|
||||
struct cgraph_node *clone = NULL;
|
||||
|
||||
if (!node->instrumentation_clone)
|
||||
chkp_maybe_create_clone (*op);
|
||||
clone = chkp_maybe_create_clone (*op);
|
||||
|
||||
*op = node->instrumented_version->decl;
|
||||
if (clone)
|
||||
*op = clone->decl;
|
||||
*walk_subtrees = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,14 @@ enum built_in_class {
|
|||
enum built_in_function {
|
||||
#include "builtins.def"
|
||||
|
||||
BEGIN_CHKP_BUILTINS,
|
||||
|
||||
#undef DEF_BUILTIN
|
||||
#define DEF_BUILTIN(ENUM, N, C, T, LT, B, F, NA, AT, IM, COND) ENUM##_CHKP,
|
||||
#include "builtins.def"
|
||||
|
||||
END_CHKP_BUILTINS,
|
||||
|
||||
/* Complex division routines in libgcc. These are done via builtins
|
||||
because emit_library_call_value can't handle complex values. */
|
||||
BUILT_IN_COMPLEX_MUL_MIN,
|
||||
|
|
|
@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "streamer-hooks.h"
|
||||
#include "lto-streamer.h"
|
||||
#include "builtins.h"
|
||||
#include "ipa-chkp.h"
|
||||
|
||||
/* Read a STRING_CST from the string table in DATA_IN using input
|
||||
block IB. */
|
||||
|
@ -1113,6 +1114,14 @@ streamer_get_builtin_tree (struct lto_input_block *ib, struct data_in *data_in)
|
|||
if (fcode >= END_BUILTINS)
|
||||
fatal_error ("machine independent builtin code out of range");
|
||||
result = builtin_decl_explicit (fcode);
|
||||
if (!result
|
||||
&& fcode > BEGIN_CHKP_BUILTINS
|
||||
&& fcode < END_CHKP_BUILTINS)
|
||||
{
|
||||
fcode = (enum built_in_function) (fcode - BEGIN_CHKP_BUILTINS - 1);
|
||||
result = builtin_decl_explicit (fcode);
|
||||
result = chkp_maybe_clone_builtin_fndecl (result);
|
||||
}
|
||||
gcc_assert (result);
|
||||
}
|
||||
else if (fclass == BUILT_IN_MD)
|
||||
|
|
Loading…
Reference in New Issue