2014-06-11 18:00 GMT+04:00 Ilya Enkovich <[email protected]>:
> Hi,
>
> This patch adds i386 target hooks for Pointer Bounds Checker.
>
> Bootstrapped and tested on linux-x86_64.
>
> Thanks,
> Ilya
> --
> gcc/
>
> 2014-06-11 Ilya Enkovich <[email protected]>
>
> * config/i386/i386.c: Include tree-iterator.h.
> (ix86_function_value_bounds): New.
> (ix86_builtin_mpx_function): New.
> (ix86_load_bounds): New.
> (ix86_store_bounds): New.
> (ix86_load_returned_bounds): New.
> (ix86_store_returned_bounds): New.
> (ix86_fn_abi_va_list_bounds_size): New.
> (ix86_mpx_bound_mode): New.
> (ix86_make_bounds_constant): New.
> (ix86_initialize_bounds):
> (TARGET_LOAD_BOUNDS_FOR_ARG): New.
> (TARGET_STORE_BOUNDS_FOR_ARG): New.
> (TARGET_LOAD_RETURNED_BOUNDS): New.
> (TARGET_STORE_RETURNED_BOUNDS): New.
> (TARGET_CHKP_BOUND_MODE): New.
> (TARGET_BUILTIN_CHKP_FUNCTION): New.
> (TARGET_FN_ABI_VA_LIST_BOUNDS_SIZE): New.
> (TARGET_CHKP_FUNCTION_VALUE_BOUNDS): New.
> (TARGET_CHKP_MAKE_BOUNDS_CONSTANT): New.
> (TARGET_CHKP_INITIALIZE_BOUNDS): New.
I have comments from the implementation side, but IMO Jeff (CC'd)
should give the final approval on the functionality and general
approach of the patch. I was not able to follow the meaning and logic
behind SLOT (which may be register ?), PTR, TO, and special BND
addresses.
Uros.
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index ac79231..dac83d0 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -81,6 +81,7 @@ along with GCC; see the file COPYING3. If not see
> #include "context.h"
> #include "pass_manager.h"
> #include "target-globals.h"
> +#include "tree-iterator.h"
> #include "tree-chkp.h"
> #include "rtl-chkp.h"
>
> @@ -7971,6 +7972,39 @@ ix86_function_value (const_tree valtype, const_tree
> fntype_or_decl,
> return ix86_function_value_1 (valtype, fntype_or_decl, orig_mode, mode);
> }
>
> +static rtx
> +ix86_function_value_bounds (const_tree valtype,
> + const_tree fntype_or_decl ATTRIBUTE_UNUSED,
> + bool outgoing ATTRIBUTE_UNUSED)
> +{
> + rtx res = NULL_RTX;
> +
> + if (BOUNDED_TYPE_P (valtype))
> + res = gen_rtx_REG (BNDmode, FIRST_BND_REG);
> + else if (chkp_type_has_pointer (valtype))
> + {
> + bitmap slots = chkp_find_bound_slots (valtype);
> + rtx bounds[2];
> + bitmap_iterator bi;
> + unsigned i, bnd_no = 0;
> +
> + EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi)
> + {
> + rtx reg = gen_rtx_REG (BNDmode, FIRST_BND_REG + bnd_no);
> + rtx offs = GEN_INT (i * POINTER_SIZE / BITS_PER_UNIT);
> + gcc_assert (bnd_no < 2);
> + bounds[bnd_no++] = gen_rtx_EXPR_LIST (VOIDmode, reg, offs);
> + }
> +
> + res = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (bnd_no, bounds));
> + BITMAP_FREE (slots);
> + }
> + else
> + res = NULL_RTX;
> +
> + return res;
> +}
> +
> /* Pointer function arguments and return values are promoted to
> word_mode. */
>
> @@ -36620,6 +36654,173 @@ static tree ix86_get_builtin (enum ix86_builtins
> code)
> return NULL_TREE;
> }
>
> +/* Return function decl for target specific builtin
> + for given MPX builtin passed i FCODE. */
> +static tree
> +ix86_builtin_mpx_function (unsigned fcode)
> +{
> + switch (fcode)
> + {
> + case BUILT_IN_CHKP_BNDMK:
> + return ix86_builtins[IX86_BUILTIN_BNDMK];
> +
> + case BUILT_IN_CHKP_BNDSTX:
> + return ix86_builtins[IX86_BUILTIN_BNDSTX];
> +
> + case BUILT_IN_CHKP_BNDLDX:
> + return ix86_builtins[IX86_BUILTIN_BNDLDX];
> +
> + case BUILT_IN_CHKP_BNDCL:
> + return ix86_builtins[IX86_BUILTIN_BNDCL];
> +
> + case BUILT_IN_CHKP_BNDCU:
> + return ix86_builtins[IX86_BUILTIN_BNDCU];
> +
> + case BUILT_IN_CHKP_BNDRET:
> + return ix86_builtins[IX86_BUILTIN_BNDRET];
> +
> + case BUILT_IN_CHKP_INTERSECT:
> + return ix86_builtins[IX86_BUILTIN_BNDINT];
> +
> + case BUILT_IN_CHKP_NARROW:
> + return ix86_builtins[IX86_BUILTIN_BNDNARROW];
> +
> + case BUILT_IN_CHKP_SIZEOF:
> + return ix86_builtins[IX86_BUILTIN_SIZEOF];
> +
> + case BUILT_IN_CHKP_EXTRACT_LOWER:
> + return ix86_builtins[IX86_BUILTIN_BNDLOWER];
> +
> + case BUILT_IN_CHKP_EXTRACT_UPPER:
> + return ix86_builtins[IX86_BUILTIN_BNDUPPER];
> +
> + default:
> + return NULL_TREE;
> + }
> +
> + gcc_unreachable ();
> +}
> +
> +/* Load bounds PTR pointer value loaded from SLOT.
> + if SLOT is a register then load bounds associated
> + with special address identified by BND.
> +
> + Return loaded bounds. */
> +static rtx
> +ix86_load_bounds (rtx slot, rtx ptr, rtx bnd)
> +{
> + rtx addr = NULL;
> + rtx reg;
> +
> + if (!ptr)
> + {
> + gcc_assert (MEM_P (slot));
> + ptr = copy_to_mode_reg (Pmode, slot);
copy_addr_to_reg
> + }
> +
> + if (!slot || REG_P (slot))
> + {
> + if (slot)
> + ptr = slot;
> +
> + gcc_assert (CONST_INT_P (bnd));
> +
> + /* Here we have the case when more than four pointers are
> + passed in registers. In this case we are out of bound
> + registers and have to use bndldx to load bound. RA,
> + RA - 8, etc. are used for address translation in bndldx. */
> + addr = plus_constant (Pmode, arg_pointer_rtx, -(INTVAL (bnd) + 1) * 8);
Magic value 8 refers to what?
> + }
> + else if (MEM_P (slot))
> + {
> + addr = XEXP (slot, 0);
> + addr = force_reg (Pmode, addr);
> + }
> + else
> + gcc_unreachable ();
> +
> + ptr = force_reg (Pmode, ptr);
> + /* If ptr was a register originally then it may have
> + mode other than Pmode. We need to extend in such
> + case because bndldx may work only with Pmode regs. */
> + if (GET_MODE (ptr) != Pmode)
> + {
> + rtx ext = gen_rtx_ZERO_EXTEND (Pmode, ptr);
> + ptr = gen_reg_rtx (Pmode);
> + emit_move_insn (ptr, ext);
> + }
Please use ix86_zero_extend_to_Pmode instead.
> + reg = gen_reg_rtx (BNDmode);
> + emit_insn (TARGET_64BIT
Check for BNDmode here, as in patch 31/x.
> + ? gen_bnd64_ldx (reg, addr, ptr)
> + : gen_bnd32_ldx (reg, addr, ptr));
> +
> + return reg;
> +}
> +
> +/* Store bounds BOUNDS for PTR pointer value stored in
> + specified ADDR. If ADDR is a register then TO identifies
> + which special address to use for bounds store. */
> +static void
> +ix86_store_bounds (rtx ptr, rtx addr, rtx bounds, rtx to)
> +{
> + if (!ptr)
> + {
> + gcc_assert (MEM_P (addr));
> + ptr = copy_to_mode_reg (Pmode, addr);
copy_addr_to_reg
> + }
> +
> + if (!addr || REG_P (addr))
> + {
> + gcc_assert (CONST_INT_P (to));
> + addr = plus_constant (Pmode, stack_pointer_rtx, -(INTVAL (to) + 1) *
> 8);
What is magic number 8? Size of Pmode pointer, different between 32bit
and 64bit targets?
> + }
> + else if (MEM_P (addr))
> + addr = XEXP (addr, 0);
> + else
> + gcc_unreachable ();
> +
> + /* Should we also ignore integer modes of incorrect size?. */
> + ptr = force_reg (Pmode, ptr);
> + addr = force_reg (Pmode, addr);
> +
> + /* Avoid registers which connot be used as index. */
> + if (!index_register_operand (ptr, Pmode))
> + {
> + rtx temp = gen_reg_rtx (Pmode);
> + emit_move_insn (temp, ptr);
> + ptr = temp;
> + }
Please merge together handling of ptr. Something like:
if (ptr)
{
if (!index_register_operand (ptr, Pmode))
ptr = copy_addr_to_reg (ptr);
}
else
{
gcc_assert (MEM_P (addr))
ptr = copy_addr_to_reg (addr);
}
if (!addr || REG_P (addr)
{
...
}
> +
> + gcc_assert (POINTER_BOUNDS_MODE_P (GET_MODE (bounds)));
> + bounds = force_reg (GET_MODE (bounds), bounds);
> +
> + emit_insn (TARGET_64BIT
Check BNDmode.
> + ? gen_bnd64_stx (addr, ptr, bounds)
> + : gen_bnd32_stx (addr, ptr, bounds));
> +}
> +
> +/* Load and return bounds returned by function in SLOT. */
> +static rtx
> +ix86_load_returned_bounds (rtx slot)
> +{
> + rtx res;
> +
> + gcc_assert (REG_P (slot));
> + res = gen_reg_rtx (BNDmode);
> + emit_move_insn (res, slot);
> +
> + return res;
> +}
> +
> +/* Store BOUNDS returned by function into SLOT. */
> +static void
> +ix86_store_returned_bounds (rtx slot, rtx bounds)
> +{
> + gcc_assert (REG_P (slot));
> + emit_move_insn (slot, bounds);
> +}
> +
> /* Returns a function decl for a vectorized version of the builtin function
> with builtin function code FN and the result vector type TYPE, or
> NULL_TREE
> if it is not available. */
> @@ -45796,6 +45997,22 @@ ix86_fn_abi_va_list (tree fndecl)
> return sysv_va_list_type_node;
> }
>
> +/* This function returns size of bounds for the calling abi
> + specific va_list node. */
> +
> +static tree
> +ix86_fn_abi_va_list_bounds_size (tree fndecl)
> +{
> + if (!TARGET_64BIT)
> + return integer_zero_node;
> + gcc_assert (fndecl != NULL_TREE);
> +
> + if (ix86_function_abi ((const_tree) fndecl) == MS_ABI)
> + return integer_zero_node;
> + else
> + return TYPE_SIZE (sysv_va_list_type_node);
> +}
> +
> /* Returns the canonical va_list type specified by TYPE. If there
> is no valid TYPE provided, it return NULL_TREE. */
>
> @@ -47246,6 +47463,61 @@ ix86_atomic_assign_expand_fenv (tree *hold, tree
> *clear, tree *update)
> atomic_feraiseexcept_call);
> }
>
> +static enum machine_mode
> +ix86_mpx_bound_mode ()
> +{
> + /* Do not support pointer checker if MPX
> + is not enabled. */
> + if (!TARGET_MPX)
> + {
> + if (flag_check_pointer_bounds)
> + warning (0, "Pointer Checker requires MPX support on this target."
> + " Use -mmpx options to enable MPX.");
> + return VOIDmode;
> + }
> +
> + return BNDmode;
> +}
> +
> +static tree
> +ix86_make_bounds_constant (HOST_WIDE_INT lb, HOST_WIDE_INT ub)
> +{
> + tree low = lb ? build_minus_one_cst (pointer_sized_int_node)
> + : build_zero_cst (pointer_sized_int_node);
> + tree high = ub ? build_zero_cst (pointer_sized_int_node)
> + : build_minus_one_cst (pointer_sized_int_node);
> +
> + /* This function is supposed to be used to create zero and
> + none bounds only. */
> + gcc_assert (lb == 0 || lb == -1);
> + gcc_assert (ub == 0 || ub == -1);
> +
> + return build_complex (NULL, low, high);
> +}
> +
> +static int
> +ix86_initialize_bounds (tree var, tree lb, tree ub, tree *stmts)
> +{
> + tree size_ptr = build_pointer_type (size_type_node);
> + tree lhs, modify, var_p;
> +
> + ub = build1 (BIT_NOT_EXPR, size_type_node, ub);
> + var_p = build1 (CONVERT_EXPR, size_ptr,
> + build_fold_addr_expr (var));
> +
> + lhs = build1 (INDIRECT_REF, size_type_node, var_p);
> + modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, lb);
> + append_to_statement_list (modify, stmts);
> +
> + lhs = build1 (INDIRECT_REF, size_type_node,
> + build2 (POINTER_PLUS_EXPR, size_ptr, var_p,
> + TYPE_SIZE_UNIT (size_type_node)));
> + modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, ub);
> + append_to_statement_list (modify, stmts);
> +
> + return 2;
> +}
> +
> /* Initialize the GCC target structure. */
> #undef TARGET_RETURN_IN_MEMORY
> #define TARGET_RETURN_IN_MEMORY ix86_return_in_memory
> @@ -47642,6 +47914,36 @@ ix86_atomic_assign_expand_fenv (tree *hold, tree
> *clear, tree *update)
> #define TARGET_FLOAT_EXCEPTIONS_ROUNDING_SUPPORTED_P \
> ix86_float_exceptions_rounding_supported_p
>
> +#undef TARGET_LOAD_BOUNDS_FOR_ARG
> +#define TARGET_LOAD_BOUNDS_FOR_ARG ix86_load_bounds
> +
> +#undef TARGET_STORE_BOUNDS_FOR_ARG
> +#define TARGET_STORE_BOUNDS_FOR_ARG ix86_store_bounds
> +
> +#undef TARGET_LOAD_RETURNED_BOUNDS
> +#define TARGET_LOAD_RETURNED_BOUNDS ix86_load_returned_bounds
> +
> +#undef TARGET_STORE_RETURNED_BOUNDS
> +#define TARGET_STORE_RETURNED_BOUNDS ix86_store_returned_bounds
> +
> +#undef TARGET_CHKP_BOUND_MODE
> +#define TARGET_CHKP_BOUND_MODE ix86_mpx_bound_mode
> +
> +#undef TARGET_BUILTIN_CHKP_FUNCTION
> +#define TARGET_BUILTIN_CHKP_FUNCTION ix86_builtin_mpx_function
> +
> +#undef TARGET_FN_ABI_VA_LIST_BOUNDS_SIZE
> +#define TARGET_FN_ABI_VA_LIST_BOUNDS_SIZE ix86_fn_abi_va_list_bounds_size
> +
> +#undef TARGET_CHKP_FUNCTION_VALUE_BOUNDS
> +#define TARGET_CHKP_FUNCTION_VALUE_BOUNDS ix86_function_value_bounds
> +
> +#undef TARGET_CHKP_MAKE_BOUNDS_CONSTANT
> +#define TARGET_CHKP_MAKE_BOUNDS_CONSTANT ix86_make_bounds_constant
> +
> +#undef TARGET_CHKP_INITIALIZE_BOUNDS
> +#define TARGET_CHKP_INITIALIZE_BOUNDS ix86_initialize_bounds
> +
> struct gcc_target targetm = TARGET_INITIALIZER;
>
> #include "gt-i386.h"