diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index fe6b333be6f054855ce5d419959b6af0ff7a561a..b6a19c4e2f7e07fc73858aeb1821eac7053cf9b0 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2019-09-27  Richard Sandiford  <richard.sandiford@arm.com>
+
+	* target.def (check_builtin_call): New target hook.
+	* doc/tm.texi.in (TARGET_CHECK_BUILTIN_CALL): New @hook.
+	* doc/tm.texi: Regenerate.
+
 2019-09-27  Richard Sandiford  <richard.sandiford@arm.com>
 
 	PR tree-optimization/91909
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 93bdf3e07988ec337cb4863143511d83cf0aa97f..7b10957ad02a0b7ee8af2e86be62965bbb944a6d 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,12 @@
+2019-09-27  Richard Sandiford  <richard.sandiford@arm.com>
+
+	* c-common.h (build_function_call_vec): Take the original
+	function decl as an optional final parameter.
+	(check_builtin_function_arguments): Take the original function decl.
+	* c-common.c (check_builtin_function_arguments): Likewise.
+	Handle all built-in functions, not just BUILT_IN_NORMAL ones.
+	Use targetm.check_builtin_call to check BUILT_IN_MD functions.
+
 2019-09-15  Jason Merrill  <jason@redhat.com>
 
 	* c-warn.c (warn_logical_operator): Strip location wrappers.  Don't
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 3756219e5cf6921df320f8eeddf34ecb900b964d..7169813d0f26a1a3621d536748236b4ba8b5d904 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5856,15 +5856,27 @@ builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs,
 /* Verifies the NARGS arguments ARGS to the builtin function FNDECL.
    Returns false if there was an error, otherwise true.  LOC is the
    location of the function; ARG_LOC is a vector of locations of the
-   arguments.  */
+   arguments.  If FNDECL is the result of resolving an overloaded
+   target built-in, ORIG_FNDECL is the original function decl,
+   otherwise it is null.  */
 
 bool
 check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
-				  tree fndecl, int nargs, tree *args)
+				  tree fndecl, tree orig_fndecl,
+				  int nargs, tree *args)
 {
-  if (!fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+  if (!fndecl_built_in_p (fndecl))
     return true;
 
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+    return (!targetm.check_builtin_call
+	    || targetm.check_builtin_call (loc, arg_loc, fndecl,
+					   orig_fndecl, nargs, args));
+
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_FRONTEND)
+    return true;
+
+  gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL);
   switch (DECL_FUNCTION_CODE (fndecl))
     {
     case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index a9f4d0c5c11e38b93db2a985da840c6532879176..c1554f3c3af2083a0bfff4323b14ccb097d1f9f2 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -818,7 +818,7 @@ extern void check_function_arguments_recurse (void (*)
 					      void *, tree,
 					      unsigned HOST_WIDE_INT);
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
-					      tree, int, tree *);
+					      tree, tree, int, tree *);
 extern void check_function_format (const_tree, tree, int, tree *,
 				   vec<location_t> *);
 extern bool attribute_fallthrough_p (tree);
@@ -995,7 +995,8 @@ extern bool c_switch_covers_all_cases_p (splay_tree, tree);
 extern tree build_function_call (location_t, tree, tree);
 
 extern tree build_function_call_vec (location_t, vec<location_t>, tree,
-				     vec<tree, va_gc> *, vec<tree, va_gc> *);
+				     vec<tree, va_gc> *, vec<tree, va_gc> *,
+				     tree = NULL_TREE);
 
 extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *);
 
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog
index 96655493ac008fdf279eec22b34a124a44783cfa..718e5b8b8509ae5fcea958cfd71ebb0c1c0c78ac 100644
--- a/gcc/c/ChangeLog
+++ b/gcc/c/ChangeLog
@@ -1,3 +1,9 @@
+2019-09-27  Richard Sandiford  <richard.sandiford@arm.com>
+
+	* c-typeck.c (build_function_call_vec): Take the original function
+	decl as an optional final parameter.  Pass all built-in calls to
+	check_builtin_function_arguments.
+
 2019-09-20  Eric Botcazou  <ebotcazou@adacore.com>
 
 	PR c/91815
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index d4e12eb93d1d2d774ed4f5964d5434e18d4109ae..cc13fdc84c879df59815c18cedfaef5685e413f4 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3003,6 +3003,8 @@ inform_declaration (tree decl)
 }
 
 /* Build a function call to function FUNCTION with parameters PARAMS.
+   If FUNCTION is the result of resolving an overloaded target built-in,
+   ORIG_FUNDECL is the original function decl, otherwise it is null.
    ORIGTYPES, if not NULL, is a vector of types; each element is
    either NULL or the original type of the corresponding element in
    PARAMS.  The original type may differ from TREE_TYPE of the
@@ -3013,7 +3015,7 @@ inform_declaration (tree decl)
 tree
 build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 			 tree function, vec<tree, va_gc> *params,
-			 vec<tree, va_gc> *origtypes)
+			 vec<tree, va_gc> *origtypes, tree orig_fundecl)
 {
   tree fntype, fundecl = NULL_TREE;
   tree name = NULL_TREE, result;
@@ -3033,6 +3035,8 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
       if (flag_tm)
 	tm_malloc_replacement (function);
       fundecl = function;
+      if (!orig_fundecl)
+	orig_fundecl = fundecl;
       /* Atomic functions have type checking/casting already done.  They are 
 	 often rewritten and don't match the original parameter list.  */
       if (name && !strncmp (IDENTIFIER_POINTER (name), "__atomic_", 9))
@@ -3110,9 +3114,10 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
   argarray = vec_safe_address (params);
 
   /* Check that arguments to builtin functions match the expectations.  */
-  if (fundecl && fndecl_built_in_p (fundecl, BUILT_IN_NORMAL)
-      && !check_builtin_function_arguments (loc, arg_loc, fundecl, nargs,
-					    argarray))
+  if (fundecl
+      && fndecl_built_in_p (fundecl)
+      && !check_builtin_function_arguments (loc, arg_loc, fundecl,
+					    orig_fundecl, nargs, argarray))
     return error_mark_node;
 
   /* Check that the arguments to the function are valid.  */
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 1d30cef717e2b7170c2f89bd54546a198e663a38..3a3ef9ed2502016bda5fbb7f23a4c5c3bb931f22 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,16 @@
+2019-09-27  Richard Sandiford  <richard.sandiford@arm.com>
+
+	* cp-tree.h (build_cxx_call): Take the original function decl
+	as an optional final parameter.
+	(cp_build_function_call_vec): Likewise.
+	* call.c (build_cxx_call): Likewise.  Pass all built-in calls to
+	check_builtin_function_arguments.
+	* typeck.c (build_function_call_vec): Take the original function
+	decl as an optional final parameter and pass it to
+	cp_build_function_call_vec.
+	(cp_build_function_call_vec): Take the original function
+	decl as an optional final parameter and pass it to build_cxx_call.
+
 2019-09-25  Marek Polacek  <polacek@redhat.com>
 
 	PR c++/91877 - ICE with converting member of packed struct.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 45b984ecb1185497361d5a4e424ab0fc7b4f8230..5ccf3b89682c114bcc2fd9773399a18752258556 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -9105,12 +9105,14 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl,
 }
 
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
+   If FN is the result of resolving an overloaded target built-in,
+   ORIG_FNDECL is the original function decl, otherwise it is null.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
 
 tree
 build_cxx_call (tree fn, int nargs, tree *argarray,
-		tsubst_flags_t complain)
+		tsubst_flags_t complain, tree orig_fndecl)
 {
   tree fndecl;
 
@@ -9120,11 +9122,13 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
   SET_EXPR_LOCATION (fn, loc);
 
   fndecl = get_callee_fndecl (fn);
+  if (!orig_fndecl)
+    orig_fndecl = fndecl;
 
   /* Check that arguments to builtin functions match the expectations.  */
   if (fndecl
       && !processing_template_decl
-      && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+      && fndecl_built_in_p (fndecl))
     {
       int i;
 
@@ -9134,7 +9138,7 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
 	argarray[i] = maybe_constant_value (argarray[i]);
 
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
-					     nargs, argarray))
+					     orig_fndecl, nargs, argarray))
 	return error_mark_node;
     }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9c0f3949c685fb989ae37451f01e2fcf169ee057..8fc3fc1f78bad743116bbce549c1e41f080f7791 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6257,7 +6257,8 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
 extern tree build_cxx_call			(tree, int, tree *,
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 tree = NULL_TREE);
 extern bool is_std_init_list			(tree);
 extern bool is_list_ctor			(tree);
 extern void validate_conversion_obstack		(void);
@@ -7391,7 +7392,8 @@ extern tree get_member_function_from_ptrfunc	(tree *, tree, tsubst_flags_t);
 extern tree cp_build_function_call_nary         (tree, tsubst_flags_t, ...)
 						ATTRIBUTE_SENTINEL;
 extern tree cp_build_function_call_vec		(tree, vec<tree, va_gc> **,
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 tree = NULL_TREE);
 extern tree build_x_binary_op			(const op_location_t &,
 						 enum tree_code, tree,
 						 enum tree_code, tree,
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index f427c4f4d3e464b1270adce8cb0523cc8c8b41ac..d549450a605fa823a1c4054003025bc18447cc64 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -3773,11 +3773,11 @@ build_function_call (location_t /*loc*/,
 tree
 build_function_call_vec (location_t /*loc*/, vec<location_t> /*arg_loc*/,
 			 tree function, vec<tree, va_gc> *params,
-			 vec<tree, va_gc> * /*origtypes*/)
+			 vec<tree, va_gc> * /*origtypes*/, tree orig_function)
 {
   vec<tree, va_gc> *orig_params = params;
   tree ret = cp_build_function_call_vec (function, &params,
-					 tf_warning_or_error);
+					 tf_warning_or_error, orig_function);
 
   /* cp_build_function_call_vec can reallocate PARAMS by adding
      default arguments.  That should never happen here.  Verify
@@ -3818,13 +3818,15 @@ cp_build_function_call_nary (tree function, tsubst_flags_t complain, ...)
   return ret;
 }
 
-/* Build a function call using a vector of arguments.  PARAMS may be
-   NULL if there are no parameters.  This changes the contents of
-   PARAMS.  */
+/* Build a function call using a vector of arguments.
+   If FUNCTION is the result of resolving an overloaded target built-in,
+   ORIG_FNDECL is the original function decl, otherwise it is null.
+   PARAMS may be NULL if there are no parameters.  This changes the
+   contents of PARAMS.  */
 
 tree
 cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
-			    tsubst_flags_t complain)
+			    tsubst_flags_t complain, tree orig_fndecl)
 {
   tree fntype, fndecl;
   int is_method;
@@ -3949,7 +3951,7 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
   bool warned_p = check_function_arguments (input_location, fndecl, fntype,
 					    nargs, argarray, NULL);
 
-  ret = build_cxx_call (function, nargs, argarray, complain);
+  ret = build_cxx_call (function, nargs, argarray, complain, orig_fndecl);
 
   if (warned_p)
     {
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 0250cf58e72b4df8fec19cfb4399ed0e2594342b..a86c210d4fe390bd0356b6e50ba7c6c34a36239a 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11567,6 +11567,21 @@ another @code{CALL_EXPR}.
 @var{arglist} really has type @samp{VEC(tree,gc)*}
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_CHECK_BUILTIN_CALL (location_t @var{loc}, vec<location_t> @var{arg_loc}, tree @var{fndecl}, tree @var{orig_fndecl}, unsigned int @var{nargs}, tree *@var{args})
+Perform semantic checking on a call to a machine-specific built-in
+function after its arguments have been constrained to the function
+signature.  Return true if the call is valid, otherwise report an error
+and return false.
+
+This hook is called after @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}.
+The call was originally to built-in function @var{orig_fndecl},
+but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}
+step is now to built-in function @var{fndecl}.  @var{loc} is the
+location of the call and @var{args} is an array of function arguments,
+of which there are @var{nargs}.  @var{arg_loc} specifies the location
+of each argument.
+@end deftypefn
+
 @deftypefn {Target Hook} tree TARGET_FOLD_BUILTIN (tree @var{fndecl}, int @var{n_args}, tree *@var{argp}, bool @var{ignore})
 Fold a call to a machine specific built-in function that was set up by
 @samp{TARGET_INIT_BUILTINS}.  @var{fndecl} is the declaration of the
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 0b77dd8eb46dc53fc585d7b3eac9805c6ed79951..06dfcda35abea7396c288a59c38ee4ef57c6fef6 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -7938,6 +7938,8 @@ to by @var{ce_info}.
 
 @hook TARGET_RESOLVE_OVERLOADED_BUILTIN
 
+@hook TARGET_CHECK_BUILTIN_CALL
+
 @hook TARGET_FOLD_BUILTIN
 
 @hook TARGET_GIMPLE_FOLD_BUILTIN
diff --git a/gcc/target.def b/gcc/target.def
index 01609136848fc157a47a93a0267c03524fe9383e..f9446fa05a22c79154c2ef36d3d8aea48a5efcc6 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2397,6 +2397,24 @@ another @code{CALL_EXPR}.\n\
 @var{arglist} really has type @samp{VEC(tree,gc)*}",
  tree, (unsigned int /*location_t*/ loc, tree fndecl, void *arglist), NULL)
 
+DEFHOOK
+(check_builtin_call,
+ "Perform semantic checking on a call to a machine-specific built-in\n\
+function after its arguments have been constrained to the function\n\
+signature.  Return true if the call is valid, otherwise report an error\n\
+and return false.\n\
+\n\
+This hook is called after @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}.\n\
+The call was originally to built-in function @var{orig_fndecl},\n\
+but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}\n\
+step is now to built-in function @var{fndecl}.  @var{loc} is the\n\
+location of the call and @var{args} is an array of function arguments,\n\
+of which there are @var{nargs}.  @var{arg_loc} specifies the location\n\
+of each argument.",
+ bool, (location_t loc, vec<location_t> arg_loc, tree fndecl,
+	tree orig_fndecl, unsigned int nargs, tree *args),
+ NULL)
+
 /* Fold a target-specific builtin to a tree valid for both GIMPLE
    and GENERIC.  */
 DEFHOOK