diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c
index c546d97..2ddd25f 100644
--- a/gcc/tree-chkp.c
+++ b/gcc/tree-chkp.c
@@ -1042,6 +1042,29 @@ chkp_get_registered_bounds (tree ptr)
   return slot ? *slot : NULL_TREE;
 }
 
+/* Add bound retvals to return statement pointed by GSI.  */
+
+static void
+chkp_add_bounds_to_ret_stmt (gimple_stmt_iterator *gsi)
+{
+  gimple ret = gsi_stmt (*gsi);
+  tree retval = gimple_return_retval (ret);
+  tree ret_decl = DECL_RESULT (cfun->decl);
+  tree bounds;
+
+  if (!retval)
+    return;
+
+  if (BOUNDED_P (ret_decl))
+    {
+      bounds = chkp_find_bounds (retval, gsi);
+      bounds = chkp_maybe_copy_and_register_bounds (ret_decl, bounds);
+      gimple_return_set_retbnd (ret, bounds);
+    }
+
+  update_stmt (ret);
+}
+
 /* Force OP to be suitable for using as an argument for call.
    New statements (if any) go to SEQ.  */
 static tree
@@ -1166,6 +1189,64 @@ chkp_check_mem_access (tree first, tree last, tree bounds,
   chkp_check_upper (last, bounds, iter, location, dirflag);
 }
 
+/* Replace call to _bnd_chk_* pointed by GSI with
+   bndcu and bndcl calls.  DIRFLAG determines whether
+   check is for read or write.  */
+
+void
+chkp_replace_address_check_builtin (gimple_stmt_iterator *gsi,
+				    tree dirflag)
+{
+  gimple_stmt_iterator call_iter = *gsi;
+  gimple call = gsi_stmt (*gsi);
+  tree fndecl = gimple_call_fndecl (call);
+  tree addr = gimple_call_arg (call, 0);
+  tree bounds = chkp_find_bounds (addr, gsi);
+
+  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
+      || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
+    chkp_check_lower (addr, bounds, *gsi, gimple_location (call), dirflag);
+
+  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS)
+    chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
+
+  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
+    {
+      tree size = gimple_call_arg (call, 1);
+      addr = fold_build_pointer_plus (addr, size);
+      addr = fold_build_pointer_plus_hwi (addr, -1);
+      chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
+    }
+
+  gsi_remove (&call_iter, true);
+}
+
+/* Replace call to _bnd_get_ptr_* pointed by GSI with
+   corresponding bounds extract call.  */
+
+void
+chkp_replace_extract_builtin (gimple_stmt_iterator *gsi)
+{
+  gimple call = gsi_stmt (*gsi);
+  tree fndecl = gimple_call_fndecl (call);
+  tree addr = gimple_call_arg (call, 0);
+  tree bounds = chkp_find_bounds (addr, gsi);
+  gimple extract;
+
+  if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND)
+    fndecl = chkp_extract_lower_fndecl;
+  else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND)
+    fndecl = chkp_extract_upper_fndecl;
+  else
+    gcc_unreachable ();
+
+  extract = gimple_build_call (fndecl, 1, bounds);
+  gimple_call_set_lhs (extract, gimple_call_lhs (call));
+  chkp_mark_stmt (extract);
+
+  gsi_replace (gsi, extract, false);
+}
+
 /* Return COMPONENT_REF accessing FIELD in OBJ.  */
 static tree
 chkp_build_component_ref (tree obj, tree field)
@@ -1227,6 +1308,78 @@ chkp_build_array_ref (tree arr, tree etype, tree esize,
   return res;
 }
 
+/* Helper function for chkp_add_bounds_to_call_stmt.
+   Fill ALL_BOUNDS output array with created bounds.
+
+   OFFS is used for recursive calls and holds basic
+   offset of TYPE in outer structure in bits.
+
+   ITER points a position where bounds are searched.
+
+   ALL_BOUNDS[i] is filled with elem bounds if there
+   is a field in TYPE which has pointer type and offset
+   equal to i * POINTER_SIZE in bits.  */
+static void
+chkp_find_bounds_for_elem (tree elem, tree *all_bounds,
+			   HOST_WIDE_INT offs,
+			   gimple_stmt_iterator *iter)
+{
+  tree type = TREE_TYPE (elem);
+
+  if (BOUNDED_TYPE_P (type))
+    {
+      if (!all_bounds[offs / POINTER_SIZE])
+	{
+	  tree temp = make_temp_ssa_name (type, gimple_build_nop (), "");
+	  gimple assign = gimple_build_assign (temp, elem);
+	  gimple_stmt_iterator gsi;
+
+	  gsi_insert_before (iter, assign, GSI_SAME_STMT);
+	  gsi = gsi_for_stmt (assign);
+
+	  all_bounds[offs / POINTER_SIZE] = chkp_find_bounds (temp, &gsi);
+	}
+    }
+  else if (RECORD_OR_UNION_TYPE_P (type))
+    {
+      tree field;
+
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	if (TREE_CODE (field) == FIELD_DECL)
+	  {
+	    tree base = unshare_expr (elem);
+	    tree field_ref = chkp_build_component_ref (base, field);
+	    HOST_WIDE_INT field_offs
+	      = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
+	    if (DECL_FIELD_OFFSET (field))
+	      field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8;
+
+	    chkp_find_bounds_for_elem (field_ref, all_bounds,
+				       offs + field_offs, iter);
+	  }
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
+      tree etype = TREE_TYPE (type);
+      HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
+      unsigned HOST_WIDE_INT cur;
+
+      if (!maxval || integer_minus_onep (maxval))
+	return;
+
+      for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
+	{
+	  tree base = unshare_expr (elem);
+	  tree arr_elem = chkp_build_array_ref (base, etype,
+						TYPE_SIZE (etype),
+						cur);
+	  chkp_find_bounds_for_elem (arr_elem, all_bounds, offs + cur * esize,
+				     iter);
+	}
+    }
+}
+
 /* Fill HAVE_BOUND output bitmap with information about
    bounds requred for object of type TYPE.
 
@@ -1286,6 +1439,223 @@ chkp_find_bound_slots (const_tree type)
   return res;
 }
 
+/* Add bound arguments to call statement pointed by GSI.
+   Also performs a replacement of user checker builtins calls
+   with internal ones.  */
+
+static void
+chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
+{
+  gimple call = gsi_stmt (*gsi);
+  unsigned arg_no = 0;
+  tree fndecl = gimple_call_fndecl (call);
+  tree fntype;
+  tree first_formal_arg;
+  tree arg;
+  bool use_fntype = false;
+  tree op;
+  ssa_op_iter iter;
+  gimple new_call;
+
+  /* Do nothing for internal functions.  */
+  if (gimple_call_internal_p (call))
+    return;
+
+  fntype = TREE_TYPE (TREE_TYPE (gimple_call_fn (call)));
+
+  /* Do nothing if back-end builtin is called.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+    return;
+
+  /* Do nothing for some middle-end builtins.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE)
+    return;
+
+  /* Donothing for calls to legacy functions.  */
+  if (fndecl
+      && lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl)))
+    return;
+
+  /* Ignore CHKP_INIT_PTR_BOUNDS, CHKP_NULL_PTR_BOUNDS
+     and CHKP_COPY_PTR_BOUNDS.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS
+	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS
+	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS
+	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS))
+    return;
+
+  /* Check user builtins are replaced with checks.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
+	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS
+	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS))
+    {
+      chkp_replace_address_check_builtin (gsi, integer_minus_one_node);
+      return;
+    }
+
+  /* Check user builtins are replaced with bound extract.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND
+	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND))
+    {
+      chkp_replace_extract_builtin (gsi);
+      return;
+    }
+
+  /* BUILT_IN_CHKP_NARROW_PTR_BOUNDS call is replaced with
+     target narrow bounds call.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
+    {
+      tree arg = gimple_call_arg (call, 1);
+      tree bounds = chkp_find_bounds (arg, gsi);
+
+      gimple_call_set_fndecl (call, chkp_narrow_bounds_fndecl);
+      gimple_call_set_arg (call, 1, bounds);
+      update_stmt (call);
+
+      return;
+    }
+
+  /* BUILT_IN_CHKP_STORE_PTR_BOUNDS call is replaced with
+     bndstx call.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_STORE_PTR_BOUNDS)
+    {
+      tree addr = gimple_call_arg (call, 0);
+      tree ptr = gimple_call_arg (call, 1);
+      tree bounds = chkp_find_bounds (ptr, gsi);
+      gimple_stmt_iterator iter = gsi_for_stmt (call);
+
+      chkp_build_bndstx (addr, ptr, bounds, gsi);
+      gsi_remove (&iter, true);
+
+      return;
+    }
+
+  if (!flag_chkp_instrument_calls)
+    return;
+
+  /* Avoid instrumented builtin functions for now.  Due to IPA
+     it also means we have to avoid instrumentation of indirect
+     calls.  */
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN)
+    return;
+
+  /* If function decl is available then use it for
+     formal arguments list.  Otherwise use function type.  */
+  if (fndecl && DECL_ARGUMENTS (fndecl))
+    first_formal_arg = DECL_ARGUMENTS (fndecl);
+  else
+    {
+      first_formal_arg = TYPE_ARG_TYPES (fntype);
+      use_fntype = true;
+    }
+
+  /* Fill vector of new call args.  */
+  vec<tree> new_args = vNULL;
+  new_args.create (gimple_call_num_args (call));
+  arg = first_formal_arg;
+  for (arg_no = 0; arg_no < gimple_call_num_args (call); arg_no++)
+    {
+      tree call_arg = gimple_call_arg (call, arg_no);
+      tree type;
+
+      /* Get arg type using formal argument description
+	 or actual argument type.  */
+      if (arg)
+	if (use_fntype)
+	  if (TREE_VALUE (arg) != void_type_node)
+	    {
+	      type = TREE_VALUE (arg);
+	      arg = TREE_CHAIN (arg);
+	    }
+	  else
+	    type = TREE_TYPE (call_arg);
+	else
+	  {
+	    type = TREE_TYPE (arg);
+	    arg = TREE_CHAIN (arg);
+	  }
+      else
+	type = TREE_TYPE (call_arg);
+
+      new_args.safe_push (call_arg);
+
+      if (BOUNDED_TYPE_P (type)
+	  || pass_by_reference (NULL, TYPE_MODE (type), type, true))
+	new_args.safe_push (chkp_find_bounds (call_arg, gsi));
+      else if (chkp_type_has_pointer (type))
+	{
+	  HOST_WIDE_INT max_bounds
+	    = TREE_INT_CST_LOW (TYPE_SIZE (type)) / POINTER_SIZE;
+	  tree *all_bounds = (tree *)xmalloc (sizeof (tree) * max_bounds);
+	  HOST_WIDE_INT bnd_no;
+
+	  memset (all_bounds, 0, sizeof (tree) * max_bounds);
+
+	  chkp_find_bounds_for_elem (call_arg, all_bounds, 0, gsi);
+
+	  for (bnd_no = 0; bnd_no < max_bounds; bnd_no++)
+	    if (all_bounds[bnd_no])
+	      new_args.safe_push (all_bounds[bnd_no]);
+
+           free (all_bounds);
+	}
+    }
+
+  if (new_args.length () == gimple_call_num_args (call))
+    new_call = call;
+  else
+    {
+      new_call = gimple_build_call_vec (gimple_op (call, 1), new_args);
+      gimple_call_set_lhs (new_call, gimple_call_lhs (call));
+      gimple_call_copy_flags (new_call, call);
+    }
+  new_args.release ();
+
+  /* If we call built-in function and pass no bounds then
+     we do not need to change anything.  */
+  if (new_call == call
+      && fndecl
+      && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)))
+      return;
+
+  /* For direct calls fndecl is replaced with instrumented version.  */
+  if (fndecl)
+    {
+      tree new_decl = chkp_maybe_create_clone (fndecl)->decl;
+      gimple_call_set_fndecl (new_call, new_decl);
+      gimple_call_set_fntype (new_call, TREE_TYPE (new_decl));
+    }
+  /* For indirect call we should fix function pointer type if
+     pass some bounds.  */
+  else if (new_call != call)
+    {
+      tree type = gimple_call_fntype (call);
+      type = chkp_copy_function_type_adding_bounds (type);
+      gimple_call_set_fntype (new_call, type);
+    }
+
+  /* replace old call statement with the new one.  */
+  if (call != new_call)
+    {
+      FOR_EACH_SSA_TREE_OPERAND (op, call, iter, SSA_OP_ALL_DEFS)
+	{
+	  SSA_NAME_DEF_STMT (op) = new_call;
+	}
+      gsi_replace (gsi, new_call, true);
+    }
+  else
+    update_stmt (new_call);
+
+  gimple_call_set_with_bounds (new_call, true);
+}
+
 /* Return constant static bounds var with specified LB and UB
    if such var exists in varpool.  Return NULL otherwise.  */
 static tree
@@ -3291,7 +3661,8 @@ chkp_replace_function_pointers (gimple_stmt_iterator *gsi)
     walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL);
 }
 
-/* This function instruments all statements working with memory.  */
+/* This function instruments all statements working with memory,
+   calls and rets.  */
 static void
 chkp_instrument_function (void)
 {
@@ -3335,12 +3706,22 @@ chkp_instrument_function (void)
 
             case GIMPLE_RETURN:
               if (gimple_return_retval (s) != NULL_TREE)
-		chkp_process_stmt (&i, gimple_return_retval (s),
-				   gimple_location (s),
-				   integer_zero_node,
-				   NULL_TREE, NULL_TREE, safe);
+		{
+		  chkp_process_stmt (&i, gimple_return_retval (s),
+				     gimple_location (s),
+				     integer_zero_node,
+				     NULL_TREE, NULL_TREE, safe);
+
+		  /* Additionally we need to add bounds
+		     to return statement.  */
+		  chkp_add_bounds_to_ret_stmt (&i);
+                }
               break;
 
+	    case GIMPLE_CALL:
+	      chkp_add_bounds_to_call_stmt (&i);
+	      break;
+
             default:
               ;
             }
