From patchwork Thu Jun 5 14:46:22 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ilya Enkovich X-Patchwork-Id: 356479 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id B5D2E14007B for ; Fri, 6 Jun 2014 00:46:47 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=F9v/pvpeQW6fogk6+ AkappYXC84+KoBEQSCdCAxPMeXv3BjfyeCTJfvA83zEDSd0M4g+0zjz30NERh2ms GTr9A0rO6ccaYceI6gssN6n5t4FFwqFwTTKZxsyl/tJjBUqe3ax7eNMrxNXHPLLE N2opJSi2WN29McvwpIboR+CvQw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=default; bh=xIE+KBAhMr0w0nM3/iFIuYi ekro=; b=axBbH74GNcEArd8IW0/g/lNpjurZ5FOHY/qBQmXr62yLvH/oZLd1Gdy VSPJMk9sKB5GvcSbej3DRrLNHI9/mrvmLmV+rdvZTSbE4dzmsv7sHo2WPY6lLQ2W SW1K1CoyxcYsP6ium0nQ+///nypT+wvYRAEUmsoZ6m+zi7qRcpaE= Received: (qmail 10984 invoked by alias); 5 Jun 2014 14:46:39 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 10971 invoked by uid 89); 5 Jun 2014 14:46:38 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.0 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-pd0-f172.google.com Received: from mail-pd0-f172.google.com (HELO mail-pd0-f172.google.com) (209.85.192.172) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Thu, 05 Jun 2014 14:46:35 +0000 Received: by mail-pd0-f172.google.com with SMTP id fp1so1184029pdb.17 for ; Thu, 05 Jun 2014 07:46:33 -0700 (PDT) X-Received: by 10.68.193.100 with SMTP id hn4mr76913112pbc.50.1401979593193; Thu, 05 Jun 2014 07:46:33 -0700 (PDT) Received: from msticlxl57.ims.intel.com (fmdmzpr03-ext.fm.intel.com. [192.55.54.38]) by mx.google.com with ESMTPSA id xk3sm23715312pbb.65.2014.06.05.07.46.30 for (version=TLSv1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 05 Jun 2014 07:46:32 -0700 (PDT) Date: Thu, 5 Jun 2014 18:46:22 +0400 From: Ilya Enkovich To: Michael Matz Cc: gcc-patches Subject: Re: [PATCH, Pointer Bounds Checker 19/x] Support bounds in expand Message-ID: <20140605141201.GA38634@msticlxl57.ims.intel.com> References: <20140602150245.GB53659@msticlxl57.ims.intel.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes On 04 Jun 16:36, Michael Matz wrote: > Hi, > > On Mon, 2 Jun 2014, Ilya Enkovich wrote: > > > > 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 * 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 &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 fnargs; - unsigned i; + unsigned i, bound_no = 0; + tree last_arg = NULL; + rtx last_arg_entry = NULL; + vec 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