Discussion:
[PATCH, Pointer Bounds Checker 19/x] Support bounds in expand
Ilya Enkovich
2014-06-02 15:02:45 UTC
Permalink
Hi,

This patch adds support for input bounds, call bounds args and returned bounds in expand pass.

Bootstrapped and tested on linux-x86_64.

Thanks,
Ilya
--
gcc/

2014-06-02 Ilya Enkovich <***@intel.com>

* calls.c: Include tree-chkp.h, rtl-chkp.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.h (store_expr): Add param for bounds target.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr): Add bounds target argument. Handle
bounds returned by calls.
(store_constructor): Adjust to new store_expr signature.
(store_field): Likewise.
(expand_expr_real_2): Likewise.
(expand_expr_real_1): Likewise.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
* tree-outof-ssa.c (insert_value_copy_on_edge): Adjust to new
store_expr signature.


diff --git a/gcc/calls.c b/gcc/calls.c
index e1dc8eb..140ceb4 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,11 +44,14 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -76,6 +79,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
+ /* For pointer bounds hold an index of parm bounds are bound to. -1 if
+ there is no such pointer. */
+ int pointer_arg;
+ /* If pointer_arg refers a structure, then pointer_offset holds an offset
+ of a pointer in this structure. */
+ int pointer_offset;
/* If REG was promoted from the actual mode of the argument expression,
indicates whether the promotion is sign- or zero-extended. */
int unsignedp;
@@ -133,6 +145,7 @@ static void emit_call_1 (rtx, tree, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT,
HOST_WIDE_INT, rtx, rtx, int, rtx, int,
cumulative_args_t);
static void precompute_register_parameters (int, struct arg_data *, int *);
+static void store_bounds (struct arg_data *, struct arg_data *);
static int store_one_arg (struct arg_data *, rtx, int, int, int);
static void store_unaligned_arguments_into_pseudos (struct arg_data *, int);
static int finalize_must_preallocate (int, int, struct arg_data *,
@@ -396,6 +409,10 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
&& MEM_EXPR (funmem) != NULL_TREE)
set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem));

+ /* Mark instrumented calls. */
+ if (call && fntree)
+ CALL_EXPR_WITH_BOUNDS_P (call) = CALL_WITH_BOUNDS_P (fntree);
+
/* Put the register usage information there. */
add_function_usage_to (call_insn, call_fusage);

@@ -1141,18 +1158,84 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* First fill in the actual arguments in the ARGS array, splitting
complex arguments if necessary. */
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;

if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
+
j += inc;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr (struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j - inc;
+
+ j += inc;
+ }
}
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
{
tree argtype = TREE_TYPE (arg);
+
+ /* Remember last param with pointer and associate it
+ with following pointer bounds. */
+ if (CALL_WITH_BOUNDS_P (exp)
+ && chkp_type_has_pointer (argtype))
+ {
+ if (slots)
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ ptr_arg = j;
+ if (!BOUNDED_TYPE_P (argtype))
+ slots = chkp_find_bound_slots (argtype);
+ }
+ else if (POINTER_BOUNDS_TYPE_P (argtype))
+ {
+ /* We expect bounds in instrumented calls only.
+ Otherwise it is a sign we lost flag due to some optimization
+ and may emit call args incorrectly. */
+ gcc_assert (CALL_WITH_BOUNDS_P (exp));
+
+ /* For structures look for the next available pointer. */
+ if (ptr_arg != -1 && slots)
+ {
+ unsigned bnd_no = bitmap_first_set_bit (slots);
+ args[j].pointer_offset =
+ bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ bitmap_clear_bit (slots, bnd_no);
+
+ /* Check we have no more pointers in the structure. */
+ if (bitmap_empty_p (slots))
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ }
+ args[j].pointer_arg = ptr_arg;
+
+ /* Check we covered all pointers in the previous
+ non bounds arg. */
+ if (!slots)
+ ptr_arg = -1;
+ }
+ else
+ ptr_arg = -1;
+
if (targetm.calls.split_complex_arg
&& argtype
&& TREE_CODE (argtype) == COMPLEX_TYPE
@@ -1167,6 +1250,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[j].tree_value = arg;
j += inc;
}
+
+ if (slots)
+ BITMAP_FREE (slots);
}

/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
@@ -1271,7 +1357,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
else
copy = assign_temp (type, 1, 0);

- store_expr (args[i].tree_value, copy, 0, false);
+ store_expr (args[i].tree_value, copy, 0, false, NULL);

/* Just change the const function to pure and then let
the next test clear the pure based on
@@ -1302,6 +1388,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);

+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
+
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
@@ -1335,10 +1427,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;

+ /* No stack allocation and padding for bounds. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ ;
/* Compute the stack-size of this argument. */
- if (args[i].reg == 0 || args[i].partial != 0
- || reg_parm_stack_space > 0
- || args[i].pass_on_stack)
+ else if (args[i].reg == 0 || args[i].partial != 0
+ || reg_parm_stack_space > 0
+ || args[i].pass_on_stack)
locate_and_pad_parm (mode, type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
@@ -1553,6 +1648,12 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
partial_seen = 1;
else if (partial_seen && args[i].reg == 0)
must_preallocate = 1;
+ /* We preallocate in case there are bounds passed
+ in the bounds table to have precomputed address
+ for bounds association. */
+ else if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ must_preallocate = 1;

if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
&& (TREE_CODE (args[i].tree_value) == CALL_EXPR
@@ -1604,6 +1705,10 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
&& args[i].partial == 0)
continue;

+ /* Pointer Bounds are never passed on the stack. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+
if (CONST_INT_P (offset))
addr = plus_constant (Pmode, arg_reg, INTVAL (offset));
else
@@ -2233,6 +2338,8 @@ expand_call (tree exp, rtx target, int ignore)
/* Register in which non-BLKmode value will be returned,
or 0 if no value or if value is BLKmode. */
rtx valreg;
+ /* Register(s) in which bounds are returned. */
+ rtx valbnd = NULL;
/* Address where we should return a BLKmode value;
0 if value not BLKmode. */
rtx structure_value_addr = 0;
@@ -2484,7 +2591,7 @@ expand_call (tree exp, rtx target, int ignore)

structure_value_addr_value =
make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
- structure_value_addr_parm = 1;
+ structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1;
}

/* Count the arguments and set NUM_ACTUALS. */
@@ -3003,15 +3110,28 @@ expand_call (tree exp, rtx target, int ignore)

/* Figure out the register where the value, if any, will come back. */
valreg = 0;
+ valbnd = 0;
if (TYPE_MODE (rettype) != VOIDmode
&& ! structure_value_addr)
{
if (pcc_struct_value)
- valreg = hard_function_value (build_pointer_type (rettype),
- fndecl, NULL, (pass == 0));
+ {
+ valreg = hard_function_value (build_pointer_type (rettype),
+ fndecl, NULL, (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.
+ chkp_function_value_bounds (build_pointer_type (rettype),
+ fndecl, (pass == 0));
+ }
else
- valreg = hard_function_value (rettype, fndecl, fntype,
- (pass == 0));
+ {
+ valreg = hard_function_value (rettype, fndecl, fntype,
+ (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.chkp_function_value_bounds (rettype,
+ fndecl,
+ (pass == 0));
+ }

/* If VALREG is a PARALLEL whose first member has a zero
offset, use that. This is for targets such as m68k that
@@ -3052,7 +3172,10 @@ expand_call (tree exp, rtx target, int ignore)

for (i = 0; i < num_actuals; i++)
{
- if (args[i].reg == 0 || args[i].pass_on_stack)
+ /* Delay bounds until all other args are stored. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+ else if (args[i].reg == 0 || args[i].pass_on_stack)
{
rtx before_arg = get_last_insn ();

@@ -3105,6 +3228,17 @@ expand_call (tree exp, rtx target, int ignore)
sibcall_failure = 1;
}

+ /* Store all bounds not passed in registers. */
+ for (i = 0; i < num_actuals; i++)
+ {
+ if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ store_bounds (&args[i],
+ args[i].pointer_arg == -1
+ ? NULL
+ : &args[args[i].pointer_arg]);
+ }
+
/* If we pushed args in forward order, perform stack alignment
after pushing the last arg. */
if (!PUSH_ARGS_REVERSED && argblock == 0)
@@ -3502,6 +3636,9 @@ expand_call (tree exp, rtx target, int ignore)

free (stack_usage_map_buf);

+ /* Join result with returned bounds so caller may use them if needed. */
+ target = chkp_join_splitted_slot (target, valbnd);
+
return target;
}

@@ -4380,6 +4517,68 @@ emit_library_call_value (rtx orgfun, rtx value,
return result;
}

+
+/* Store pointer bounds argument ARG into Bounds Table entry
+ associated with PARM. */
+static void
+store_bounds (struct arg_data *arg, struct arg_data *parm)
+{
+ rtx slot = NULL, ptr = NULL, addr = NULL;
+
+ /* We may pass bounds not associated with any pointer. */
+ if (!parm)
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+ ptr = const0_rtx;
+ }
+ /* Find pointer associated with bounds and where it is
+ passed. */
+ else
+ {
+ if (!parm->reg)
+ {
+ gcc_assert (!arg->special_slot);
+
+ addr = adjust_address (parm->stack, Pmode, arg->pointer_offset);
+ }
+ else if (REG_P (parm->reg))
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (MEM_P (parm->value))
+ addr = adjust_address (parm->value, Pmode, arg->pointer_offset);
+ else if (REG_P (parm->value))
+ ptr = gen_rtx_SUBREG (Pmode, parm->value, arg->pointer_offset);
+ else
+ {
+ gcc_assert (!arg->pointer_offset);
+ ptr = parm->value;
+ }
+ }
+ else
+ {
+ gcc_assert (GET_CODE (parm->reg) == PARALLEL);
+
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (parm->parallel_value)
+ ptr = chkp_get_value_with_offs (parm->parallel_value,
+ GEN_INT (arg->pointer_offset));
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ /* Expand bounds. */
+ if (!arg->value)
+ arg->value = expand_normal (arg->tree_value);
+
+ targetm.calls.store_bounds_for_arg (ptr, addr, arg->value, slot);
+}
+
/* Store a single argument for a function call
into the register or memory area where it must be passed.
*ARG describes the argument value and where to pass it.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index b7f6360..1c75586 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -73,6 +73,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-address.h"
#include "recog.h"
#include "output.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -2238,6 +2240,7 @@ expand_call_stmt (gimple stmt)
CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt);
CALL_EXPR_VA_ARG_PACK (exp) = gimple_call_va_arg_pack_p (stmt);
SET_EXPR_LOCATION (exp, gimple_location (stmt));
+ CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);

/* Ensure RTL is created for debug args. */
if (decl && DECL_HAS_DEBUG_ARGS_P (decl))
@@ -3048,11 +3051,12 @@ expand_value_return (rtx val)
from the current function. */

static void
-expand_return (tree retval)
+expand_return (tree retval, tree bounds)
{
rtx result_rtl;
rtx val = 0;
tree retval_rhs;
+ rtx bounds_rtl;

/* If function wants no value, give it none. */
if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
@@ -3078,6 +3082,56 @@ expand_return (tree retval)

result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));

+ /* Put returned bounds to the right place. */
+ bounds_rtl = DECL_BOUNDS_RTL (DECL_RESULT (current_function_decl));
+ if (bounds_rtl)
+ {
+ rtx addr, bnd;
+
+ if (bounds)
+ {
+ bnd = expand_normal (bounds);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else if (REG_P (bounds_rtl))
+ {
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+ bnd = targetm.calls.load_bounds_for_arg (addr, NULL, NULL);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else
+ {
+ int n;
+
+ gcc_assert (GET_CODE (bounds_rtl) == PARALLEL);
+
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ for (n = 0; n < XVECLEN (bounds_rtl, 0); n++)
+ {
+ rtx offs = XEXP (XVECEXP (bounds_rtl, 0, n), 1);
+ rtx slot = XEXP (XVECEXP (bounds_rtl, 0, n), 0);
+ rtx from = adjust_address (addr, Pmode, INTVAL (offs));
+ rtx bnd = targetm.calls.load_bounds_for_arg (from, NULL, NULL);
+ targetm.calls.store_returned_bounds (slot, bnd);
+ }
+ }
+ }
+ else if (chkp_function_instrumented_p (current_function_decl)
+ && !BOUNDED_P (retval_rhs)
+ && chkp_type_has_pointer (TREE_TYPE (retval_rhs))
+ && TREE_CODE (retval_rhs) != RESULT_DECL)
+ {
+ rtx addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ gcc_assert (MEM_P (result_rtl));
+
+ chkp_copy_bounds_for_stack_parm (result_rtl, addr, TREE_TYPE (retval_rhs));
+ }
+
/* If we are returning the RESULT_DECL, then the value has already
been stored into it, so we don't have to do anything special. */
if (TREE_CODE (retval_rhs) == RESULT_DECL)
@@ -3183,7 +3237,7 @@ expand_gimple_stmt_1 (gimple stmt)
if (!op0)
expand_null_return ();
else
- expand_return (op0);
+ expand_return (op0, gimple_return_retbnd (stmt));
break;

case GIMPLE_ASSIGN:
@@ -5556,6 +5610,9 @@ gimple_expand_cfg (void)

rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun));

+ if (chkp_function_instrumented_p (current_function_decl))
+ chkp_reset_rtl_bounds ();
+
insn_locations_init ();
if (!DECL_IS_BUILTIN (current_function_decl))
{
diff --git a/gcc/expr.c b/gcc/expr.c
index 72e4401..fe76553 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -67,6 +67,8 @@ along with GCC; see the file COPYING3. If not see
#include "params.h"
#include "tree-ssa-address.h"
#include "cfgexpand.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Decide whether a function's arguments should be processed
from first to last or from last to first.
@@ -4917,11 +4919,11 @@ expand_assignment (tree to, tree from, bool nontemporal)
if (COMPLEX_MODE_P (TYPE_MODE (TREE_TYPE (from)))
&& bitpos == 0
&& bitsize == mode_bitsize)
- result = store_expr (from, to_rtx, false, nontemporal);
+ result = store_expr (from, to_rtx, false, nontemporal, NULL);
else if (bitsize == mode_bitsize / 2
&& (bitpos == 0 || bitpos == mode_bitsize / 2))
result = store_expr (from, XEXP (to_rtx, bitpos != 0), false,
- nontemporal);
+ nontemporal, NULL);
else if (bitpos + bitsize <= mode_bitsize / 2)
result = store_field (XEXP (to_rtx, 0), bitsize, bitpos,
bitregion_start, bitregion_end,
@@ -5008,9 +5010,14 @@ expand_assignment (tree to, tree from, bool nontemporal)
|| TREE_CODE (to) == SSA_NAME))
{
rtx value;
+ rtx bounds;

push_temp_slots ();
value = expand_normal (from);
+
+ /* Split value and bounds to store them separately. */
+ chkp_split_slot (value, &value, &bounds);
+
if (to_rtx == 0)
to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);

@@ -5044,6 +5051,15 @@ expand_assignment (tree to, tree from, bool nontemporal)

emit_move_insn (to_rtx, value);
}
+
+ /* Store bounds if required. */
+ if (bounds
+ && (BOUNDED_P (to) || chkp_type_has_pointer (TREE_TYPE (to))))
+ {
+ gcc_assert (MEM_P (to_rtx));
+ chkp_emit_bounds_store (bounds, value, to_rtx);
+ }
+
preserve_temp_slots (to_rtx);
pop_temp_slots ();
return;
@@ -5119,7 +5135,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
/* Compute FROM and store the value in the rtx we got. */

push_temp_slots ();
- result = store_expr (from, to_rtx, 0, nontemporal);
+ result = store_expr (from, to_rtx, 0, nontemporal, to);
preserve_temp_slots (result);
pop_temp_slots ();
return;
@@ -5156,10 +5172,14 @@ emit_storent_insn (rtx to, rtx from)
If CALL_PARAM_P is nonzero, this is a store into a call param on the
stack, and block moves may need to be treated specially.

- If NONTEMPORAL is true, try using a nontemporal store instruction. */
+ If NONTEMPORAL is true, try using a nontemporal store instruction.
+
+ If BTARGET is not NULL then computed bounds of TARGET are
+ associated with BTARGET. */

rtx
-store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+store_expr (tree exp, rtx target, int call_param_p, bool nontemporal,
+ tree btarget)
{
rtx temp;
rtx alt_rtl = NULL_RTX;
@@ -5181,7 +5201,7 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
return store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ nontemporal, btarget);
}
else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
{
@@ -5196,12 +5216,12 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1, -1);
store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ nontemporal, btarget);
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
store_expr (TREE_OPERAND (exp, 2), target, call_param_p,
- nontemporal);
+ nontemporal, btarget);
emit_label (lab2);
OK_DEFER_POP;

@@ -5253,6 +5273,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
temp = expand_expr (exp, inner_target, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);

+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
+
/* If TEMP is a VOIDmode constant, use convert_modes to make
sure that we properly convert it. */
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
@@ -5334,6 +5367,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
(call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL),
&alt_rtl, false);
+
+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
}

/* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
@@ -6222,7 +6268,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
VAR_DECL, NULL_TREE, domain);
index_r = gen_reg_rtx (promote_decl_mode (index, NULL));
SET_DECL_RTL (index, index_r);
- store_expr (lo_index, index_r, 0, false);
+ store_expr (lo_index, index_r, 0, false, NULL);

/* Build the head of the loop. */
do_pending_stack_adjust ();
@@ -6249,7 +6295,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
store_constructor (value, xtarget, cleared,
bitsize / BITS_PER_UNIT);
else
- store_expr (value, xtarget, 0, false);
+ store_expr (value, xtarget, 0, false, NULL);

/* Generate a conditional jump to exit the loop. */
exit_cond = build2 (LT_EXPR, integer_type_node,
@@ -6292,7 +6338,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
expand_normal (position),
highest_pow2_factor (position));
xtarget = adjust_address (xtarget, mode, 0);
- store_expr (value, xtarget, 0, false);
+ store_expr (value, xtarget, 0, false, NULL);
}
else
{
@@ -6498,7 +6544,7 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
/* We're storing into a struct containing a single __complex. */

gcc_assert (!bitpos);
- return store_expr (exp, target, 0, nontemporal);
+ return store_expr (exp, target, 0, nontemporal, NULL);
}

/* If the structure is in a register or if the component
@@ -6651,7 +6697,7 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
if (!MEM_KEEP_ALIAS_SET_P (to_rtx) && MEM_ALIAS_SET (to_rtx) != 0)
set_mem_alias_set (to_rtx, alias_set);

- return store_expr (exp, to_rtx, 0, nontemporal);
+ return store_expr (exp, to_rtx, 0, nontemporal, NULL);
}
}

@@ -8141,7 +8187,7 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
store_expr (treeop0,
adjust_address (target, TYPE_MODE (valtype), 0),
modifier == EXPAND_STACK_PARM,
- false);
+ false, NULL);

else
{
@@ -9189,14 +9235,14 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
jumpifnot (treeop0, op0, -1);
store_expr (treeop1, temp,
modifier == EXPAND_STACK_PARM,
- false);
+ false, NULL);

emit_jump_insn (gen_jump (op1));
emit_barrier ();
emit_label (op0);
store_expr (treeop2, temp,
modifier == EXPAND_STACK_PARM,
- false);
+ false, NULL);

emit_label (op1);
OK_DEFER_POP;
@@ -9722,7 +9768,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
{
temp = assign_stack_temp (DECL_MODE (base),
GET_MODE_SIZE (DECL_MODE (base)));
- store_expr (base, temp, 0, false);
+ store_expr (base, temp, 0, false, NULL);
temp = adjust_address (temp, BLKmode, offset);
set_mem_size (temp, int_size_in_bytes (type));
return temp;
diff --git a/gcc/expr.h b/gcc/expr.h
index 524da67..bbb29bc 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -432,7 +432,7 @@ extern void expand_assignment (tree, tree, bool);
and storing the value into TARGET.
If SUGGEST_REG is nonzero, copy the value through a register
and return that register, if that is possible. */
-extern rtx store_expr (tree, rtx, int, bool);
+extern rtx store_expr (tree, rtx, int, bool, tree);

/* Given an rtx that may include add and multiply operations,
generate them as insns and return a pseudo-reg containing the value.
diff --git a/gcc/function.c b/gcc/function.c
index a61e475..a08d4ad 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3. If not see
#include "df.h"
#include "params.h"
#include "bb-reorder.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* So we can assign to cfun in this file. */
#undef cfun
@@ -2082,6 +2084,14 @@ use_register_for_decl (const_tree decl)
if (TREE_ADDRESSABLE (decl))
return false;

+ /* Decl is implicitly addressible by bound stores and loads
+ if it is an aggregate holding bounds. */
+ if (chkp_function_instrumented_p (current_function_decl)
+ && TREE_TYPE (decl)
+ && !BOUNDED_P (decl)
+ && chkp_type_has_pointer (TREE_TYPE (decl)))
+ return false;
+
/* Only register-like things go in registers. */
if (DECL_MODE (decl) == BLKmode)
return false;
@@ -2202,6 +2212,15 @@ struct assign_parm_data_one
BOOL_BITFIELD loaded_in_reg : 1;
};

+struct bounds_parm_data
+{
+ assign_parm_data_one parm_data;
+ tree bounds_parm;
+ tree ptr_parm;
+ rtx ptr_entry;
+ int bound_no;
+};
+
/* A subroutine of assign_parms. Initialize ALL. */

static void
@@ -2312,6 +2331,23 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
fnargs.safe_insert (0, decl);

all->function_result_decl = decl;
+
+ /* If function is instrumented then bounds of the
+ passed structure address is the second argument. */
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ decl = build_decl (DECL_SOURCE_LOCATION (fndecl),
+ PARM_DECL, get_identifier (".result_bnd"),
+ pointer_bounds_type_node);
+ DECL_ARG_TYPE (decl) = pointer_bounds_type_node;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_NAMELESS (decl) = 1;
+ TREE_CONSTANT (decl) = 1;
+
+ DECL_CHAIN (decl) = DECL_CHAIN (all->orig_fnargs);
+ DECL_CHAIN (all->orig_fnargs) = decl;
+ fnargs.safe_insert (1, decl);
+ }
}

/* If the target wants to split complex arguments into scalars, do so. */
@@ -2452,7 +2488,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
In this case, we call FUNCTION_ARG with NAMED set to 1 instead of 0
as it was the previous time. */
- in_regs = entry_parm != 0;
+ in_regs = (entry_parm != 0) || POINTER_BOUNDS_TYPE_P (data->passed_type);
#ifdef STACK_PARMS_IN_REG_PARM_AREA
in_regs = true;
#endif
@@ -2541,8 +2577,12 @@ static bool
assign_parm_is_stack_parm (struct assign_parm_data_all *all,
struct assign_parm_data_one *data)
{
+ /* Bounds are never passed on the stack to keep compatibility
+ with not instrumented code. */
+ if (POINTER_BOUNDS_TYPE_P (data->passed_type))
+ return false;
/* Trivially true if we've no incoming register. */
- if (data->entry_parm == NULL)
+ else if (data->entry_parm == NULL)
;
/* Also true if we're partially in registers and partially not,
since we've arranged to drop the entire argument on the stack. */
@@ -3348,6 +3388,119 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all,
}
}

+/* Load bounds PARM from bounds table. */
+static void
+assign_parm_load_bounds (struct assign_parm_data_one *data,
+ tree parm,
+ rtx entry,
+ unsigned bound_no)
+{
+ bitmap_iterator bi;
+ unsigned i, offs = 0;
+ int bnd_no = -1;
+ rtx slot = NULL, ptr = NULL;
+
+ if (parm)
+ {
+ bitmap slots = chkp_find_bound_slots (TREE_TYPE (parm));
+ EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi)
+ {
+ if (bound_no)
+ bound_no--;
+ else
+ {
+ bnd_no = i;
+ break;
+ }
+ }
+ BITMAP_FREE (slots);
+ }
+
+ /* We may have bounds not associated with any pointer. */
+ if (bnd_no != -1)
+ offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ /* Find associated pointer. */
+ if (bnd_no == -1)
+ {
+ /* If bounds are not associated with any bounds,
+ then it is passed in a register or special slot. */
+ gcc_assert (data->entry_parm);
+ ptr = const0_rtx;
+ }
+ else if (MEM_P (entry))
+ slot = adjust_address (entry, Pmode, offs);
+ else if (REG_P (entry))
+ ptr = gen_rtx_REG (Pmode, REGNO (entry) + bnd_no);
+ else if (GET_CODE (entry) == PARALLEL)
+ ptr = chkp_get_value_with_offs (entry, GEN_INT (offs));
+ else
+ gcc_unreachable ();
+ data->entry_parm = targetm.calls.load_bounds_for_arg (slot, ptr,
+ data->entry_parm);
+}
+
+/* Assign RTL expressions to the function's bounds parameters BNDARGS. */
+
+static void
+assign_bounds (vec<bounds_parm_data> &bndargs,
+ struct assign_parm_data_all &all)
+{
+ unsigned i, pass, handled = 0;
+ bounds_parm_data *pbdata;
+
+ if (!bndargs.exists ())
+ return;
+
+ /* We make few passes to store input bounds. Firstly handle bounds
+ passed in registers. After that we load bounds passed in special
+ slots. Finally we load bounds from Bounds Table. */
+ for (pass = 0; pass < 3; pass++)
+ FOR_EACH_VEC_ELT (bndargs, i, pbdata)
+ {
+ /* Pass 0 => regs only. */
+ if (pass == 0
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG))
+ continue;
+ /* Pass 1 => slots only. */
+ else if (pass == 1
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) == REG))
+ continue;
+ /* Pass 2 => BT only. */
+ else if (pass == 2
+ && pbdata->parm_data.entry_parm)
+ continue;
+
+ if (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG)
+ assign_parm_load_bounds (&pbdata->parm_data, pbdata->ptr_parm,
+ pbdata->ptr_entry, pbdata->bound_no);
+
+ set_decl_incoming_rtl (pbdata->bounds_parm,
+ pbdata->parm_data.entry_parm, false);
+
+ if (assign_parm_setup_block_p (&pbdata->parm_data))
+ assign_parm_setup_block (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else if (pbdata->parm_data.passed_pointer
+ || use_register_for_decl (pbdata->bounds_parm))
+ assign_parm_setup_reg (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else
+ assign_parm_setup_stack (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+
+ /* Count handled bounds to make sure we miss nothing. */
+ handled++;
+ }
+
+ gcc_assert (handled == bndargs.length ());
+
+ bndargs.release ();
+}
+
/* Assign RTL expressions to the function's parameters. This may involve
copying them into registers and using those registers as the DECL_RTL. */

@@ -3357,7 +3510,11 @@ assign_parms (tree fndecl)
struct assign_parm_data_all all;
tree parm;
vec<tree> fnargs;
- unsigned i;
+ unsigned i, bound_no = 0;
+ tree last_arg = NULL;
+ rtx last_arg_entry = NULL;
+ vec<bounds_parm_data> bndargs = vNULL;
+ bounds_parm_data bdata;

crtl->args.internal_arg_pointer
= targetm.calls.internal_arg_pointer ();
@@ -3399,9 +3556,6 @@ assign_parms (tree fndecl)
}
}

- if (cfun->stdarg && !DECL_CHAIN (parm))
- assign_parms_setup_varargs (&all, &data, false);
-
/* Find out where the parameter arrives in this function. */
assign_parm_find_entry_rtl (&all, &data);

@@ -3411,7 +3565,15 @@ assign_parms (tree fndecl)
assign_parm_find_stack_rtl (parm, &data);
assign_parm_adjust_entry_rtl (&data);
}
-
+ if (!POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Remember where last non bounds arg was passed in case
+ we have to load associated bounds for it from Bounds
+ Table. */
+ last_arg = parm;
+ last_arg_entry = data.entry_parm;
+ bound_no = 0;
+ }
/* Record permanently how this parm was passed. */
if (data.passed_pointer)
{
@@ -3423,20 +3585,63 @@ assign_parms (tree fndecl)
else
set_decl_incoming_rtl (parm, data.entry_parm, false);

+ /* Boudns should be loaded in the particular order to
+ have registers allocated correctly. Collect info about
+ input bounds and load them later. */
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Expect bounds in instrumented functions only. */
+ gcc_assert (chkp_function_instrumented_p (fndecl));
+
+ bdata.parm_data = data;
+ bdata.bounds_parm = parm;
+ bdata.ptr_parm = last_arg;
+ bdata.ptr_entry = last_arg_entry;
+ bdata.bound_no = bound_no;
+ bndargs.safe_push (bdata);
+ }
+ else
+ {
+ assign_parm_adjust_stack_rtl (&data);
+
+ if (assign_parm_setup_block_p (&data))
+ assign_parm_setup_block (&all, parm, &data);
+ else if (data.passed_pointer || use_register_for_decl (parm))
+ assign_parm_setup_reg (&all, parm, &data);
+ else
+ assign_parm_setup_stack (&all, parm, &data);
+ }
+
+ if (cfun->stdarg && !DECL_CHAIN (parm))
+ {
+ int pretend_bytes = 0;
+
+ assign_parms_setup_varargs (&all, &data, false);
+
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ /* We expect this is the last parm. Otherwise it is wrong
+ to assign bounds right now. */
+ gcc_assert (i == (fnargs.length () - 1));
+ assign_bounds (bndargs, all);
+ targetm.calls.setup_incoming_vararg_bounds (all.args_so_far,
+ data.promoted_mode,
+ data.passed_type,
+ &pretend_bytes,
+ false);
+ }
+ }
+
/* Update info on where next arg arrives in registers. */
targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
data.passed_type, data.named_arg);

- assign_parm_adjust_stack_rtl (&data);
-
- if (assign_parm_setup_block_p (&data))
- assign_parm_setup_block (&all, parm, &data);
- else if (data.passed_pointer || use_register_for_decl (parm))
- assign_parm_setup_reg (&all, parm, &data);
- else
- assign_parm_setup_stack (&all, parm, &data);
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ bound_no++;
}

+ assign_bounds (bndargs, all);
+
if (targetm.calls.split_complex_arg)
assign_parms_unsplit_complex (&all, fnargs);

@@ -3557,6 +3762,10 @@ assign_parms (tree fndecl)

real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result),
fndecl, true);
+ if (chkp_function_instrumented_p (fndecl))
+ crtl->return_bnd
+ = targetm.calls.chkp_function_value_bounds (TREE_TYPE (decl_result),
+ fndecl, true);
REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
/* The delay slot scheduler assumes that crtl->return_rtx
holds the hard register containing the return value, not a
@@ -4778,6 +4987,14 @@ expand_function_start (tree subr)
/* Set DECL_REGISTER flag so that expand_function_end will copy the
result to the real return register(s). */
DECL_REGISTER (DECL_RESULT (subr)) = 1;
+
+ if (chkp_function_instrumented_p (current_function_decl))
+ {
+ tree return_type = TREE_TYPE (DECL_RESULT (subr));
+ rtx bounds = targetm.calls.chkp_function_value_bounds (return_type,
+ subr, 1);
+ SET_DECL_BOUNDS_RTL (DECL_RESULT (subr), bounds);
+ }
}

/* Initialize rtx for parameters and local variables.
@@ -4867,14 +5084,11 @@ expand_dummy_function_end (void)
in_dummy_function = false;
}

-/* Call DOIT for each hard register used as a return value from
- the current function. */
+/* Helper for diddle_return_value. */

void
-diddle_return_value (void (*doit) (rtx, void *), void *arg)
+diddle_return_value_1 (void (*doit) (rtx, void *), void *arg, rtx outgoing)
{
- rtx outgoing = crtl->return_rtx;
-
if (! outgoing)
return;

@@ -4894,6 +5108,16 @@ diddle_return_value (void (*doit) (rtx, void *), void *arg)
}
}

+/* Call DOIT for each hard register used as a return value from
+ the current function. */
+
+void
+diddle_return_value (void (*doit) (rtx, void *), void *arg)
+{
+ diddle_return_value_1 (doit, arg, crtl->return_rtx);
+ diddle_return_value_1 (doit, arg, crtl->return_bnd);
+}
+
static void
do_clobber_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED)
{
diff --git a/gcc/function.h b/gcc/function.h
index 38a0fc4..736bb02 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -252,6 +252,9 @@ struct GTY(()) rtl_data {
result in a register, current_function_return_rtx will always be
the hard register containing the result. */
rtx return_rtx;
+ /* If nonxero, an RTL expression for the lcoation at which the current
+ function returns bounds for its result. */
+ rtx return_bnd;

/* Vector of initial-value pairs. Each pair consists of a pseudo
register of approprite mode that stores the initial value a hard
diff --git a/gcc/tree-outof-ssa.c b/gcc/tree-outof-ssa.c
index d5a635b..07f336b 100644
--- a/gcc/tree-outof-ssa.c
+++ b/gcc/tree-outof-ssa.c
@@ -313,7 +313,7 @@ insert_value_copy_on_edge (edge e, int dest, tree src, source_location locus)
else if (src_mode == BLKmode)
{
x = SA.partition_to_pseudo[dest];
- store_expr (src, x, 0, false);
+ store_expr (src, x, 0, false, NULL);
}
else
x = expand_expr (src, SA.partition_to_pseudo[dest],
Michael Matz
2014-06-02 15:28:06 UTC
Permalink
Hi,
Post by Ilya Enkovich
This patch adds support for input bounds, call bounds args and returned
bounds in expand pass.
* expr.h (store_expr): Add param for bounds target.
There is exactly one place (except for the self-recursive ones) where you
call the new store_expr with a non-null argument for bounds target, and it
seems to be only necessary for when some sub-expression of the RHS is a
call. Can you somehow arrange to move that handling to the single place
in expand_assignment() so that you don't need to change the signature of
store_expr?


Ciao,
Michael.
Ilya Enkovich
2014-06-02 15:55:37 UTC
Permalink
Post by Michael Matz
Hi,
Post by Ilya Enkovich
This patch adds support for input bounds, call bounds args and returned
bounds in expand pass.
* expr.h (store_expr): Add param for bounds target.
There is exactly one place (except for the self-recursive ones) where you
call the new store_expr with a non-null argument for bounds target, and it
seems to be only necessary for when some sub-expression of the RHS is a
call. Can you somehow arrange to move that handling to the single place
in expand_assignment() so that you don't need to change the signature of
store_expr?
I see the only nice way to do it - store_expr should return bounds of
expanded exp. Currently it always return NULL_RTX. Does it look better
than a new argument?

Ilya
Post by Michael Matz
Ciao,
Michael.
Michael Matz
2014-06-04 14:36:31 UTC
Permalink
Hi,
Post by Ilya Enkovich
Post by Michael Matz
There is exactly one place (except for the self-recursive ones) where
you call the new store_expr with a non-null argument for bounds
target, and it seems to be only necessary for when some sub-expression
of the RHS is a call. Can you somehow arrange to move that handling
to the single place in expand_assignment() so that you don't need to
change the signature of store_expr?
I see the only nice way to do it - store_expr should return bounds of
expanded exp. Currently it always return NULL_RTX. Does it look better
than a new argument?
IMHO it does. That or introducing a new store_expr_with_bounds (with the
new argument) and letting store_expr be a wrapper for that, passing the
NULL. Basically anything that avoids adding a new parameter for most of
the existing calls to store_expr.


Ciao,
Michael.
Ilya Enkovich
2014-06-05 14:46:22 UTC
Permalink
Post by Michael Matz
Hi,
Post by Ilya Enkovich
Post by Michael Matz
There is exactly one place (except for the self-recursive ones) where
you call the new store_expr with a non-null argument for bounds
target, and it seems to be only necessary for when some sub-expression
of the RHS is a call. Can you somehow arrange to move that handling
to the single place in expand_assignment() so that you don't need to
change the signature of store_expr?
I see the only nice way to do it - store_expr should return bounds of
expanded exp. Currently it always return NULL_RTX. Does it look better
than a new argument?
IMHO it does. That or introducing a new store_expr_with_bounds (with the
new argument) and letting store_expr be a wrapper for that, passing the
NULL. Basically anything that avoids adding a new parameter for most of
the existing calls to store_expr.
Ciao,
Michael.
Here is an updated version using store_expr_with_bounds and store_expr as a wrapper for it.

Bootstrapped and tested on linux-x86_64.

Thanks,
Ilya
--
gcc/

2014-06-05 Ilya Enkovich <***@intel.com>

* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.


diff --git a/gcc/calls.c b/gcc/calls.c
index e1dc8eb..5fbbe9f 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,11 +44,14 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -76,6 +79,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
+ /* For pointer bounds hold an index of parm bounds are bound to. -1 if
+ there is no such pointer. */
+ int pointer_arg;
+ /* If pointer_arg refers a structure, then pointer_offset holds an offset
+ of a pointer in this structure. */
+ int pointer_offset;
/* If REG was promoted from the actual mode of the argument expression,
indicates whether the promotion is sign- or zero-extended. */
int unsignedp;
@@ -133,6 +145,7 @@ static void emit_call_1 (rtx, tree, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT,
HOST_WIDE_INT, rtx, rtx, int, rtx, int,
cumulative_args_t);
static void precompute_register_parameters (int, struct arg_data *, int *);
+static void store_bounds (struct arg_data *, struct arg_data *);
static int store_one_arg (struct arg_data *, rtx, int, int, int);
static void store_unaligned_arguments_into_pseudos (struct arg_data *, int);
static int finalize_must_preallocate (int, int, struct arg_data *,
@@ -396,6 +409,10 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
&& MEM_EXPR (funmem) != NULL_TREE)
set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem));

+ /* Mark instrumented calls. */
+ if (call && fntree)
+ CALL_EXPR_WITH_BOUNDS_P (call) = CALL_WITH_BOUNDS_P (fntree);
+
/* Put the register usage information there. */
add_function_usage_to (call_insn, call_fusage);

@@ -1141,18 +1158,84 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* First fill in the actual arguments in the ARGS array, splitting
complex arguments if necessary. */
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;

if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
+
j += inc;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr (struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j - inc;
+
+ j += inc;
+ }
}
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
{
tree argtype = TREE_TYPE (arg);
+
+ /* Remember last param with pointer and associate it
+ with following pointer bounds. */
+ if (CALL_WITH_BOUNDS_P (exp)
+ && chkp_type_has_pointer (argtype))
+ {
+ if (slots)
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ ptr_arg = j;
+ if (!BOUNDED_TYPE_P (argtype))
+ slots = chkp_find_bound_slots (argtype);
+ }
+ else if (POINTER_BOUNDS_TYPE_P (argtype))
+ {
+ /* We expect bounds in instrumented calls only.
+ Otherwise it is a sign we lost flag due to some optimization
+ and may emit call args incorrectly. */
+ gcc_assert (CALL_WITH_BOUNDS_P (exp));
+
+ /* For structures look for the next available pointer. */
+ if (ptr_arg != -1 && slots)
+ {
+ unsigned bnd_no = bitmap_first_set_bit (slots);
+ args[j].pointer_offset =
+ bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ bitmap_clear_bit (slots, bnd_no);
+
+ /* Check we have no more pointers in the structure. */
+ if (bitmap_empty_p (slots))
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ }
+ args[j].pointer_arg = ptr_arg;
+
+ /* Check we covered all pointers in the previous
+ non bounds arg. */
+ if (!slots)
+ ptr_arg = -1;
+ }
+ else
+ ptr_arg = -1;
+
if (targetm.calls.split_complex_arg
&& argtype
&& TREE_CODE (argtype) == COMPLEX_TYPE
@@ -1167,6 +1250,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[j].tree_value = arg;
j += inc;
}
+
+ if (slots)
+ BITMAP_FREE (slots);
}

/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
@@ -1302,6 +1388,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);

+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
+
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
@@ -1335,10 +1427,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;

+ /* No stack allocation and padding for bounds. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ ;
/* Compute the stack-size of this argument. */
- if (args[i].reg == 0 || args[i].partial != 0
- || reg_parm_stack_space > 0
- || args[i].pass_on_stack)
+ else if (args[i].reg == 0 || args[i].partial != 0
+ || reg_parm_stack_space > 0
+ || args[i].pass_on_stack)
locate_and_pad_parm (mode, type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
@@ -1553,6 +1648,12 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
partial_seen = 1;
else if (partial_seen && args[i].reg == 0)
must_preallocate = 1;
+ /* We preallocate in case there are bounds passed
+ in the bounds table to have precomputed address
+ for bounds association. */
+ else if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ must_preallocate = 1;

if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
&& (TREE_CODE (args[i].tree_value) == CALL_EXPR
@@ -1604,6 +1705,10 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
&& args[i].partial == 0)
continue;

+ /* Pointer Bounds are never passed on the stack. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+
if (CONST_INT_P (offset))
addr = plus_constant (Pmode, arg_reg, INTVAL (offset));
else
@@ -2233,6 +2338,8 @@ expand_call (tree exp, rtx target, int ignore)
/* Register in which non-BLKmode value will be returned,
or 0 if no value or if value is BLKmode. */
rtx valreg;
+ /* Register(s) in which bounds are returned. */
+ rtx valbnd = NULL;
/* Address where we should return a BLKmode value;
0 if value not BLKmode. */
rtx structure_value_addr = 0;
@@ -2484,7 +2591,7 @@ expand_call (tree exp, rtx target, int ignore)

structure_value_addr_value =
make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
- structure_value_addr_parm = 1;
+ structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1;
}

/* Count the arguments and set NUM_ACTUALS. */
@@ -3003,15 +3110,28 @@ expand_call (tree exp, rtx target, int ignore)

/* Figure out the register where the value, if any, will come back. */
valreg = 0;
+ valbnd = 0;
if (TYPE_MODE (rettype) != VOIDmode
&& ! structure_value_addr)
{
if (pcc_struct_value)
- valreg = hard_function_value (build_pointer_type (rettype),
- fndecl, NULL, (pass == 0));
+ {
+ valreg = hard_function_value (build_pointer_type (rettype),
+ fndecl, NULL, (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.
+ chkp_function_value_bounds (build_pointer_type (rettype),
+ fndecl, (pass == 0));
+ }
else
- valreg = hard_function_value (rettype, fndecl, fntype,
- (pass == 0));
+ {
+ valreg = hard_function_value (rettype, fndecl, fntype,
+ (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.chkp_function_value_bounds (rettype,
+ fndecl,
+ (pass == 0));
+ }

/* If VALREG is a PARALLEL whose first member has a zero
offset, use that. This is for targets such as m68k that
@@ -3052,7 +3172,10 @@ expand_call (tree exp, rtx target, int ignore)

for (i = 0; i < num_actuals; i++)
{
- if (args[i].reg == 0 || args[i].pass_on_stack)
+ /* Delay bounds until all other args are stored. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+ else if (args[i].reg == 0 || args[i].pass_on_stack)
{
rtx before_arg = get_last_insn ();

@@ -3105,6 +3228,17 @@ expand_call (tree exp, rtx target, int ignore)
sibcall_failure = 1;
}

+ /* Store all bounds not passed in registers. */
+ for (i = 0; i < num_actuals; i++)
+ {
+ if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ store_bounds (&args[i],
+ args[i].pointer_arg == -1
+ ? NULL
+ : &args[args[i].pointer_arg]);
+ }
+
/* If we pushed args in forward order, perform stack alignment
after pushing the last arg. */
if (!PUSH_ARGS_REVERSED && argblock == 0)
@@ -3502,6 +3636,9 @@ expand_call (tree exp, rtx target, int ignore)

free (stack_usage_map_buf);

+ /* Join result with returned bounds so caller may use them if needed. */
+ target = chkp_join_splitted_slot (target, valbnd);
+
return target;
}

@@ -4380,6 +4517,68 @@ emit_library_call_value (rtx orgfun, rtx value,
return result;
}

+
+/* Store pointer bounds argument ARG into Bounds Table entry
+ associated with PARM. */
+static void
+store_bounds (struct arg_data *arg, struct arg_data *parm)
+{
+ rtx slot = NULL, ptr = NULL, addr = NULL;
+
+ /* We may pass bounds not associated with any pointer. */
+ if (!parm)
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+ ptr = const0_rtx;
+ }
+ /* Find pointer associated with bounds and where it is
+ passed. */
+ else
+ {
+ if (!parm->reg)
+ {
+ gcc_assert (!arg->special_slot);
+
+ addr = adjust_address (parm->stack, Pmode, arg->pointer_offset);
+ }
+ else if (REG_P (parm->reg))
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (MEM_P (parm->value))
+ addr = adjust_address (parm->value, Pmode, arg->pointer_offset);
+ else if (REG_P (parm->value))
+ ptr = gen_rtx_SUBREG (Pmode, parm->value, arg->pointer_offset);
+ else
+ {
+ gcc_assert (!arg->pointer_offset);
+ ptr = parm->value;
+ }
+ }
+ else
+ {
+ gcc_assert (GET_CODE (parm->reg) == PARALLEL);
+
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (parm->parallel_value)
+ ptr = chkp_get_value_with_offs (parm->parallel_value,
+ GEN_INT (arg->pointer_offset));
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ /* Expand bounds. */
+ if (!arg->value)
+ arg->value = expand_normal (arg->tree_value);
+
+ targetm.calls.store_bounds_for_arg (ptr, addr, arg->value, slot);
+}
+
/* Store a single argument for a function call
into the register or memory area where it must be passed.
*ARG describes the argument value and where to pass it.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index b7f6360..1c75586 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -73,6 +73,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-address.h"
#include "recog.h"
#include "output.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -2238,6 +2240,7 @@ expand_call_stmt (gimple stmt)
CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt);
CALL_EXPR_VA_ARG_PACK (exp) = gimple_call_va_arg_pack_p (stmt);
SET_EXPR_LOCATION (exp, gimple_location (stmt));
+ CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);

/* Ensure RTL is created for debug args. */
if (decl && DECL_HAS_DEBUG_ARGS_P (decl))
@@ -3048,11 +3051,12 @@ expand_value_return (rtx val)
from the current function. */

static void
-expand_return (tree retval)
+expand_return (tree retval, tree bounds)
{
rtx result_rtl;
rtx val = 0;
tree retval_rhs;
+ rtx bounds_rtl;

/* If function wants no value, give it none. */
if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
@@ -3078,6 +3082,56 @@ expand_return (tree retval)

result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));

+ /* Put returned bounds to the right place. */
+ bounds_rtl = DECL_BOUNDS_RTL (DECL_RESULT (current_function_decl));
+ if (bounds_rtl)
+ {
+ rtx addr, bnd;
+
+ if (bounds)
+ {
+ bnd = expand_normal (bounds);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else if (REG_P (bounds_rtl))
+ {
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+ bnd = targetm.calls.load_bounds_for_arg (addr, NULL, NULL);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else
+ {
+ int n;
+
+ gcc_assert (GET_CODE (bounds_rtl) == PARALLEL);
+
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ for (n = 0; n < XVECLEN (bounds_rtl, 0); n++)
+ {
+ rtx offs = XEXP (XVECEXP (bounds_rtl, 0, n), 1);
+ rtx slot = XEXP (XVECEXP (bounds_rtl, 0, n), 0);
+ rtx from = adjust_address (addr, Pmode, INTVAL (offs));
+ rtx bnd = targetm.calls.load_bounds_for_arg (from, NULL, NULL);
+ targetm.calls.store_returned_bounds (slot, bnd);
+ }
+ }
+ }
+ else if (chkp_function_instrumented_p (current_function_decl)
+ && !BOUNDED_P (retval_rhs)
+ && chkp_type_has_pointer (TREE_TYPE (retval_rhs))
+ && TREE_CODE (retval_rhs) != RESULT_DECL)
+ {
+ rtx addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ gcc_assert (MEM_P (result_rtl));
+
+ chkp_copy_bounds_for_stack_parm (result_rtl, addr, TREE_TYPE (retval_rhs));
+ }
+
/* If we are returning the RESULT_DECL, then the value has already
been stored into it, so we don't have to do anything special. */
if (TREE_CODE (retval_rhs) == RESULT_DECL)
@@ -3183,7 +3237,7 @@ expand_gimple_stmt_1 (gimple stmt)
if (!op0)
expand_null_return ();
else
- expand_return (op0);
+ expand_return (op0, gimple_return_retbnd (stmt));
break;

case GIMPLE_ASSIGN:
@@ -5556,6 +5610,9 @@ gimple_expand_cfg (void)

rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun));

+ if (chkp_function_instrumented_p (current_function_decl))
+ chkp_reset_rtl_bounds ();
+
insn_locations_init ();
if (!DECL_IS_BUILTIN (current_function_decl))
{
diff --git a/gcc/expr.c b/gcc/expr.c
index 72e4401..40cf67e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -67,6 +67,8 @@ along with GCC; see the file COPYING3. If not see
#include "params.h"
#include "tree-ssa-address.h"
#include "cfgexpand.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Decide whether a function's arguments should be processed
from first to last or from last to first.
@@ -5008,9 +5010,14 @@ expand_assignment (tree to, tree from, bool nontemporal)
|| TREE_CODE (to) == SSA_NAME))
{
rtx value;
+ rtx bounds;

push_temp_slots ();
value = expand_normal (from);
+
+ /* Split value and bounds to store them separately. */
+ chkp_split_slot (value, &value, &bounds);
+
if (to_rtx == 0)
to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);

@@ -5044,6 +5051,15 @@ expand_assignment (tree to, tree from, bool nontemporal)

emit_move_insn (to_rtx, value);
}
+
+ /* Store bounds if required. */
+ if (bounds
+ && (BOUNDED_P (to) || chkp_type_has_pointer (TREE_TYPE (to))))
+ {
+ gcc_assert (MEM_P (to_rtx));
+ chkp_emit_bounds_store (bounds, value, to_rtx);
+ }
+
preserve_temp_slots (to_rtx);
pop_temp_slots ();
return;
@@ -5119,7 +5135,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
/* Compute FROM and store the value in the rtx we got. */

push_temp_slots ();
- result = store_expr (from, to_rtx, 0, nontemporal);
+ result = store_expr_with_bounds (from, to_rtx, 0, nontemporal, to);
preserve_temp_slots (result);
pop_temp_slots ();
return;
@@ -5156,10 +5172,14 @@ emit_storent_insn (rtx to, rtx from)
If CALL_PARAM_P is nonzero, this is a store into a call param on the
stack, and block moves may need to be treated specially.

- If NONTEMPORAL is true, try using a nontemporal store instruction. */
+ If NONTEMPORAL is true, try using a nontemporal store instruction.
+
+ If BTARGET is not NULL then computed bounds of EXP are
+ associated with BTARGET. */

rtx
-store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+store_expr_with_bounds (tree exp, rtx target, int call_param_p,
+ bool nontemporal, tree btarget)
{
rtx temp;
rtx alt_rtl = NULL_RTX;
@@ -5180,8 +5200,8 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
part. */
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
- return store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ return store_expr_with_bounds (TREE_OPERAND (exp, 1), target,
+ call_param_p, nontemporal, btarget);
}
else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
{
@@ -5195,13 +5215,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
do_pending_stack_adjust ();
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1, -1);
- store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ store_expr_with_bounds (TREE_OPERAND (exp, 1), target, call_param_p,
+ nontemporal, btarget);
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
- store_expr (TREE_OPERAND (exp, 2), target, call_param_p,
- nontemporal);
+ store_expr_with_bounds (TREE_OPERAND (exp, 2), target, call_param_p,
+ nontemporal, btarget);
emit_label (lab2);
OK_DEFER_POP;

@@ -5253,6 +5273,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
temp = expand_expr (exp, inner_target, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);

+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
+
/* If TEMP is a VOIDmode constant, use convert_modes to make
sure that we properly convert it. */
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
@@ -5334,6 +5367,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
(call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL),
&alt_rtl, false);
+
+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
}

/* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
@@ -5498,6 +5544,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)

return NULL_RTX;
}
+
+/* Same as store_expr_with_bounds but ignoring bounds of EXP. */
+rtx
+store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+{
+ return store_expr_with_bounds (exp, target, call_param_p, nontemporal, NULL);
+}

/* Return true if field F of structure TYPE is a flexible array. */

diff --git a/gcc/expr.h b/gcc/expr.h
index 524da67..d06468d 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -432,6 +432,7 @@ extern void expand_assignment (tree, tree, bool);
and storing the value into TARGET.
If SUGGEST_REG is nonzero, copy the value through a register
and return that register, if that is possible. */
+extern rtx store_expr_with_bounds (tree, rtx, int, bool, tree);
extern rtx store_expr (tree, rtx, int, bool);

/* Given an rtx that may include add and multiply operations,
diff --git a/gcc/function.c b/gcc/function.c
index a61e475..a08d4ad 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3. If not see
#include "df.h"
#include "params.h"
#include "bb-reorder.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* So we can assign to cfun in this file. */
#undef cfun
@@ -2082,6 +2084,14 @@ use_register_for_decl (const_tree decl)
if (TREE_ADDRESSABLE (decl))
return false;

+ /* Decl is implicitly addressible by bound stores and loads
+ if it is an aggregate holding bounds. */
+ if (chkp_function_instrumented_p (current_function_decl)
+ && TREE_TYPE (decl)
+ && !BOUNDED_P (decl)
+ && chkp_type_has_pointer (TREE_TYPE (decl)))
+ return false;
+
/* Only register-like things go in registers. */
if (DECL_MODE (decl) == BLKmode)
return false;
@@ -2202,6 +2212,15 @@ struct assign_parm_data_one
BOOL_BITFIELD loaded_in_reg : 1;
};

+struct bounds_parm_data
+{
+ assign_parm_data_one parm_data;
+ tree bounds_parm;
+ tree ptr_parm;
+ rtx ptr_entry;
+ int bound_no;
+};
+
/* A subroutine of assign_parms. Initialize ALL. */

static void
@@ -2312,6 +2331,23 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
fnargs.safe_insert (0, decl);

all->function_result_decl = decl;
+
+ /* If function is instrumented then bounds of the
+ passed structure address is the second argument. */
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ decl = build_decl (DECL_SOURCE_LOCATION (fndecl),
+ PARM_DECL, get_identifier (".result_bnd"),
+ pointer_bounds_type_node);
+ DECL_ARG_TYPE (decl) = pointer_bounds_type_node;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_NAMELESS (decl) = 1;
+ TREE_CONSTANT (decl) = 1;
+
+ DECL_CHAIN (decl) = DECL_CHAIN (all->orig_fnargs);
+ DECL_CHAIN (all->orig_fnargs) = decl;
+ fnargs.safe_insert (1, decl);
+ }
}

/* If the target wants to split complex arguments into scalars, do so. */
@@ -2452,7 +2488,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
In this case, we call FUNCTION_ARG with NAMED set to 1 instead of 0
as it was the previous time. */
- in_regs = entry_parm != 0;
+ in_regs = (entry_parm != 0) || POINTER_BOUNDS_TYPE_P (data->passed_type);
#ifdef STACK_PARMS_IN_REG_PARM_AREA
in_regs = true;
#endif
@@ -2541,8 +2577,12 @@ static bool
assign_parm_is_stack_parm (struct assign_parm_data_all *all,
struct assign_parm_data_one *data)
{
+ /* Bounds are never passed on the stack to keep compatibility
+ with not instrumented code. */
+ if (POINTER_BOUNDS_TYPE_P (data->passed_type))
+ return false;
/* Trivially true if we've no incoming register. */
- if (data->entry_parm == NULL)
+ else if (data->entry_parm == NULL)
;
/* Also true if we're partially in registers and partially not,
since we've arranged to drop the entire argument on the stack. */
@@ -3348,6 +3388,119 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all,
}
}

+/* Load bounds PARM from bounds table. */
+static void
+assign_parm_load_bounds (struct assign_parm_data_one *data,
+ tree parm,
+ rtx entry,
+ unsigned bound_no)
+{
+ bitmap_iterator bi;
+ unsigned i, offs = 0;
+ int bnd_no = -1;
+ rtx slot = NULL, ptr = NULL;
+
+ if (parm)
+ {
+ bitmap slots = chkp_find_bound_slots (TREE_TYPE (parm));
+ EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi)
+ {
+ if (bound_no)
+ bound_no--;
+ else
+ {
+ bnd_no = i;
+ break;
+ }
+ }
+ BITMAP_FREE (slots);
+ }
+
+ /* We may have bounds not associated with any pointer. */
+ if (bnd_no != -1)
+ offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ /* Find associated pointer. */
+ if (bnd_no == -1)
+ {
+ /* If bounds are not associated with any bounds,
+ then it is passed in a register or special slot. */
+ gcc_assert (data->entry_parm);
+ ptr = const0_rtx;
+ }
+ else if (MEM_P (entry))
+ slot = adjust_address (entry, Pmode, offs);
+ else if (REG_P (entry))
+ ptr = gen_rtx_REG (Pmode, REGNO (entry) + bnd_no);
+ else if (GET_CODE (entry) == PARALLEL)
+ ptr = chkp_get_value_with_offs (entry, GEN_INT (offs));
+ else
+ gcc_unreachable ();
+ data->entry_parm = targetm.calls.load_bounds_for_arg (slot, ptr,
+ data->entry_parm);
+}
+
+/* Assign RTL expressions to the function's bounds parameters BNDARGS. */
+
+static void
+assign_bounds (vec<bounds_parm_data> &bndargs,
+ struct assign_parm_data_all &all)
+{
+ unsigned i, pass, handled = 0;
+ bounds_parm_data *pbdata;
+
+ if (!bndargs.exists ())
+ return;
+
+ /* We make few passes to store input bounds. Firstly handle bounds
+ passed in registers. After that we load bounds passed in special
+ slots. Finally we load bounds from Bounds Table. */
+ for (pass = 0; pass < 3; pass++)
+ FOR_EACH_VEC_ELT (bndargs, i, pbdata)
+ {
+ /* Pass 0 => regs only. */
+ if (pass == 0
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG))
+ continue;
+ /* Pass 1 => slots only. */
+ else if (pass == 1
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) == REG))
+ continue;
+ /* Pass 2 => BT only. */
+ else if (pass == 2
+ && pbdata->parm_data.entry_parm)
+ continue;
+
+ if (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG)
+ assign_parm_load_bounds (&pbdata->parm_data, pbdata->ptr_parm,
+ pbdata->ptr_entry, pbdata->bound_no);
+
+ set_decl_incoming_rtl (pbdata->bounds_parm,
+ pbdata->parm_data.entry_parm, false);
+
+ if (assign_parm_setup_block_p (&pbdata->parm_data))
+ assign_parm_setup_block (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else if (pbdata->parm_data.passed_pointer
+ || use_register_for_decl (pbdata->bounds_parm))
+ assign_parm_setup_reg (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else
+ assign_parm_setup_stack (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+
+ /* Count handled bounds to make sure we miss nothing. */
+ handled++;
+ }
+
+ gcc_assert (handled == bndargs.length ());
+
+ bndargs.release ();
+}
+
/* Assign RTL expressions to the function's parameters. This may involve
copying them into registers and using those registers as the DECL_RTL. */

@@ -3357,7 +3510,11 @@ assign_parms (tree fndecl)
struct assign_parm_data_all all;
tree parm;
vec<tree> fnargs;
- unsigned i;
+ unsigned i, bound_no = 0;
+ tree last_arg = NULL;
+ rtx last_arg_entry = NULL;
+ vec<bounds_parm_data> bndargs = vNULL;
+ bounds_parm_data bdata;

crtl->args.internal_arg_pointer
= targetm.calls.internal_arg_pointer ();
@@ -3399,9 +3556,6 @@ assign_parms (tree fndecl)
}
}

- if (cfun->stdarg && !DECL_CHAIN (parm))
- assign_parms_setup_varargs (&all, &data, false);
-
/* Find out where the parameter arrives in this function. */
assign_parm_find_entry_rtl (&all, &data);

@@ -3411,7 +3565,15 @@ assign_parms (tree fndecl)
assign_parm_find_stack_rtl (parm, &data);
assign_parm_adjust_entry_rtl (&data);
}
-
+ if (!POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Remember where last non bounds arg was passed in case
+ we have to load associated bounds for it from Bounds
+ Table. */
+ last_arg = parm;
+ last_arg_entry = data.entry_parm;
+ bound_no = 0;
+ }
/* Record permanently how this parm was passed. */
if (data.passed_pointer)
{
@@ -3423,20 +3585,63 @@ assign_parms (tree fndecl)
else
set_decl_incoming_rtl (parm, data.entry_parm, false);

+ /* Boudns should be loaded in the particular order to
+ have registers allocated correctly. Collect info about
+ input bounds and load them later. */
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Expect bounds in instrumented functions only. */
+ gcc_assert (chkp_function_instrumented_p (fndecl));
+
+ bdata.parm_data = data;
+ bdata.bounds_parm = parm;
+ bdata.ptr_parm = last_arg;
+ bdata.ptr_entry = last_arg_entry;
+ bdata.bound_no = bound_no;
+ bndargs.safe_push (bdata);
+ }
+ else
+ {
+ assign_parm_adjust_stack_rtl (&data);
+
+ if (assign_parm_setup_block_p (&data))
+ assign_parm_setup_block (&all, parm, &data);
+ else if (data.passed_pointer || use_register_for_decl (parm))
+ assign_parm_setup_reg (&all, parm, &data);
+ else
+ assign_parm_setup_stack (&all, parm, &data);
+ }
+
+ if (cfun->stdarg && !DECL_CHAIN (parm))
+ {
+ int pretend_bytes = 0;
+
+ assign_parms_setup_varargs (&all, &data, false);
+
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ /* We expect this is the last parm. Otherwise it is wrong
+ to assign bounds right now. */
+ gcc_assert (i == (fnargs.length () - 1));
+ assign_bounds (bndargs, all);
+ targetm.calls.setup_incoming_vararg_bounds (all.args_so_far,
+ data.promoted_mode,
+ data.passed_type,
+ &pretend_bytes,
+ false);
+ }
+ }
+
/* Update info on where next arg arrives in registers. */
targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
data.passed_type, data.named_arg);

- assign_parm_adjust_stack_rtl (&data);
-
- if (assign_parm_setup_block_p (&data))
- assign_parm_setup_block (&all, parm, &data);
- else if (data.passed_pointer || use_register_for_decl (parm))
- assign_parm_setup_reg (&all, parm, &data);
- else
- assign_parm_setup_stack (&all, parm, &data);
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ bound_no++;
}

+ assign_bounds (bndargs, all);
+
if (targetm.calls.split_complex_arg)
assign_parms_unsplit_complex (&all, fnargs);

@@ -3557,6 +3762,10 @@ assign_parms (tree fndecl)

real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result),
fndecl, true);
+ if (chkp_function_instrumented_p (fndecl))
+ crtl->return_bnd
+ = targetm.calls.chkp_function_value_bounds (TREE_TYPE (decl_result),
+ fndecl, true);
REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
/* The delay slot scheduler assumes that crtl->return_rtx
holds the hard register containing the return value, not a
@@ -4778,6 +4987,14 @@ expand_function_start (tree subr)
/* Set DECL_REGISTER flag so that expand_function_end will copy the
result to the real return register(s). */
DECL_REGISTER (DECL_RESULT (subr)) = 1;
+
+ if (chkp_function_instrumented_p (current_function_decl))
+ {
+ tree return_type = TREE_TYPE (DECL_RESULT (subr));
+ rtx bounds = targetm.calls.chkp_function_value_bounds (return_type,
+ subr, 1);
+ SET_DECL_BOUNDS_RTL (DECL_RESULT (subr), bounds);
+ }
}

/* Initialize rtx for parameters and local variables.
@@ -4867,14 +5084,11 @@ expand_dummy_function_end (void)
in_dummy_function = false;
}

-/* Call DOIT for each hard register used as a return value from
- the current function. */
+/* Helper for diddle_return_value. */

void
-diddle_return_value (void (*doit) (rtx, void *), void *arg)
+diddle_return_value_1 (void (*doit) (rtx, void *), void *arg, rtx outgoing)
{
- rtx outgoing = crtl->return_rtx;
-
if (! outgoing)
return;

@@ -4894,6 +5108,16 @@ diddle_return_value (void (*doit) (rtx, void *), void *arg)
}
}

+/* Call DOIT for each hard register used as a return value from
+ the current function. */
+
+void
+diddle_return_value (void (*doit) (rtx, void *), void *arg)
+{
+ diddle_return_value_1 (doit, arg, crtl->return_rtx);
+ diddle_return_value_1 (doit, arg, crtl->return_bnd);
+}
+
static void
do_clobber_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED)
{
diff --git a/gcc/function.h b/gcc/function.h
index 38a0fc4..736bb02 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -252,6 +252,9 @@ struct GTY(()) rtl_data {
result in a register, current_function_return_rtx will always be
the hard register containing the result. */
rtx return_rtx;
+ /* If nonxero, an RTL expression for the lcoation at which the current
+ function returns bounds for its result. */
+ rtx return_bnd;

/* Vector of initial-value pairs. Each pair consists of a pseudo
register of approprite mode that stores the initial value a hard
Ilya Enkovich
2014-09-15 07:20:48 UTC
Permalink
Ping
Post by Ilya Enkovich
Post by Michael Matz
Hi,
Post by Ilya Enkovich
Post by Michael Matz
There is exactly one place (except for the self-recursive ones) where
you call the new store_expr with a non-null argument for bounds
target, and it seems to be only necessary for when some sub-expression
of the RHS is a call. Can you somehow arrange to move that handling
to the single place in expand_assignment() so that you don't need to
change the signature of store_expr?
I see the only nice way to do it - store_expr should return bounds of
expanded exp. Currently it always return NULL_RTX. Does it look better
than a new argument?
IMHO it does. That or introducing a new store_expr_with_bounds (with the
new argument) and letting store_expr be a wrapper for that, passing the
NULL. Basically anything that avoids adding a new parameter for most of
the existing calls to store_expr.
Ciao,
Michael.
Here is an updated version using store_expr_with_bounds and store_expr as a wrapper for it.
Bootstrapped and tested on linux-x86_64.
Thanks,
Ilya
--
gcc/
* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
diff --git a/gcc/calls.c b/gcc/calls.c
index e1dc8eb..5fbbe9f 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,11 +44,14 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -76,6 +79,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
+ /* For pointer bounds hold an index of parm bounds are bound to. -1 if
+ there is no such pointer. */
+ int pointer_arg;
+ /* If pointer_arg refers a structure, then pointer_offset holds an offset
+ of a pointer in this structure. */
+ int pointer_offset;
/* If REG was promoted from the actual mode of the argument expression,
indicates whether the promotion is sign- or zero-extended. */
int unsignedp;
@@ -133,6 +145,7 @@ static void emit_call_1 (rtx, tree, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT,
HOST_WIDE_INT, rtx, rtx, int, rtx, int,
cumulative_args_t);
static void precompute_register_parameters (int, struct arg_data *, int *);
+static void store_bounds (struct arg_data *, struct arg_data *);
static int store_one_arg (struct arg_data *, rtx, int, int, int);
static void store_unaligned_arguments_into_pseudos (struct arg_data *, int);
static int finalize_must_preallocate (int, int, struct arg_data *,
@@ -396,6 +409,10 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
&& MEM_EXPR (funmem) != NULL_TREE)
set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem));
+ /* Mark instrumented calls. */
+ if (call && fntree)
+ CALL_EXPR_WITH_BOUNDS_P (call) = CALL_WITH_BOUNDS_P (fntree);
+
/* Put the register usage information there. */
add_function_usage_to (call_insn, call_fusage);
@@ -1141,18 +1158,84 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* First fill in the actual arguments in the ARGS array, splitting
complex arguments if necessary. */
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;
if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
+
j += inc;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr (struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j - inc;
+
+ j += inc;
+ }
}
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
{
tree argtype = TREE_TYPE (arg);
+
+ /* Remember last param with pointer and associate it
+ with following pointer bounds. */
+ if (CALL_WITH_BOUNDS_P (exp)
+ && chkp_type_has_pointer (argtype))
+ {
+ if (slots)
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ ptr_arg = j;
+ if (!BOUNDED_TYPE_P (argtype))
+ slots = chkp_find_bound_slots (argtype);
+ }
+ else if (POINTER_BOUNDS_TYPE_P (argtype))
+ {
+ /* We expect bounds in instrumented calls only.
+ Otherwise it is a sign we lost flag due to some optimization
+ and may emit call args incorrectly. */
+ gcc_assert (CALL_WITH_BOUNDS_P (exp));
+
+ /* For structures look for the next available pointer. */
+ if (ptr_arg != -1 && slots)
+ {
+ unsigned bnd_no = bitmap_first_set_bit (slots);
+ args[j].pointer_offset =
+ bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ bitmap_clear_bit (slots, bnd_no);
+
+ /* Check we have no more pointers in the structure. */
+ if (bitmap_empty_p (slots))
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ }
+ args[j].pointer_arg = ptr_arg;
+
+ /* Check we covered all pointers in the previous
+ non bounds arg. */
+ if (!slots)
+ ptr_arg = -1;
+ }
+ else
+ ptr_arg = -1;
+
if (targetm.calls.split_complex_arg
&& argtype
&& TREE_CODE (argtype) == COMPLEX_TYPE
@@ -1167,6 +1250,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[j].tree_value = arg;
j += inc;
}
+
+ if (slots)
+ BITMAP_FREE (slots);
}
/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
@@ -1302,6 +1388,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);
+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
+
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
@@ -1335,10 +1427,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;
+ /* No stack allocation and padding for bounds. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ ;
/* Compute the stack-size of this argument. */
- if (args[i].reg == 0 || args[i].partial != 0
- || reg_parm_stack_space > 0
- || args[i].pass_on_stack)
+ else if (args[i].reg == 0 || args[i].partial != 0
+ || reg_parm_stack_space > 0
+ || args[i].pass_on_stack)
locate_and_pad_parm (mode, type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
@@ -1553,6 +1648,12 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
partial_seen = 1;
else if (partial_seen && args[i].reg == 0)
must_preallocate = 1;
+ /* We preallocate in case there are bounds passed
+ in the bounds table to have precomputed address
+ for bounds association. */
+ else if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ must_preallocate = 1;
if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
&& (TREE_CODE (args[i].tree_value) == CALL_EXPR
@@ -1604,6 +1705,10 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
&& args[i].partial == 0)
continue;
+ /* Pointer Bounds are never passed on the stack. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+
if (CONST_INT_P (offset))
addr = plus_constant (Pmode, arg_reg, INTVAL (offset));
else
@@ -2233,6 +2338,8 @@ expand_call (tree exp, rtx target, int ignore)
/* Register in which non-BLKmode value will be returned,
or 0 if no value or if value is BLKmode. */
rtx valreg;
+ /* Register(s) in which bounds are returned. */
+ rtx valbnd = NULL;
/* Address where we should return a BLKmode value;
0 if value not BLKmode. */
rtx structure_value_addr = 0;
@@ -2484,7 +2591,7 @@ expand_call (tree exp, rtx target, int ignore)
structure_value_addr_value =
make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
- structure_value_addr_parm = 1;
+ structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1;
}
/* Count the arguments and set NUM_ACTUALS. */
@@ -3003,15 +3110,28 @@ expand_call (tree exp, rtx target, int ignore)
/* Figure out the register where the value, if any, will come back. */
valreg = 0;
+ valbnd = 0;
if (TYPE_MODE (rettype) != VOIDmode
&& ! structure_value_addr)
{
if (pcc_struct_value)
- valreg = hard_function_value (build_pointer_type (rettype),
- fndecl, NULL, (pass == 0));
+ {
+ valreg = hard_function_value (build_pointer_type (rettype),
+ fndecl, NULL, (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.
+ chkp_function_value_bounds (build_pointer_type (rettype),
+ fndecl, (pass == 0));
+ }
else
- valreg = hard_function_value (rettype, fndecl, fntype,
- (pass == 0));
+ {
+ valreg = hard_function_value (rettype, fndecl, fntype,
+ (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.chkp_function_value_bounds (rettype,
+ fndecl,
+ (pass == 0));
+ }
/* If VALREG is a PARALLEL whose first member has a zero
offset, use that. This is for targets such as m68k that
@@ -3052,7 +3172,10 @@ expand_call (tree exp, rtx target, int ignore)
for (i = 0; i < num_actuals; i++)
{
- if (args[i].reg == 0 || args[i].pass_on_stack)
+ /* Delay bounds until all other args are stored. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+ else if (args[i].reg == 0 || args[i].pass_on_stack)
{
rtx before_arg = get_last_insn ();
@@ -3105,6 +3228,17 @@ expand_call (tree exp, rtx target, int ignore)
sibcall_failure = 1;
}
+ /* Store all bounds not passed in registers. */
+ for (i = 0; i < num_actuals; i++)
+ {
+ if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ store_bounds (&args[i],
+ args[i].pointer_arg == -1
+ ? NULL
+ : &args[args[i].pointer_arg]);
+ }
+
/* If we pushed args in forward order, perform stack alignment
after pushing the last arg. */
if (!PUSH_ARGS_REVERSED && argblock == 0)
@@ -3502,6 +3636,9 @@ expand_call (tree exp, rtx target, int ignore)
free (stack_usage_map_buf);
+ /* Join result with returned bounds so caller may use them if needed. */
+ target = chkp_join_splitted_slot (target, valbnd);
+
return target;
}
@@ -4380,6 +4517,68 @@ emit_library_call_value (rtx orgfun, rtx value,
return result;
}
+
+/* Store pointer bounds argument ARG into Bounds Table entry
+ associated with PARM. */
+static void
+store_bounds (struct arg_data *arg, struct arg_data *parm)
+{
+ rtx slot = NULL, ptr = NULL, addr = NULL;
+
+ /* We may pass bounds not associated with any pointer. */
+ if (!parm)
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+ ptr = const0_rtx;
+ }
+ /* Find pointer associated with bounds and where it is
+ passed. */
+ else
+ {
+ if (!parm->reg)
+ {
+ gcc_assert (!arg->special_slot);
+
+ addr = adjust_address (parm->stack, Pmode, arg->pointer_offset);
+ }
+ else if (REG_P (parm->reg))
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (MEM_P (parm->value))
+ addr = adjust_address (parm->value, Pmode, arg->pointer_offset);
+ else if (REG_P (parm->value))
+ ptr = gen_rtx_SUBREG (Pmode, parm->value, arg->pointer_offset);
+ else
+ {
+ gcc_assert (!arg->pointer_offset);
+ ptr = parm->value;
+ }
+ }
+ else
+ {
+ gcc_assert (GET_CODE (parm->reg) == PARALLEL);
+
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (parm->parallel_value)
+ ptr = chkp_get_value_with_offs (parm->parallel_value,
+ GEN_INT (arg->pointer_offset));
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ /* Expand bounds. */
+ if (!arg->value)
+ arg->value = expand_normal (arg->tree_value);
+
+ targetm.calls.store_bounds_for_arg (ptr, addr, arg->value, slot);
+}
+
/* Store a single argument for a function call
into the register or memory area where it must be passed.
*ARG describes the argument value and where to pass it.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index b7f6360..1c75586 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -73,6 +73,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-address.h"
#include "recog.h"
#include "output.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -2238,6 +2240,7 @@ expand_call_stmt (gimple stmt)
CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt);
CALL_EXPR_VA_ARG_PACK (exp) = gimple_call_va_arg_pack_p (stmt);
SET_EXPR_LOCATION (exp, gimple_location (stmt));
+ CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);
/* Ensure RTL is created for debug args. */
if (decl && DECL_HAS_DEBUG_ARGS_P (decl))
@@ -3048,11 +3051,12 @@ expand_value_return (rtx val)
from the current function. */
static void
-expand_return (tree retval)
+expand_return (tree retval, tree bounds)
{
rtx result_rtl;
rtx val = 0;
tree retval_rhs;
+ rtx bounds_rtl;
/* If function wants no value, give it none. */
if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
@@ -3078,6 +3082,56 @@ expand_return (tree retval)
result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));
+ /* Put returned bounds to the right place. */
+ bounds_rtl = DECL_BOUNDS_RTL (DECL_RESULT (current_function_decl));
+ if (bounds_rtl)
+ {
+ rtx addr, bnd;
+
+ if (bounds)
+ {
+ bnd = expand_normal (bounds);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else if (REG_P (bounds_rtl))
+ {
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+ bnd = targetm.calls.load_bounds_for_arg (addr, NULL, NULL);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else
+ {
+ int n;
+
+ gcc_assert (GET_CODE (bounds_rtl) == PARALLEL);
+
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ for (n = 0; n < XVECLEN (bounds_rtl, 0); n++)
+ {
+ rtx offs = XEXP (XVECEXP (bounds_rtl, 0, n), 1);
+ rtx slot = XEXP (XVECEXP (bounds_rtl, 0, n), 0);
+ rtx from = adjust_address (addr, Pmode, INTVAL (offs));
+ rtx bnd = targetm.calls.load_bounds_for_arg (from, NULL, NULL);
+ targetm.calls.store_returned_bounds (slot, bnd);
+ }
+ }
+ }
+ else if (chkp_function_instrumented_p (current_function_decl)
+ && !BOUNDED_P (retval_rhs)
+ && chkp_type_has_pointer (TREE_TYPE (retval_rhs))
+ && TREE_CODE (retval_rhs) != RESULT_DECL)
+ {
+ rtx addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ gcc_assert (MEM_P (result_rtl));
+
+ chkp_copy_bounds_for_stack_parm (result_rtl, addr, TREE_TYPE (retval_rhs));
+ }
+
/* If we are returning the RESULT_DECL, then the value has already
been stored into it, so we don't have to do anything special. */
if (TREE_CODE (retval_rhs) == RESULT_DECL)
@@ -3183,7 +3237,7 @@ expand_gimple_stmt_1 (gimple stmt)
if (!op0)
expand_null_return ();
else
- expand_return (op0);
+ expand_return (op0, gimple_return_retbnd (stmt));
break;
@@ -5556,6 +5610,9 @@ gimple_expand_cfg (void)
rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+ if (chkp_function_instrumented_p (current_function_decl))
+ chkp_reset_rtl_bounds ();
+
insn_locations_init ();
if (!DECL_IS_BUILTIN (current_function_decl))
{
diff --git a/gcc/expr.c b/gcc/expr.c
index 72e4401..40cf67e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -67,6 +67,8 @@ along with GCC; see the file COPYING3. If not see
#include "params.h"
#include "tree-ssa-address.h"
#include "cfgexpand.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* Decide whether a function's arguments should be processed
from first to last or from last to first.
@@ -5008,9 +5010,14 @@ expand_assignment (tree to, tree from, bool nontemporal)
|| TREE_CODE (to) == SSA_NAME))
{
rtx value;
+ rtx bounds;
push_temp_slots ();
value = expand_normal (from);
+
+ /* Split value and bounds to store them separately. */
+ chkp_split_slot (value, &value, &bounds);
+
if (to_rtx == 0)
to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);
@@ -5044,6 +5051,15 @@ expand_assignment (tree to, tree from, bool nontemporal)
emit_move_insn (to_rtx, value);
}
+
+ /* Store bounds if required. */
+ if (bounds
+ && (BOUNDED_P (to) || chkp_type_has_pointer (TREE_TYPE (to))))
+ {
+ gcc_assert (MEM_P (to_rtx));
+ chkp_emit_bounds_store (bounds, value, to_rtx);
+ }
+
preserve_temp_slots (to_rtx);
pop_temp_slots ();
return;
@@ -5119,7 +5135,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
/* Compute FROM and store the value in the rtx we got. */
push_temp_slots ();
- result = store_expr (from, to_rtx, 0, nontemporal);
+ result = store_expr_with_bounds (from, to_rtx, 0, nontemporal, to);
preserve_temp_slots (result);
pop_temp_slots ();
return;
@@ -5156,10 +5172,14 @@ emit_storent_insn (rtx to, rtx from)
If CALL_PARAM_P is nonzero, this is a store into a call param on the
stack, and block moves may need to be treated specially.
- If NONTEMPORAL is true, try using a nontemporal store instruction. */
+ If NONTEMPORAL is true, try using a nontemporal store instruction.
+
+ If BTARGET is not NULL then computed bounds of EXP are
+ associated with BTARGET. */
rtx
-store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+store_expr_with_bounds (tree exp, rtx target, int call_param_p,
+ bool nontemporal, tree btarget)
{
rtx temp;
rtx alt_rtl = NULL_RTX;
@@ -5180,8 +5200,8 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
part. */
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
- return store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ return store_expr_with_bounds (TREE_OPERAND (exp, 1), target,
+ call_param_p, nontemporal, btarget);
}
else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
{
@@ -5195,13 +5215,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
do_pending_stack_adjust ();
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1, -1);
- store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ store_expr_with_bounds (TREE_OPERAND (exp, 1), target, call_param_p,
+ nontemporal, btarget);
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
- store_expr (TREE_OPERAND (exp, 2), target, call_param_p,
- nontemporal);
+ store_expr_with_bounds (TREE_OPERAND (exp, 2), target, call_param_p,
+ nontemporal, btarget);
emit_label (lab2);
OK_DEFER_POP;
@@ -5253,6 +5273,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
temp = expand_expr (exp, inner_target, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
+
/* If TEMP is a VOIDmode constant, use convert_modes to make
sure that we properly convert it. */
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
@@ -5334,6 +5367,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
(call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL),
&alt_rtl, false);
+
+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
}
/* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
@@ -5498,6 +5544,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
return NULL_RTX;
}
+
+/* Same as store_expr_with_bounds but ignoring bounds of EXP. */
+rtx
+store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+{
+ return store_expr_with_bounds (exp, target, call_param_p, nontemporal, NULL);
+}
/* Return true if field F of structure TYPE is a flexible array. */
diff --git a/gcc/expr.h b/gcc/expr.h
index 524da67..d06468d 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -432,6 +432,7 @@ extern void expand_assignment (tree, tree, bool);
and storing the value into TARGET.
If SUGGEST_REG is nonzero, copy the value through a register
and return that register, if that is possible. */
+extern rtx store_expr_with_bounds (tree, rtx, int, bool, tree);
extern rtx store_expr (tree, rtx, int, bool);
/* Given an rtx that may include add and multiply operations,
diff --git a/gcc/function.c b/gcc/function.c
index a61e475..a08d4ad 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3. If not see
#include "df.h"
#include "params.h"
#include "bb-reorder.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* So we can assign to cfun in this file. */
#undef cfun
@@ -2082,6 +2084,14 @@ use_register_for_decl (const_tree decl)
if (TREE_ADDRESSABLE (decl))
return false;
+ /* Decl is implicitly addressible by bound stores and loads
+ if it is an aggregate holding bounds. */
+ if (chkp_function_instrumented_p (current_function_decl)
+ && TREE_TYPE (decl)
+ && !BOUNDED_P (decl)
+ && chkp_type_has_pointer (TREE_TYPE (decl)))
+ return false;
+
/* Only register-like things go in registers. */
if (DECL_MODE (decl) == BLKmode)
return false;
@@ -2202,6 +2212,15 @@ struct assign_parm_data_one
BOOL_BITFIELD loaded_in_reg : 1;
};
+struct bounds_parm_data
+{
+ assign_parm_data_one parm_data;
+ tree bounds_parm;
+ tree ptr_parm;
+ rtx ptr_entry;
+ int bound_no;
+};
+
/* A subroutine of assign_parms. Initialize ALL. */
static void
@@ -2312,6 +2331,23 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
fnargs.safe_insert (0, decl);
all->function_result_decl = decl;
+
+ /* If function is instrumented then bounds of the
+ passed structure address is the second argument. */
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ decl = build_decl (DECL_SOURCE_LOCATION (fndecl),
+ PARM_DECL, get_identifier (".result_bnd"),
+ pointer_bounds_type_node);
+ DECL_ARG_TYPE (decl) = pointer_bounds_type_node;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_NAMELESS (decl) = 1;
+ TREE_CONSTANT (decl) = 1;
+
+ DECL_CHAIN (decl) = DECL_CHAIN (all->orig_fnargs);
+ DECL_CHAIN (all->orig_fnargs) = decl;
+ fnargs.safe_insert (1, decl);
+ }
}
/* If the target wants to split complex arguments into scalars, do so. */
@@ -2452,7 +2488,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
In this case, we call FUNCTION_ARG with NAMED set to 1 instead of 0
as it was the previous time. */
- in_regs = entry_parm != 0;
+ in_regs = (entry_parm != 0) || POINTER_BOUNDS_TYPE_P (data->passed_type);
#ifdef STACK_PARMS_IN_REG_PARM_AREA
in_regs = true;
#endif
@@ -2541,8 +2577,12 @@ static bool
assign_parm_is_stack_parm (struct assign_parm_data_all *all,
struct assign_parm_data_one *data)
{
+ /* Bounds are never passed on the stack to keep compatibility
+ with not instrumented code. */
+ if (POINTER_BOUNDS_TYPE_P (data->passed_type))
+ return false;
/* Trivially true if we've no incoming register. */
- if (data->entry_parm == NULL)
+ else if (data->entry_parm == NULL)
;
/* Also true if we're partially in registers and partially not,
since we've arranged to drop the entire argument on the stack. */
@@ -3348,6 +3388,119 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all,
}
}
+/* Load bounds PARM from bounds table. */
+static void
+assign_parm_load_bounds (struct assign_parm_data_one *data,
+ tree parm,
+ rtx entry,
+ unsigned bound_no)
+{
+ bitmap_iterator bi;
+ unsigned i, offs = 0;
+ int bnd_no = -1;
+ rtx slot = NULL, ptr = NULL;
+
+ if (parm)
+ {
+ bitmap slots = chkp_find_bound_slots (TREE_TYPE (parm));
+ EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi)
+ {
+ if (bound_no)
+ bound_no--;
+ else
+ {
+ bnd_no = i;
+ break;
+ }
+ }
+ BITMAP_FREE (slots);
+ }
+
+ /* We may have bounds not associated with any pointer. */
+ if (bnd_no != -1)
+ offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ /* Find associated pointer. */
+ if (bnd_no == -1)
+ {
+ /* If bounds are not associated with any bounds,
+ then it is passed in a register or special slot. */
+ gcc_assert (data->entry_parm);
+ ptr = const0_rtx;
+ }
+ else if (MEM_P (entry))
+ slot = adjust_address (entry, Pmode, offs);
+ else if (REG_P (entry))
+ ptr = gen_rtx_REG (Pmode, REGNO (entry) + bnd_no);
+ else if (GET_CODE (entry) == PARALLEL)
+ ptr = chkp_get_value_with_offs (entry, GEN_INT (offs));
+ else
+ gcc_unreachable ();
+ data->entry_parm = targetm.calls.load_bounds_for_arg (slot, ptr,
+ data->entry_parm);
+}
+
+/* Assign RTL expressions to the function's bounds parameters BNDARGS. */
+
+static void
+assign_bounds (vec<bounds_parm_data> &bndargs,
+ struct assign_parm_data_all &all)
+{
+ unsigned i, pass, handled = 0;
+ bounds_parm_data *pbdata;
+
+ if (!bndargs.exists ())
+ return;
+
+ /* We make few passes to store input bounds. Firstly handle bounds
+ passed in registers. After that we load bounds passed in special
+ slots. Finally we load bounds from Bounds Table. */
+ for (pass = 0; pass < 3; pass++)
+ FOR_EACH_VEC_ELT (bndargs, i, pbdata)
+ {
+ /* Pass 0 => regs only. */
+ if (pass == 0
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG))
+ continue;
+ /* Pass 1 => slots only. */
+ else if (pass == 1
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) == REG))
+ continue;
+ /* Pass 2 => BT only. */
+ else if (pass == 2
+ && pbdata->parm_data.entry_parm)
+ continue;
+
+ if (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG)
+ assign_parm_load_bounds (&pbdata->parm_data, pbdata->ptr_parm,
+ pbdata->ptr_entry, pbdata->bound_no);
+
+ set_decl_incoming_rtl (pbdata->bounds_parm,
+ pbdata->parm_data.entry_parm, false);
+
+ if (assign_parm_setup_block_p (&pbdata->parm_data))
+ assign_parm_setup_block (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else if (pbdata->parm_data.passed_pointer
+ || use_register_for_decl (pbdata->bounds_parm))
+ assign_parm_setup_reg (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else
+ assign_parm_setup_stack (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+
+ /* Count handled bounds to make sure we miss nothing. */
+ handled++;
+ }
+
+ gcc_assert (handled == bndargs.length ());
+
+ bndargs.release ();
+}
+
/* Assign RTL expressions to the function's parameters. This may involve
copying them into registers and using those registers as the DECL_RTL. */
@@ -3357,7 +3510,11 @@ assign_parms (tree fndecl)
struct assign_parm_data_all all;
tree parm;
vec<tree> fnargs;
- unsigned i;
+ unsigned i, bound_no = 0;
+ tree last_arg = NULL;
+ rtx last_arg_entry = NULL;
+ vec<bounds_parm_data> bndargs = vNULL;
+ bounds_parm_data bdata;
crtl->args.internal_arg_pointer
= targetm.calls.internal_arg_pointer ();
@@ -3399,9 +3556,6 @@ assign_parms (tree fndecl)
}
}
- if (cfun->stdarg && !DECL_CHAIN (parm))
- assign_parms_setup_varargs (&all, &data, false);
-
/* Find out where the parameter arrives in this function. */
assign_parm_find_entry_rtl (&all, &data);
@@ -3411,7 +3565,15 @@ assign_parms (tree fndecl)
assign_parm_find_stack_rtl (parm, &data);
assign_parm_adjust_entry_rtl (&data);
}
-
+ if (!POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Remember where last non bounds arg was passed in case
+ we have to load associated bounds for it from Bounds
+ Table. */
+ last_arg = parm;
+ last_arg_entry = data.entry_parm;
+ bound_no = 0;
+ }
/* Record permanently how this parm was passed. */
if (data.passed_pointer)
{
@@ -3423,20 +3585,63 @@ assign_parms (tree fndecl)
else
set_decl_incoming_rtl (parm, data.entry_parm, false);
+ /* Boudns should be loaded in the particular order to
+ have registers allocated correctly. Collect info about
+ input bounds and load them later. */
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Expect bounds in instrumented functions only. */
+ gcc_assert (chkp_function_instrumented_p (fndecl));
+
+ bdata.parm_data = data;
+ bdata.bounds_parm = parm;
+ bdata.ptr_parm = last_arg;
+ bdata.ptr_entry = last_arg_entry;
+ bdata.bound_no = bound_no;
+ bndargs.safe_push (bdata);
+ }
+ else
+ {
+ assign_parm_adjust_stack_rtl (&data);
+
+ if (assign_parm_setup_block_p (&data))
+ assign_parm_setup_block (&all, parm, &data);
+ else if (data.passed_pointer || use_register_for_decl (parm))
+ assign_parm_setup_reg (&all, parm, &data);
+ else
+ assign_parm_setup_stack (&all, parm, &data);
+ }
+
+ if (cfun->stdarg && !DECL_CHAIN (parm))
+ {
+ int pretend_bytes = 0;
+
+ assign_parms_setup_varargs (&all, &data, false);
+
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ /* We expect this is the last parm. Otherwise it is wrong
+ to assign bounds right now. */
+ gcc_assert (i == (fnargs.length () - 1));
+ assign_bounds (bndargs, all);
+ targetm.calls.setup_incoming_vararg_bounds (all.args_so_far,
+ data.promoted_mode,
+ data.passed_type,
+ &pretend_bytes,
+ false);
+ }
+ }
+
/* Update info on where next arg arrives in registers. */
targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
data.passed_type, data.named_arg);
- assign_parm_adjust_stack_rtl (&data);
-
- if (assign_parm_setup_block_p (&data))
- assign_parm_setup_block (&all, parm, &data);
- else if (data.passed_pointer || use_register_for_decl (parm))
- assign_parm_setup_reg (&all, parm, &data);
- else
- assign_parm_setup_stack (&all, parm, &data);
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ bound_no++;
}
+ assign_bounds (bndargs, all);
+
if (targetm.calls.split_complex_arg)
assign_parms_unsplit_complex (&all, fnargs);
@@ -3557,6 +3762,10 @@ assign_parms (tree fndecl)
real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result),
fndecl, true);
+ if (chkp_function_instrumented_p (fndecl))
+ crtl->return_bnd
+ = targetm.calls.chkp_function_value_bounds (TREE_TYPE (decl_result),
+ fndecl, true);
REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
/* The delay slot scheduler assumes that crtl->return_rtx
holds the hard register containing the return value, not a
@@ -4778,6 +4987,14 @@ expand_function_start (tree subr)
/* Set DECL_REGISTER flag so that expand_function_end will copy the
result to the real return register(s). */
DECL_REGISTER (DECL_RESULT (subr)) = 1;
+
+ if (chkp_function_instrumented_p (current_function_decl))
+ {
+ tree return_type = TREE_TYPE (DECL_RESULT (subr));
+ rtx bounds = targetm.calls.chkp_function_value_bounds (return_type,
+ subr, 1);
+ SET_DECL_BOUNDS_RTL (DECL_RESULT (subr), bounds);
+ }
}
/* Initialize rtx for parameters and local variables.
@@ -4867,14 +5084,11 @@ expand_dummy_function_end (void)
in_dummy_function = false;
}
-/* Call DOIT for each hard register used as a return value from
- the current function. */
+/* Helper for diddle_return_value. */
void
-diddle_return_value (void (*doit) (rtx, void *), void *arg)
+diddle_return_value_1 (void (*doit) (rtx, void *), void *arg, rtx outgoing)
{
- rtx outgoing = crtl->return_rtx;
-
if (! outgoing)
return;
@@ -4894,6 +5108,16 @@ diddle_return_value (void (*doit) (rtx, void *), void *arg)
}
}
+/* Call DOIT for each hard register used as a return value from
+ the current function. */
+
+void
+diddle_return_value (void (*doit) (rtx, void *), void *arg)
+{
+ diddle_return_value_1 (doit, arg, crtl->return_rtx);
+ diddle_return_value_1 (doit, arg, crtl->return_bnd);
+}
+
static void
do_clobber_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED)
{
diff --git a/gcc/function.h b/gcc/function.h
index 38a0fc4..736bb02 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -252,6 +252,9 @@ struct GTY(()) rtl_data {
result in a register, current_function_return_rtx will always be
the hard register containing the result. */
rtx return_rtx;
+ /* If nonxero, an RTL expression for the lcoation at which the current
+ function returns bounds for its result. */
+ rtx return_bnd;
/* Vector of initial-value pairs. Each pair consists of a pseudo
register of approprite mode that stores the initial value a hard
Jeff Law
2014-09-23 20:58:05 UTC
Permalink
Post by Ilya Enkovich
* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
diff --git a/gcc/calls.c b/gcc/calls.c
index e1dc8eb..5fbbe9f 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,11 +44,14 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -76,6 +79,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
I really dislike "special_slot" and the comment here. The comment that
it's neither a reg nor stack is just bogus. What hardware resource does
"special_slot" refer to? It's a register, but one that we do not
typically expose. Let's at least clarify the comment and then we'll see
if something other than "special_slot" as a name makes sense. Yes, I
realize this is a bit of bikeshedding, but when the comments/terminology
is confusing, the code becomes even harder to understand.

I'm a bit concerned that this is exposing more details of the MPX
implementation than is advisable to the front/middle end. On the other
hand, I'd expect any other implementation that seeks to work in a
transparent manner is going to have many of the same implementation
properties as we see with MPX, so perhaps it's not a major problem.
Post by Ilya Enkovich
@@ -1141,18 +1158,84 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* First fill in the actual arguments in the ARGS array, splitting
complex arguments if necessary. */
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;
if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
+
j += inc;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr (struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j - inc;
+
+ j += inc;
+ }
Just an FYI, I'm pretty sure this hunk isn't going to apply cleanly as
the context has changed on the trunk. I'd recommend getting this code
updated for the trunk. I suspect you're getting close to having all the
basic functionality bits in, you're obviously going to need to do a new
bootstrap & regression test prior to checkin. I think git sqashing the
series and testing/committing them as an atomic unit is probably wise.

It's been a while since I looked at this code, but is it safe to create
a new call tree at this point? I recall some major complications if you
try to insert a call once you've started filling in arguments. Hmm,
gIven you're at the start of initialize_argument_information, you're
probably OK since we haven't stored any arguments into their arg
regs/memory yet.
Post by Ilya Enkovich
@@ -1302,6 +1388,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);
+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
I can't recall from the earlier patches, but have you updated the
documentation to indicate that function_arg can return a CONST_INT?


I think this is mostly OK. If you could update and resend for another
once-over, it'd be appreciated.

Jeff
Ilya Enkovich
2014-09-24 08:29:44 UTC
Permalink
Post by Ilya Enkovich
* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
diff --git a/gcc/calls.c b/gcc/calls.c
index e1dc8eb..5fbbe9f 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,11 +44,14 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -76,6 +79,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
I really dislike "special_slot" and the comment here. The comment that it's
neither a reg nor stack is just bogus. What hardware resource does
"special_slot" refer to? It's a register, but one that we do not typically
expose. Let's at least clarify the comment and then we'll see if something
other than "special_slot" as a name makes sense. Yes, I realize this is a
bit of bikeshedding, but when the comments/terminology is confusing, the
code becomes even harder to understand.
Special slot is not a register. When bounds are passed in a register
then everything work as if we pass any other argument in a register.
Special slot is used when we are out of bounds registers and pass
bounds for pointer passed in a register. It doesn't refer to any
hardware resource. In MPX ABI we state that special Bounds Table
entries (related to stack pointer value (and lower) right before a
call) are used. In software implementation it also may be some other
places like vars in TLS.
I'm a bit concerned that this is exposing more details of the MPX
implementation than is advisable to the front/middle end. On the other
hand, I'd expect any other implementation that seeks to work in a
transparent manner is going to have many of the same implementation
properties as we see with MPX, so perhaps it's not a major problem.
I'm trying to not introduce any hardware dependencies into middle end.
Several months ago I created a simple prototype of generic target
support in Pointer Bounds Checker which used library calls instead of
MPX instructions, TLS for bounds passing etc. I did it to check our
design is not bound to MPX and allows such implementation. It was
very useful and showed some MPX details soaked into GIMPLE part. E.g.
chkp_initialize_bounds and chkp_make_bounds_constant hooks appeared
during that work. Special slots mechanism worked well for it though.
Post by Ilya Enkovich
@@ -1141,18 +1158,84 @@ initialize_argument_information (int num_actuals
ATTRIBUTE_UNUSED,
/* First fill in the actual arguments in the ARGS array, splitting
complex arguments if necessary. */
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;
if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
+
j += inc;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr
(struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j - inc;
+
+ j += inc;
+ }
Just an FYI, I'm pretty sure this hunk isn't going to apply cleanly as the
context has changed on the trunk. I'd recommend getting this code updated
for the trunk. I suspect you're getting close to having all the basic
functionality bits in, you're obviously going to need to do a new bootstrap
& regression test prior to checkin. I think git sqashing the series and
testing/committing them as an atomic unit is probably wise.
There are also some other patches which require update. I'll do it
and repost modified patches.
It's been a while since I looked at this code, but is it safe to create a
new call tree at this point? I recall some major complications if you try
to insert a call once you've started filling in arguments. Hmm, gIven
you're at the start of initialize_argument_information, you're probably OK
since we haven't stored any arguments into their arg regs/memory yet.
It should be quite safe to expand another call until we start moving
arguments to their actual places.
Post by Ilya Enkovich
@@ -1302,6 +1388,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);
+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
I can't recall from the earlier patches, but have you updated the
documentation to indicate that function_arg can return a CONST_INT?
I didn't update it. Will do.

Thanks,
Ilya
I think this is mostly OK. If you could update and resend for another
once-over, it'd be appreciated.
Jeff
Ilya Enkovich
2014-10-02 14:03:07 UTC
Permalink
Post by Ilya Enkovich
Post by Ilya Enkovich
* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
diff --git a/gcc/calls.c b/gcc/calls.c
index e1dc8eb..5fbbe9f 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,11 +44,14 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -76,6 +79,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
I really dislike "special_slot" and the comment here. The comment that it's
neither a reg nor stack is just bogus. What hardware resource does
"special_slot" refer to? It's a register, but one that we do not typically
expose. Let's at least clarify the comment and then we'll see if something
other than "special_slot" as a name makes sense. Yes, I realize this is a
bit of bikeshedding, but when the comments/terminology is confusing, the
code becomes even harder to understand.
Special slot is not a register. When bounds are passed in a register
then everything work as if we pass any other argument in a register.
Special slot is used when we are out of bounds registers and pass
bounds for pointer passed in a register. It doesn't refer to any
hardware resource. In MPX ABI we state that special Bounds Table
entries (related to stack pointer value (and lower) right before a
call) are used. In software implementation it also may be some other
places like vars in TLS.
I'm a bit concerned that this is exposing more details of the MPX
implementation than is advisable to the front/middle end. On the other
hand, I'd expect any other implementation that seeks to work in a
transparent manner is going to have many of the same implementation
properties as we see with MPX, so perhaps it's not a major problem.
I'm trying to not introduce any hardware dependencies into middle end.
Several months ago I created a simple prototype of generic target
support in Pointer Bounds Checker which used library calls instead of
MPX instructions, TLS for bounds passing etc. I did it to check our
design is not bound to MPX and allows such implementation. It was
very useful and showed some MPX details soaked into GIMPLE part. E.g.
chkp_initialize_bounds and chkp_make_bounds_constant hooks appeared
during that work. Special slots mechanism worked well for it though.
Post by Ilya Enkovich
@@ -1141,18 +1158,84 @@ initialize_argument_information (int num_actuals
ATTRIBUTE_UNUSED,
/* First fill in the actual arguments in the ARGS array, splitting
complex arguments if necessary. */
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;
if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
+
j += inc;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr
(struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j - inc;
+
+ j += inc;
+ }
Just an FYI, I'm pretty sure this hunk isn't going to apply cleanly as the
context has changed on the trunk. I'd recommend getting this code updated
for the trunk. I suspect you're getting close to having all the basic
functionality bits in, you're obviously going to need to do a new bootstrap
& regression test prior to checkin. I think git sqashing the series and
testing/committing them as an atomic unit is probably wise.
There are also some other patches which require update. I'll do it
and repost modified patches.
It's been a while since I looked at this code, but is it safe to create a
new call tree at this point? I recall some major complications if you try
to insert a call once you've started filling in arguments. Hmm, gIven
you're at the start of initialize_argument_information, you're probably OK
since we haven't stored any arguments into their arg regs/memory yet.
It should be quite safe to expand another call until we start moving
arguments to their actual places.
Post by Ilya Enkovich
@@ -1302,6 +1388,12 @@ initialize_argument_information (int num_actuals
ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);
+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
I can't recall from the earlier patches, but have you updated the
documentation to indicate that function_arg can return a CONST_INT?
I didn't update it. Will do.
Thanks,
Ilya
I think this is mostly OK. If you could update and resend for another
once-over, it'd be appreciated.
Jeff
Here is an updated version.

Thanks,
Ilya
--
2014-10-02 Ilya Enkovich <***@intel.com>

* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
* target.def (TARGET_FUNCTION_ARG): Update hook description with new
possible return value CONST_INT.
* doc/tm.texi: Regenerate.


diff --git a/gcc/calls.c b/gcc/calls.c
index df6d268..a9c22ac 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -44,12 +44,15 @@ along with GCC; see the file COPYING3. If not see
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
+#include "bitmap.h"
#include "langhooks.h"
#include "target.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
#include "rtl-iter.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -77,6 +80,15 @@ struct arg_data
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
+ /* For pointer bounds hold an index of parm bounds are bound to. -1 if
+ there is no such pointer. */
+ int pointer_arg;
+ /* If pointer_arg refers a structure, then pointer_offset holds an offset
+ of a pointer in this structure. */
+ int pointer_offset;
/* If REG was promoted from the actual mode of the argument expression,
indicates whether the promotion is sign- or zero-extended. */
int unsignedp;
@@ -134,6 +146,7 @@ static void emit_call_1 (rtx, tree, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT,
HOST_WIDE_INT, rtx, rtx, int, rtx, int,
cumulative_args_t);
static void precompute_register_parameters (int, struct arg_data *, int *);
+static void store_bounds (struct arg_data *, struct arg_data *);
static int store_one_arg (struct arg_data *, rtx, int, int, int);
static void store_unaligned_arguments_into_pseudos (struct arg_data *, int);
static int finalize_must_preallocate (int, int, struct arg_data *,
@@ -398,6 +411,10 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
&& MEM_EXPR (funmem) != NULL_TREE)
set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem));

+ /* Mark instrumented calls. */
+ if (call && fntree)
+ CALL_EXPR_WITH_BOUNDS_P (call) = CALL_WITH_BOUNDS_P (fntree);
+
/* Put the register usage information there. */
add_function_usage_to (call_insn, call_fusage);

@@ -1128,18 +1145,82 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,

i = num_actuals - 1;
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;

if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
j--;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr (struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j + 1;
+ j--;
+ }
}
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
{
tree argtype = TREE_TYPE (arg);
+
+ /* Remember last param with pointer and associate it
+ with following pointer bounds. */
+ if (CALL_WITH_BOUNDS_P (exp)
+ && chkp_type_has_pointer (argtype))
+ {
+ if (slots)
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ ptr_arg = j;
+ if (!BOUNDED_TYPE_P (argtype))
+ slots = chkp_find_bound_slots (argtype);
+ }
+ else if (POINTER_BOUNDS_TYPE_P (argtype))
+ {
+ /* We expect bounds in instrumented calls only.
+ Otherwise it is a sign we lost flag due to some optimization
+ and may emit call args incorrectly. */
+ gcc_assert (CALL_WITH_BOUNDS_P (exp));
+
+ /* For structures look for the next available pointer. */
+ if (ptr_arg != -1 && slots)
+ {
+ unsigned bnd_no = bitmap_first_set_bit (slots);
+ args[j].pointer_offset =
+ bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ bitmap_clear_bit (slots, bnd_no);
+
+ /* Check we have no more pointers in the structure. */
+ if (bitmap_empty_p (slots))
+ {
+ BITMAP_FREE (slots);
+ slots = NULL;
+ }
+ }
+ args[j].pointer_arg = ptr_arg;
+
+ /* Check we covered all pointers in the previous
+ non bounds arg. */
+ if (!slots)
+ ptr_arg = -1;
+ }
+ else
+ ptr_arg = -1;
+
if (targetm.calls.split_complex_arg
&& argtype
&& TREE_CODE (argtype) == COMPLEX_TYPE
@@ -1154,6 +1235,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[j].tree_value = arg;
j--;
}
+
+ if (slots)
+ BITMAP_FREE (slots);
}

/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
@@ -1289,6 +1373,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);

+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
+
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
@@ -1322,10 +1412,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;

+ /* No stack allocation and padding for bounds. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ ;
/* Compute the stack-size of this argument. */
- if (args[i].reg == 0 || args[i].partial != 0
- || reg_parm_stack_space > 0
- || args[i].pass_on_stack)
+ else if (args[i].reg == 0 || args[i].partial != 0
+ || reg_parm_stack_space > 0
+ || args[i].pass_on_stack)
locate_and_pad_parm (mode, type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
@@ -1539,6 +1632,12 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
partial_seen = 1;
else if (partial_seen && args[i].reg == 0)
must_preallocate = 1;
+ /* We preallocate in case there are bounds passed
+ in the bounds table to have precomputed address
+ for bounds association. */
+ else if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ must_preallocate = 1;

if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
&& (TREE_CODE (args[i].tree_value) == CALL_EXPR
@@ -1590,6 +1689,10 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
&& args[i].partial == 0)
continue;

+ /* Pointer Bounds are never passed on the stack. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+
if (CONST_INT_P (offset))
addr = plus_constant (Pmode, arg_reg, INTVAL (offset));
else
@@ -2212,6 +2315,8 @@ expand_call (tree exp, rtx target, int ignore)
/* Register in which non-BLKmode value will be returned,
or 0 if no value or if value is BLKmode. */
rtx valreg;
+ /* Register(s) in which bounds are returned. */
+ rtx valbnd = NULL;
/* Address where we should return a BLKmode value;
0 if value not BLKmode. */
rtx structure_value_addr = 0;
@@ -2463,7 +2568,7 @@ expand_call (tree exp, rtx target, int ignore)

structure_value_addr_value =
make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
- structure_value_addr_parm = 1;
+ structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1;
}

/* Count the arguments and set NUM_ACTUALS. */
@@ -2981,15 +3086,28 @@ expand_call (tree exp, rtx target, int ignore)

/* Figure out the register where the value, if any, will come back. */
valreg = 0;
+ valbnd = 0;
if (TYPE_MODE (rettype) != VOIDmode
&& ! structure_value_addr)
{
if (pcc_struct_value)
- valreg = hard_function_value (build_pointer_type (rettype),
- fndecl, NULL, (pass == 0));
+ {
+ valreg = hard_function_value (build_pointer_type (rettype),
+ fndecl, NULL, (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.
+ chkp_function_value_bounds (build_pointer_type (rettype),
+ fndecl, (pass == 0));
+ }
else
- valreg = hard_function_value (rettype, fndecl, fntype,
- (pass == 0));
+ {
+ valreg = hard_function_value (rettype, fndecl, fntype,
+ (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.chkp_function_value_bounds (rettype,
+ fndecl,
+ (pass == 0));
+ }

/* If VALREG is a PARALLEL whose first member has a zero
offset, use that. This is for targets such as m68k that
@@ -3030,7 +3148,10 @@ expand_call (tree exp, rtx target, int ignore)

for (i = 0; i < num_actuals; i++)
{
- if (args[i].reg == 0 || args[i].pass_on_stack)
+ /* Delay bounds until all other args are stored. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+ else if (args[i].reg == 0 || args[i].pass_on_stack)
{
rtx_insn *before_arg = get_last_insn ();

@@ -3083,6 +3204,17 @@ expand_call (tree exp, rtx target, int ignore)
sibcall_failure = 1;
}

+ /* Store all bounds not passed in registers. */
+ for (i = 0; i < num_actuals; i++)
+ {
+ if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ store_bounds (&args[i],
+ args[i].pointer_arg == -1
+ ? NULL
+ : &args[args[i].pointer_arg]);
+ }
+
/* If register arguments require space on the stack and stack space
was not preallocated, allocate stack space here for arguments
passed in registers. */
@@ -3487,6 +3619,9 @@ expand_call (tree exp, rtx target, int ignore)

free (stack_usage_map_buf);

+ /* Join result with returned bounds so caller may use them if needed. */
+ target = chkp_join_splitted_slot (target, valbnd);
+
return target;
}

@@ -4356,6 +4491,68 @@ emit_library_call_value (rtx orgfun, rtx value,
return result;
}

+
+/* Store pointer bounds argument ARG into Bounds Table entry
+ associated with PARM. */
+static void
+store_bounds (struct arg_data *arg, struct arg_data *parm)
+{
+ rtx slot = NULL, ptr = NULL, addr = NULL;
+
+ /* We may pass bounds not associated with any pointer. */
+ if (!parm)
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+ ptr = const0_rtx;
+ }
+ /* Find pointer associated with bounds and where it is
+ passed. */
+ else
+ {
+ if (!parm->reg)
+ {
+ gcc_assert (!arg->special_slot);
+
+ addr = adjust_address (parm->stack, Pmode, arg->pointer_offset);
+ }
+ else if (REG_P (parm->reg))
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (MEM_P (parm->value))
+ addr = adjust_address (parm->value, Pmode, arg->pointer_offset);
+ else if (REG_P (parm->value))
+ ptr = gen_rtx_SUBREG (Pmode, parm->value, arg->pointer_offset);
+ else
+ {
+ gcc_assert (!arg->pointer_offset);
+ ptr = parm->value;
+ }
+ }
+ else
+ {
+ gcc_assert (GET_CODE (parm->reg) == PARALLEL);
+
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (parm->parallel_value)
+ ptr = chkp_get_value_with_offs (parm->parallel_value,
+ GEN_INT (arg->pointer_offset));
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ /* Expand bounds. */
+ if (!arg->value)
+ arg->value = expand_normal (arg->tree_value);
+
+ targetm.calls.store_bounds_for_arg (ptr, addr, arg->value, slot);
+}
+
/* Store a single argument for a function call
into the register or memory area where it must be passed.
*ARG describes the argument value and where to pass it.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index f95981b..d54a18c 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -74,6 +74,8 @@ along with GCC; see the file COPYING3. If not see
#include "recog.h"
#include "output.h"
#include "builtins.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -2300,6 +2302,7 @@ expand_call_stmt (gimple stmt)
CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt);
CALL_EXPR_VA_ARG_PACK (exp) = gimple_call_va_arg_pack_p (stmt);
SET_EXPR_LOCATION (exp, gimple_location (stmt));
+ CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);

/* Ensure RTL is created for debug args. */
if (decl && DECL_HAS_DEBUG_ARGS_P (decl))
@@ -3110,11 +3113,12 @@ expand_value_return (rtx val)
from the current function. */

static void
-expand_return (tree retval)
+expand_return (tree retval, tree bounds)
{
rtx result_rtl;
rtx val = 0;
tree retval_rhs;
+ rtx bounds_rtl;

/* If function wants no value, give it none. */
if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
@@ -3140,6 +3144,56 @@ expand_return (tree retval)

result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));

+ /* Put returned bounds to the right place. */
+ bounds_rtl = DECL_BOUNDS_RTL (DECL_RESULT (current_function_decl));
+ if (bounds_rtl)
+ {
+ rtx addr, bnd;
+
+ if (bounds)
+ {
+ bnd = expand_normal (bounds);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else if (REG_P (bounds_rtl))
+ {
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+ bnd = targetm.calls.load_bounds_for_arg (addr, NULL, NULL);
+ targetm.calls.store_returned_bounds (bounds_rtl, bnd);
+ }
+ else
+ {
+ int n;
+
+ gcc_assert (GET_CODE (bounds_rtl) == PARALLEL);
+
+ addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ for (n = 0; n < XVECLEN (bounds_rtl, 0); n++)
+ {
+ rtx offs = XEXP (XVECEXP (bounds_rtl, 0, n), 1);
+ rtx slot = XEXP (XVECEXP (bounds_rtl, 0, n), 0);
+ rtx from = adjust_address (addr, Pmode, INTVAL (offs));
+ rtx bnd = targetm.calls.load_bounds_for_arg (from, NULL, NULL);
+ targetm.calls.store_returned_bounds (slot, bnd);
+ }
+ }
+ }
+ else if (chkp_function_instrumented_p (current_function_decl)
+ && !BOUNDED_P (retval_rhs)
+ && chkp_type_has_pointer (TREE_TYPE (retval_rhs))
+ && TREE_CODE (retval_rhs) != RESULT_DECL)
+ {
+ rtx addr = expand_normal (build_fold_addr_expr (retval_rhs));
+ addr = gen_rtx_MEM (Pmode, addr);
+
+ gcc_assert (MEM_P (result_rtl));
+
+ chkp_copy_bounds_for_stack_parm (result_rtl, addr, TREE_TYPE (retval_rhs));
+ }
+
/* If we are returning the RESULT_DECL, then the value has already
been stored into it, so we don't have to do anything special. */
if (TREE_CODE (retval_rhs) == RESULT_DECL)
@@ -3245,7 +3299,7 @@ expand_gimple_stmt_1 (gimple stmt)
if (!op0)
expand_null_return ();
else
- expand_return (op0);
+ expand_return (op0, gimple_return_retbnd (stmt));
break;

case GIMPLE_ASSIGN:
@@ -5644,6 +5698,9 @@ pass_expand::execute (function *fun)

rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (fun));

+ if (chkp_function_instrumented_p (current_function_decl))
+ chkp_reset_rtl_bounds ();
+
insn_locations_init ();
if (!DECL_IS_BUILTIN (current_function_decl))
{
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 3e8a41c..1a86dc3 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -3843,6 +3843,12 @@ The return value is usually either a @code{reg} RTX for the hard
register in which to pass the argument, or zero to pass the argument
on the stack.

+The return value can be a @code{const_int} which means argument is
+passed in a target specific slot with specified number. Target hooks
+should be used to store or load argument in such case. See
+@code{TARGET_STORE_BOUNDS_FOR_ARG} and @code{TARGET_LOAD_BOUNDS_FOR_ARG}
+for more information.
+
The value of the expression can also be a @code{parallel} ***@. This is
used when an argument is passed in multiple locations. The mode of the
@code{parallel} should be the mode of the entire argument. The
diff --git a/gcc/expr.c b/gcc/expr.c
index a6233f3..1aaa361 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -68,6 +68,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-address.h"
#include "cfgexpand.h"
#include "builtins.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
@@ -5009,9 +5011,14 @@ expand_assignment (tree to, tree from, bool nontemporal)
|| TREE_CODE (to) == SSA_NAME))
{
rtx value;
+ rtx bounds;

push_temp_slots ();
value = expand_normal (from);
+
+ /* Split value and bounds to store them separately. */
+ chkp_split_slot (value, &value, &bounds);
+
if (to_rtx == 0)
to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);

@@ -5045,6 +5052,15 @@ expand_assignment (tree to, tree from, bool nontemporal)

emit_move_insn (to_rtx, value);
}
+
+ /* Store bounds if required. */
+ if (bounds
+ && (BOUNDED_P (to) || chkp_type_has_pointer (TREE_TYPE (to))))
+ {
+ gcc_assert (MEM_P (to_rtx));
+ chkp_emit_bounds_store (bounds, value, to_rtx);
+ }
+
preserve_temp_slots (to_rtx);
pop_temp_slots ();
return;
@@ -5120,7 +5136,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
/* Compute FROM and store the value in the rtx we got. */

push_temp_slots ();
- result = store_expr (from, to_rtx, 0, nontemporal);
+ result = store_expr_with_bounds (from, to_rtx, 0, nontemporal, to);
preserve_temp_slots (result);
pop_temp_slots ();
return;
@@ -5157,10 +5173,14 @@ emit_storent_insn (rtx to, rtx from)
If CALL_PARAM_P is nonzero, this is a store into a call param on the
stack, and block moves may need to be treated specially.

- If NONTEMPORAL is true, try using a nontemporal store instruction. */
+ If NONTEMPORAL is true, try using a nontemporal store instruction.
+
+ If BTARGET is not NULL then computed bounds of EXP are
+ associated with BTARGET. */

rtx
-store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+store_expr_with_bounds (tree exp, rtx target, int call_param_p,
+ bool nontemporal, tree btarget)
{
rtx temp;
rtx alt_rtl = NULL_RTX;
@@ -5181,8 +5201,8 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
part. */
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
- return store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ return store_expr_with_bounds (TREE_OPERAND (exp, 1), target,
+ call_param_p, nontemporal, btarget);
}
else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
{
@@ -5196,13 +5216,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
do_pending_stack_adjust ();
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1, -1);
- store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
- nontemporal);
+ store_expr_with_bounds (TREE_OPERAND (exp, 1), target, call_param_p,
+ nontemporal, btarget);
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
- store_expr (TREE_OPERAND (exp, 2), target, call_param_p,
- nontemporal);
+ store_expr_with_bounds (TREE_OPERAND (exp, 2), target, call_param_p,
+ nontemporal, btarget);
emit_label (lab2);
OK_DEFER_POP;

@@ -5254,6 +5274,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
temp = expand_expr (exp, inner_target, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);

+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
+
/* If TEMP is a VOIDmode constant, use convert_modes to make
sure that we properly convert it. */
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
@@ -5335,6 +5368,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
(call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL),
&alt_rtl, false);
+
+ /* Handle bounds returned by call. */
+ if (TREE_CODE (exp) == CALL_EXPR)
+ {
+ rtx bounds;
+ chkp_split_slot (temp, &temp, &bounds);
+ if (bounds && btarget)
+ {
+ gcc_assert (TREE_CODE (btarget) == SSA_NAME);
+ rtx tmp = targetm.calls.load_returned_bounds (bounds);
+ chkp_set_rtl_bounds (btarget, tmp);
+ }
+ }
}

/* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
@@ -5499,6 +5545,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)

return NULL_RTX;
}
+
+/* Same as store_expr_with_bounds but ignoring bounds of EXP. */
+rtx
+store_expr (tree exp, rtx target, int call_param_p, bool nontemporal)
+{
+ return store_expr_with_bounds (exp, target, call_param_p, nontemporal, NULL);
+}

/* Return true if field F of structure TYPE is a flexible array. */

diff --git a/gcc/expr.h b/gcc/expr.h
index 2e66329..de212d9 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -426,6 +426,7 @@ extern void expand_assignment (tree, tree, bool);
and storing the value into TARGET.
If SUGGEST_REG is nonzero, copy the value through a register
and return that register, if that is possible. */
+extern rtx store_expr_with_bounds (tree, rtx, int, bool, tree);
extern rtx store_expr (tree, rtx, int, bool);

/* Given an rtx that may include add and multiply operations,
diff --git a/gcc/function.c b/gcc/function.c
index ac50f4a..99dccdb 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -66,6 +66,8 @@ along with GCC; see the file COPYING3. If not see
#include "shrink-wrap.h"
#include "toplev.h"
#include "rtl-iter.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"

/* So we can assign to cfun in this file. */
#undef cfun
@@ -2095,6 +2097,14 @@ use_register_for_decl (const_tree decl)
if (TREE_ADDRESSABLE (decl))
return false;

+ /* Decl is implicitly addressible by bound stores and loads
+ if it is an aggregate holding bounds. */
+ if (chkp_function_instrumented_p (current_function_decl)
+ && TREE_TYPE (decl)
+ && !BOUNDED_P (decl)
+ && chkp_type_has_pointer (TREE_TYPE (decl)))
+ return false;
+
/* Only register-like things go in registers. */
if (DECL_MODE (decl) == BLKmode)
return false;
@@ -2215,6 +2225,15 @@ struct assign_parm_data_one
BOOL_BITFIELD loaded_in_reg : 1;
};

+struct bounds_parm_data
+{
+ assign_parm_data_one parm_data;
+ tree bounds_parm;
+ tree ptr_parm;
+ rtx ptr_entry;
+ int bound_no;
+};
+
/* A subroutine of assign_parms. Initialize ALL. */

static void
@@ -2326,6 +2345,23 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
fnargs.safe_insert (0, decl);

all->function_result_decl = decl;
+
+ /* If function is instrumented then bounds of the
+ passed structure address is the second argument. */
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ decl = build_decl (DECL_SOURCE_LOCATION (fndecl),
+ PARM_DECL, get_identifier (".result_bnd"),
+ pointer_bounds_type_node);
+ DECL_ARG_TYPE (decl) = pointer_bounds_type_node;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_NAMELESS (decl) = 1;
+ TREE_CONSTANT (decl) = 1;
+
+ DECL_CHAIN (decl) = DECL_CHAIN (all->orig_fnargs);
+ DECL_CHAIN (all->orig_fnargs) = decl;
+ fnargs.safe_insert (1, decl);
+ }
}

/* If the target wants to split complex arguments into scalars, do so. */
@@ -2466,7 +2502,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
In this case, we call FUNCTION_ARG with NAMED set to 1 instead of 0
as it was the previous time. */
- in_regs = entry_parm != 0;
+ in_regs = (entry_parm != 0) || POINTER_BOUNDS_TYPE_P (data->passed_type);
#ifdef STACK_PARMS_IN_REG_PARM_AREA
in_regs = true;
#endif
@@ -2555,8 +2591,12 @@ static bool
assign_parm_is_stack_parm (struct assign_parm_data_all *all,
struct assign_parm_data_one *data)
{
+ /* Bounds are never passed on the stack to keep compatibility
+ with not instrumented code. */
+ if (POINTER_BOUNDS_TYPE_P (data->passed_type))
+ return false;
/* Trivially true if we've no incoming register. */
- if (data->entry_parm == NULL)
+ else if (data->entry_parm == NULL)
;
/* Also true if we're partially in registers and partially not,
since we've arranged to drop the entire argument on the stack. */
@@ -3365,6 +3405,119 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all,
}
}

+/* Load bounds PARM from bounds table. */
+static void
+assign_parm_load_bounds (struct assign_parm_data_one *data,
+ tree parm,
+ rtx entry,
+ unsigned bound_no)
+{
+ bitmap_iterator bi;
+ unsigned i, offs = 0;
+ int bnd_no = -1;
+ rtx slot = NULL, ptr = NULL;
+
+ if (parm)
+ {
+ bitmap slots = chkp_find_bound_slots (TREE_TYPE (parm));
+ EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi)
+ {
+ if (bound_no)
+ bound_no--;
+ else
+ {
+ bnd_no = i;
+ break;
+ }
+ }
+ BITMAP_FREE (slots);
+ }
+
+ /* We may have bounds not associated with any pointer. */
+ if (bnd_no != -1)
+ offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ /* Find associated pointer. */
+ if (bnd_no == -1)
+ {
+ /* If bounds are not associated with any bounds,
+ then it is passed in a register or special slot. */
+ gcc_assert (data->entry_parm);
+ ptr = const0_rtx;
+ }
+ else if (MEM_P (entry))
+ slot = adjust_address (entry, Pmode, offs);
+ else if (REG_P (entry))
+ ptr = gen_rtx_REG (Pmode, REGNO (entry) + bnd_no);
+ else if (GET_CODE (entry) == PARALLEL)
+ ptr = chkp_get_value_with_offs (entry, GEN_INT (offs));
+ else
+ gcc_unreachable ();
+ data->entry_parm = targetm.calls.load_bounds_for_arg (slot, ptr,
+ data->entry_parm);
+}
+
+/* Assign RTL expressions to the function's bounds parameters BNDARGS. */
+
+static void
+assign_bounds (vec<bounds_parm_data> &bndargs,
+ struct assign_parm_data_all &all)
+{
+ unsigned i, pass, handled = 0;
+ bounds_parm_data *pbdata;
+
+ if (!bndargs.exists ())
+ return;
+
+ /* We make few passes to store input bounds. Firstly handle bounds
+ passed in registers. After that we load bounds passed in special
+ slots. Finally we load bounds from Bounds Table. */
+ for (pass = 0; pass < 3; pass++)
+ FOR_EACH_VEC_ELT (bndargs, i, pbdata)
+ {
+ /* Pass 0 => regs only. */
+ if (pass == 0
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG))
+ continue;
+ /* Pass 1 => slots only. */
+ else if (pass == 1
+ && (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) == REG))
+ continue;
+ /* Pass 2 => BT only. */
+ else if (pass == 2
+ && pbdata->parm_data.entry_parm)
+ continue;
+
+ if (!pbdata->parm_data.entry_parm
+ || GET_CODE (pbdata->parm_data.entry_parm) != REG)
+ assign_parm_load_bounds (&pbdata->parm_data, pbdata->ptr_parm,
+ pbdata->ptr_entry, pbdata->bound_no);
+
+ set_decl_incoming_rtl (pbdata->bounds_parm,
+ pbdata->parm_data.entry_parm, false);
+
+ if (assign_parm_setup_block_p (&pbdata->parm_data))
+ assign_parm_setup_block (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else if (pbdata->parm_data.passed_pointer
+ || use_register_for_decl (pbdata->bounds_parm))
+ assign_parm_setup_reg (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+ else
+ assign_parm_setup_stack (&all, pbdata->bounds_parm,
+ &pbdata->parm_data);
+
+ /* Count handled bounds to make sure we miss nothing. */
+ handled++;
+ }
+
+ gcc_assert (handled == bndargs.length ());
+
+ bndargs.release ();
+}
+
/* Assign RTL expressions to the function's parameters. This may involve
copying them into registers and using those registers as the DECL_RTL. */

@@ -3374,7 +3527,11 @@ assign_parms (tree fndecl)
struct assign_parm_data_all all;
tree parm;
vec<tree> fnargs;
- unsigned i;
+ unsigned i, bound_no = 0;
+ tree last_arg = NULL;
+ rtx last_arg_entry = NULL;
+ vec<bounds_parm_data> bndargs = vNULL;
+ bounds_parm_data bdata;

crtl->args.internal_arg_pointer
= targetm.calls.internal_arg_pointer ();
@@ -3416,9 +3573,6 @@ assign_parms (tree fndecl)
}
}

- if (cfun->stdarg && !DECL_CHAIN (parm))
- assign_parms_setup_varargs (&all, &data, false);
-
/* Find out where the parameter arrives in this function. */
assign_parm_find_entry_rtl (&all, &data);

@@ -3428,7 +3582,15 @@ assign_parms (tree fndecl)
assign_parm_find_stack_rtl (parm, &data);
assign_parm_adjust_entry_rtl (&data);
}
-
+ if (!POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Remember where last non bounds arg was passed in case
+ we have to load associated bounds for it from Bounds
+ Table. */
+ last_arg = parm;
+ last_arg_entry = data.entry_parm;
+ bound_no = 0;
+ }
/* Record permanently how this parm was passed. */
if (data.passed_pointer)
{
@@ -3440,20 +3602,63 @@ assign_parms (tree fndecl)
else
set_decl_incoming_rtl (parm, data.entry_parm, false);

+ /* Boudns should be loaded in the particular order to
+ have registers allocated correctly. Collect info about
+ input bounds and load them later. */
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ {
+ /* Expect bounds in instrumented functions only. */
+ gcc_assert (chkp_function_instrumented_p (fndecl));
+
+ bdata.parm_data = data;
+ bdata.bounds_parm = parm;
+ bdata.ptr_parm = last_arg;
+ bdata.ptr_entry = last_arg_entry;
+ bdata.bound_no = bound_no;
+ bndargs.safe_push (bdata);
+ }
+ else
+ {
+ assign_parm_adjust_stack_rtl (&data);
+
+ if (assign_parm_setup_block_p (&data))
+ assign_parm_setup_block (&all, parm, &data);
+ else if (data.passed_pointer || use_register_for_decl (parm))
+ assign_parm_setup_reg (&all, parm, &data);
+ else
+ assign_parm_setup_stack (&all, parm, &data);
+ }
+
+ if (cfun->stdarg && !DECL_CHAIN (parm))
+ {
+ int pretend_bytes = 0;
+
+ assign_parms_setup_varargs (&all, &data, false);
+
+ if (chkp_function_instrumented_p (fndecl))
+ {
+ /* We expect this is the last parm. Otherwise it is wrong
+ to assign bounds right now. */
+ gcc_assert (i == (fnargs.length () - 1));
+ assign_bounds (bndargs, all);
+ targetm.calls.setup_incoming_vararg_bounds (all.args_so_far,
+ data.promoted_mode,
+ data.passed_type,
+ &pretend_bytes,
+ false);
+ }
+ }
+
/* Update info on where next arg arrives in registers. */
targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
data.passed_type, data.named_arg);

- assign_parm_adjust_stack_rtl (&data);
-
- if (assign_parm_setup_block_p (&data))
- assign_parm_setup_block (&all, parm, &data);
- else if (data.passed_pointer || use_register_for_decl (parm))
- assign_parm_setup_reg (&all, parm, &data);
- else
- assign_parm_setup_stack (&all, parm, &data);
+ if (POINTER_BOUNDS_TYPE_P (data.passed_type))
+ bound_no++;
}

+ assign_bounds (bndargs, all);
+
if (targetm.calls.split_complex_arg)
assign_parms_unsplit_complex (&all, fnargs);

@@ -3574,6 +3779,10 @@ assign_parms (tree fndecl)

real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result),
fndecl, true);
+ if (chkp_function_instrumented_p (fndecl))
+ crtl->return_bnd
+ = targetm.calls.chkp_function_value_bounds (TREE_TYPE (decl_result),
+ fndecl, true);
REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
/* The delay slot scheduler assumes that crtl->return_rtx
holds the hard register containing the return value, not a
@@ -4804,6 +5013,14 @@ expand_function_start (tree subr)
/* Set DECL_REGISTER flag so that expand_function_end will copy the
result to the real return register(s). */
DECL_REGISTER (DECL_RESULT (subr)) = 1;
+
+ if (chkp_function_instrumented_p (current_function_decl))
+ {
+ tree return_type = TREE_TYPE (DECL_RESULT (subr));
+ rtx bounds = targetm.calls.chkp_function_value_bounds (return_type,
+ subr, 1);
+ SET_DECL_BOUNDS_RTL (DECL_RESULT (subr), bounds);
+ }
}

/* Initialize rtx for parameters and local variables.
@@ -4907,14 +5124,11 @@ expand_dummy_function_end (void)
in_dummy_function = false;
}

-/* Call DOIT for each hard register used as a return value from
- the current function. */
+/* Helper for diddle_return_value. */

void
-diddle_return_value (void (*doit) (rtx, void *), void *arg)
+diddle_return_value_1 (void (*doit) (rtx, void *), void *arg, rtx outgoing)
{
- rtx outgoing = crtl->return_rtx;
-
if (! outgoing)
return;

@@ -4934,6 +5148,16 @@ diddle_return_value (void (*doit) (rtx, void *), void *arg)
}
}

+/* Call DOIT for each hard register used as a return value from
+ the current function. */
+
+void
+diddle_return_value (void (*doit) (rtx, void *), void *arg)
+{
+ diddle_return_value_1 (doit, arg, crtl->return_rtx);
+ diddle_return_value_1 (doit, arg, crtl->return_bnd);
+}
+
static void
do_clobber_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED)
{
diff --git a/gcc/function.h b/gcc/function.h
index e71210d..5cd0149 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -253,6 +253,9 @@ struct GTY(()) rtl_data {
result in a register, current_function_return_rtx will always be
the hard register containing the result. */
rtx return_rtx;
+ /* If nonxero, an RTL expression for the lcoation at which the current
+ function returns bounds for its result. */
+ rtx return_bnd;

/* Vector of initial-value pairs. Each pair consists of a pseudo
register of approprite mode that stores the initial value a hard
diff --git a/gcc/target.def b/gcc/target.def
index 7952f02..ae01d82 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4088,6 +4088,12 @@ The return value is usually either a @code{reg} RTX for the hard\n\
register in which to pass the argument, or zero to pass the argument\n\
on the stack.\n\
\n\
+The return value can be a @code{const_int} which means argument is\n\
+passed in a target specific slot with specified number. Target hooks\n\
+should be used to store or load argument in such case. See\n\
+@code{TARGET_STORE_BOUNDS_FOR_ARG} and @code{TARGET_LOAD_BOUNDS_FOR_ARG}\n\
+for more information.\n\
+\n\
The value of the expression can also be a @code{parallel} ***@. This is\n\
used when an argument is passed in multiple locations. The mode of the\n\
@code{parallel} should be the mode of the entire argument. The\n\
Jeff Law
2014-10-03 20:07:25 UTC
Permalink
Post by Ilya Enkovich
Here is an updated version.
Thanks,
Ilya
--
* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
* target.def (TARGET_FUNCTION_ARG): Update hook description with new
possible return value CONST_INT.
* doc/tm.texi: Regenerate.
OK Thanks for your patience with my questions/concerns.

jeff

Loading...