[PATCH, i386, Pointer Bounds Checker 32/x] Pointer Bounds Checker hooks for i386 target

Ilya Enkovich enkovich.gnu@gmail.com
Fri Sep 19 12:53:00 GMT 2014


On 18 Sep 21:34, Uros Bizjak wrote:
> 2014-06-11 18:00 GMT+04:00 Ilya Enkovich <enkovich.gnu@gmail.com>:
> > 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  <ilya.enkovich@intel.com>
> >
> >         * 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.
> 
> > +
> > +      /* 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?

This code is supposed to work for 64 bit target only and 8 is a size of Pmode.  I'll replace this and other cases with mode size.

> 
> > +    }
> > +  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));

New version with fixes and better documentation for ix86_load_bounds and ix86_store_bounds is below.

Thanks,
Ilya
--
2014-09-18  Ilya Enkovich  <ilya.enkovich@intel.com>

	* 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_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_CHKP_FUNCTION_VALUE_BOUNDS): New.
	(TARGET_CHKP_MAKE_BOUNDS_CONSTANT): New.
	(TARGET_CHKP_INITIALIZE_BOUNDS): New.


diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d493983..9549cb3 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -85,6 +85,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-vectorizer.h"
 #include "shrink-wrap.h"
 #include "builtins.h"
+#include "tree-iterator.h"
 #include "tree-chkp.h"
 #include "rtl-chkp.h"
 
@@ -8001,6 +8002,39 @@ ix86_function_value (const_tree valtype, const_tree fntype_or_decl, bool)
   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.  */
 
@@ -36754,6 +36788,193 @@ 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 ();
+}
+
+/* Expand pass uses this hook to load bounds for function parameter
+   PTR passed in SLOT in case its bounds are not passed in a register.
+
+   If SLOT is a memory, then bounds are loaded as for regular pointer
+   loaded from memory.  PTR may be NULL in case SLOT is a memory.
+   In such case value of PTR (if required) may be loaded from SLOT.
+
+   If SLOT is NULL or a register then SLOT_NO is an integer constant
+   holding number of the target dependent special slot which should be
+   used to obtain bounds.
+
+   Return loaded bounds.  */
+
+static rtx
+ix86_load_bounds (rtx slot, rtx ptr, rtx slot_no)
+{
+  rtx addr = NULL;
+  rtx reg;
+
+  if (!ptr)
+    {
+      gcc_assert (MEM_P (slot));
+      ptr = copy_addr_to_reg (slot);
+    }
+
+  if (!slot || REG_P (slot))
+    {
+      if (slot)
+	ptr = slot;
+
+      gcc_assert (CONST_INT_P (slot_no));
+
+      /* 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 (slot_no) + 1) * GET_MODE_SIZE (Pmode));
+    }
+  else if (MEM_P (slot))
+    {
+      addr = XEXP (slot, 0);
+      if (!register_operand (addr, Pmode))
+	addr = copy_addr_to_reg (addr);
+    }
+  else
+    gcc_unreachable ();
+
+  if (!register_operand (ptr, Pmode))
+    ptr = copy_addr_to_reg (ptr);
+
+  reg = gen_reg_rtx (BNDmode);
+  emit_insn (BNDmode == BND64mode
+	     ? gen_bnd64_ldx (reg, addr, ptr)
+	     : gen_bnd32_ldx (reg, addr, ptr));
+
+  return reg;
+}
+
+/* Expand pass uses this hook to store BOUNDS for call argument PTR
+   passed in SLOT in case BOUNDS are not passed in a register.
+
+   If SLOT is a memory, then BOUNDS are stored as for regular pointer
+   stored in memory.  PTR may be NULL in case SLOT is a memory.
+   In such case value of PTR (if required) may be loaded from SLOT.
+
+   If SLOT is NULL or a register then SLOT_NO is an integer constant
+   holding number of the target dependent special slot which should be
+   used to store BOUNDS.  */
+
+static void
+ix86_store_bounds (rtx ptr, rtx slot, rtx bounds, rtx slot_no)
+{
+  rtx addr;
+
+  if (ptr)
+    {
+      if (!register_operand (ptr, Pmode))
+	ptr = copy_addr_to_reg (ptr);
+    }
+  else
+    {
+      gcc_assert (MEM_P (slot));
+      ptr = copy_addr_to_reg (slot);
+    }
+
+  if (!slot || REG_P (slot))
+    {
+      gcc_assert (CONST_INT_P (slot_no));
+      addr = plus_constant (Pmode, stack_pointer_rtx,
+			    -(INTVAL (slot_no) + 1) * GET_MODE_SIZE (Pmode));
+    }
+  else if (MEM_P (slot))
+    addr = XEXP (slot, 0);
+  else
+    gcc_unreachable ();
+
+  if (!register_operand (addr, Pmode))
+    addr = copy_addr_to_reg (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;
+    }
+
+  gcc_assert (POINTER_BOUNDS_MODE_P (GET_MODE (bounds)));
+  if (!register_operand (bounds, BNDmode))
+    bounds = copy_to_mode_reg (BNDmode, bounds);
+
+  emit_insn (BNDmode == BND64mode
+	     ? 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.  */
@@ -47480,6 +47701,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
@@ -47897,6 +48173,33 @@ ix86_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
 #undef TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS
 #define TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS true
 
+#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_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"



More information about the Gcc-patches mailing list