diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index b632f68..ca04910 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -6292,41 +6292,54 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
 static tree
 c_parser_asm_statement (c_parser *parser)
 {
-  tree quals, str, outputs, inputs, clobbers, labels, ret;
-  bool simple, is_volatile, is_inline, is_goto;
+  tree str, outputs, inputs, clobbers, labels, ret;
+  bool simple;
   location_t asm_loc = c_parser_peek_token (parser)->location;
   int section, nsections;
 
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
   c_parser_consume_token (parser);
 
-  quals = NULL_TREE;
-  is_volatile = false;
-  is_inline = false;
-  is_goto = false;
+  /* Handle the asm-qualifier-list.  */
+  location_t volatile_loc = UNKNOWN_LOCATION;
+  location_t inline_loc = UNKNOWN_LOCATION;
+  location_t goto_loc = UNKNOWN_LOCATION;
   for (;;)
     {
-      switch (c_parser_peek_token (parser)->keyword)
+      c_token *token = c_parser_peek_token (parser);
+      location_t loc = token->location;
+      switch (token->keyword)
 	{
 	case RID_VOLATILE:
-	  if (is_volatile)
-	    break;
-	  is_volatile = true;
-	  quals = c_parser_peek_token (parser)->value;
+	  if (volatile_loc)
+	    {
+	      error_at (loc, "duplicate asm qualifier %qE", token->value);
+	      inform (volatile_loc, "first seen here");
+	    }
+	  else
+	    volatile_loc = loc;
 	  c_parser_consume_token (parser);
 	  continue;
 
 	case RID_INLINE:
-	  if (is_inline)
-	    break;
-	  is_inline = true;
+	  if (inline_loc)
+	    {
+	      error_at (loc, "duplicate asm qualifier %qE", token->value);
+	      inform (inline_loc, "first seen here");
+	    }
+	  else
+	    inline_loc = loc;
 	  c_parser_consume_token (parser);
 	  continue;
 
 	case RID_GOTO:
-	  if (is_goto)
-	    break;
-	  is_goto = true;
+	  if (goto_loc)
+	    {
+	      error_at (loc, "duplicate asm qualifier %qE", token->value);
+	      inform (goto_loc, "first seen here");
+	    }
+	  else
+	    goto_loc = loc;
 	  c_parser_consume_token (parser);
 	  continue;
 
@@ -6336,6 +6349,10 @@ c_parser_asm_statement (c_parser *parser)
       break;
     }
 
+  bool is_volatile = (volatile_loc != UNKNOWN_LOCATION);
+  bool is_inline = (inline_loc != UNKNOWN_LOCATION);
+  bool is_goto = (goto_loc != UNKNOWN_LOCATION);
+
   /* ??? Follow the C++ parser rather than using the
      lex_untranslated_string kludge.  */
   parser->lex_untranslated_string = true;
@@ -6410,9 +6427,9 @@ c_parser_asm_statement (c_parser *parser)
   if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
     c_parser_skip_to_end_of_block_or_statement (parser);
 
-  ret = build_asm_stmt (quals, build_asm_expr (asm_loc, str, outputs, inputs,
-					       clobbers, labels, simple,
-					       is_inline));
+  ret = build_asm_stmt (is_volatile,
+			build_asm_expr (asm_loc, str, outputs, inputs,
+					clobbers, labels, simple, is_inline));
 
  error:
   parser->lex_untranslated_string = false;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 7f34bdc..aa66aa2 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -679,7 +679,7 @@ extern tree c_start_case (location_t, location_t, tree, bool);
 extern void c_finish_case (tree, tree);
 extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool,
 			    bool);
-extern tree build_asm_stmt (tree, tree);
+extern tree build_asm_stmt (bool, tree);
 extern int c_types_compatible_p (tree, tree);
 extern tree c_begin_compound_stmt (bool);
 extern tree c_end_compound_stmt (location_t, tree, bool);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 3ebb28e..7b90b5c 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -9979,9 +9979,9 @@ process_init_element (location_t loc, struct c_expr value, bool implicit,
    (guaranteed to be 'volatile' or null) and ARGS (represented using
    an ASM_EXPR node).  */
 tree
-build_asm_stmt (tree cv_qualifier, tree args)
+build_asm_stmt (bool is_volatile, tree args)
 {
-  if (!ASM_VOLATILE_P (args) && cv_qualifier)
+  if (is_volatile)
     ASM_VOLATILE_P (args) = 1;
   return add_stmt (args);
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7660565..44fdace 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -19106,12 +19106,9 @@ cp_parser_asm_definition (cp_parser* parser)
   tree clobbers = NULL_TREE;
   tree labels = NULL_TREE;
   tree asm_stmt;
-  bool volatile_p = false;
   bool extended_p = false;
   bool invalid_inputs_p = false;
   bool invalid_outputs_p = false;
-  bool inline_p = false;
-  bool goto_p = false;
   required_token missing = RT_NONE;
 
   /* Look for the `asm' keyword.  */
@@ -19125,29 +19122,50 @@ cp_parser_asm_definition (cp_parser* parser)
     }
 
   /* Handle the asm-qualifier-list.  */
+  location_t volatile_loc = UNKNOWN_LOCATION;
+  location_t inline_loc = UNKNOWN_LOCATION;
+  location_t goto_loc = UNKNOWN_LOCATION;
   if (cp_parser_allow_gnu_extensions_p (parser))
     for (;;)
       {
+	cp_token *token = cp_lexer_peek_token (parser->lexer);
+	location_t loc = token->location;
 	switch (cp_lexer_peek_token (parser->lexer)->keyword)
 	  {
 	  case RID_VOLATILE:
-	    if (volatile_p)
-	      break;
-	    volatile_p = true;
+	    if (volatile_loc)
+	      {
+		error_at (loc, "duplicate asm qualifier %qT", token->u.value);
+		inform (volatile_loc, "first seen here");
+	      }
+	    else
+	      volatile_loc = loc;
 	    cp_lexer_consume_token (parser->lexer);
 	    continue;
 
 	  case RID_INLINE:
-	    if (inline_p || !parser->in_function_body)
+	    if (!parser->in_function_body)
 	      break;
-	    inline_p = true;
+	    if (inline_loc)
+	      {
+		error_at (loc, "duplicate asm qualifier %qT", token->u.value);
+		inform (inline_loc, "first seen here");
+	      }
+	    else
+	      inline_loc = loc;
 	    cp_lexer_consume_token (parser->lexer);
 	    continue;
 
 	  case RID_GOTO:
-	    if (goto_p || !parser->in_function_body)
+	    if (!parser->in_function_body)
 	      break;
-	    goto_p = true;
+	    if (goto_loc)
+	      {
+		error_at (loc, "duplicate asm qualifier %qT", token->u.value);
+		inform (goto_loc, "first seen here");
+	      }
+	    else
+	      goto_loc = loc;
 	    cp_lexer_consume_token (parser->lexer);
 	    continue;
 
@@ -19157,6 +19175,10 @@ cp_parser_asm_definition (cp_parser* parser)
 	break;
       }
 
+  bool volatile_p = (volatile_loc != UNKNOWN_LOCATION);
+  bool inline_p = (inline_loc != UNKNOWN_LOCATION);
+  bool goto_p = (goto_loc != UNKNOWN_LOCATION);
+
   /* Look for the opening `('.  */
   if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
     return;
@@ -19248,8 +19270,7 @@ cp_parser_asm_definition (cp_parser* parser)
 					     CPP_CLOSE_PAREN))
 	    clobbers = cp_parser_asm_clobber_list (parser);
 	}
-      else if (goto_p
-	       && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
+      else if (goto_p && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
 	/* The labels are coming next.  */
 	labels_p = true;
 
