This is a follow-up to
https://gcc.gnu.org/pipermail/gcc-patches/2026-July/722603.html
with the following changes:

- When I started working on hard register constraints I always wanted to
  streamline/move error checking from expand to gimplification so that
  we have one place to rule them all.  With this patch I'm one step
  closer to this.  This also streamlines diagnostic messages for hard
  register constraints as well as register asm usages which in turn means
  that I had to change dg-error messages for gcc.dg/pr87600-2.c.  I
  scanned through the test suite but didn't find any further test which
  must be adjusted.
- Since validation for inputs and outputs are similar, I de-duplicated
  logic into helper function validate_reg_asm_operands().
- Similarly de-duplicate and use new helper function
  diag_reg_asm_cstr_mismatch() for inputs and outputs.
- I gave the usage of local reg asm vars which are defined in an outer
  function and used in an inner function a second thought.  I refrained
  from diagnosing this after it was broken for decades, just before it
  is about being fixed by -fstrict-extended-asm.  Thus, I removed the
  diagnostics from v4.

Bootstrap didn't finish over night.  Just restarted.

-- >8 --

From: Stefan Schulze Frielinghaus <[email protected]>

Currently local register asm assignments materialize during expand into
assignments utilizing hard registers.  Since prior register allocation,
hard registers or more precisely objects residing in hard registers are
not tracked individually, those are subject to be clobbered.  Well known
and documented are function calls which may clobber hard registers used
for register asm objects.  For example, compiling on aarch64

register int x asm ("x0") = 0x123;
register int y asm ("x1") = *ptr;

using address sanitizers results after expand in

x0:SI=0x123
x0:DI=r104:DI
call [`__asan_load4'] argc:0
x1:SI=[r104:DI]

The implicit function call added by the address sanitizer clobbers
argument register x0 which was previously set for the register asm
object.

With the advent of hard register constraints, this can be overcome.
Instead of expanding a register asm assignment directly into a hard
register assignment, keep the register asm object in a pseudo for as
long as possible and use a hard register constraint in Extended Asm
statements which ensures that the object is finally allocated the
respective hard register.  Since local register asm is supposed to have
an effect only for Extended Asm statements, this coincides with hard
register constraints which materialize for the respective insn.

This patch adds the feature of rewriting local register asm into code
which exploits hard register constraints.  For example

register int global asm ("r3");

int foo (int x0)
{
  register int x asm ("r4") = x0;
  register int y asm ("r5");

  asm ("bar\t%0,%1,%2" : "=r" (x) : "0" (x), "r" (global));
  x += 42;
  asm ("baz\t%0,%1" : "=r" (y) : "r" (x));

  return y;
}

is rewritten during gimplification into

register int global asm ("r3");

int foo (int x0)
{
  int x = x0;
  int y;

  asm ("bar\t%0,%1,%2" : "={r4}" (x) : "0" (x), "r" (global));
  x += 42;
  asm ("baz\t%0,%1" : "={r5}" (y) : "{r4}" (x));

  return y;
}

The resulting code solely relies on hard register constraints modulo
global register asm.  Thus, for GIMPLE we fall back to ordinary
SSA_NAME and finally for RTL to pseudos.

Note, since hard register constraints are more strict in order to
prevent subtle bugs, this in turn means that certain programs are not
valid after register asm demotion anymore.  For example,

register int x asm ("r5") = 42;
asm ("" : "+r" (x) : "r" (x));

is rewritten into

int x = 42;
asm ("" : "={r5}" (x) : "0" (x), "{r5}" (x));

Two inputs refer to the very same register which is invalid when using
hard register constraints.  Therefore, currently the transformation is
hidden behind new flag -fstrict-extended-asm which is disabled by
default.  This patch also adds the new warning -Wstrict-extended-asm
which is enabled by -Wall in order to diagnose cases for which
-fstrict-extended-asm errors out.

Another incompatibility which is worthwhile to mention are usages of
uninitialized register asm input operands which is undefined for
-fstrict-extended-asm.  In case -W{maybe-,}uninitialized is specified a
diagnostics may be emitted.

In order to automatically rewrite register asm into hard register
constraints, it is crucial that the register referred to by the register
asm operand is entailed in the register class of the corresponding
constraint of the operand.  If this is not the case, then error out in
case of -fstrict-extended-asm, or in case of -fno-strict-extended-asm
-Wstrict-extended-asm emit a diagnostics.

gcc/ChangeLog:

        * cfgexpand.cc (expand_asm_stmt): Verify that register asm output
        operands have no overlapping registers during gimplification.
        * common.opt (Wstrict-extended-asm): New warning.
        (fstrict-extended-asm) New flag.
        * doc/invoke.texi: Document new warning and flag.
        * gimplify.cc (gimplify_demote_register_asm): Helper for
        gimplify_asm_expr in order to prepare demotion of a register
        asm object into an ordinary one by rewriting constraints.
        (validate_reg_asm_operands): Helper function for diagnostics.
        (gimplify_asm_expr): Add diagnostics logic for new warning.
        (gimplify_body): Cleanup helper hash set.
        * gimplify_reg_info.h: Instead of passing a 9th/10th argument
        via parse_{in,out}put_constraint utilize this helper class.
        * output.h (decode_reg_name): New function.
        * stmt.cc (diag_reg_asm_cstr_mismatch): Helper function for
        diagnostics.
        (parse_output_constraint): Utilize new function.  Add
        diagnostics logic.
        * varasm.cc (decode_reg_name): New function.

gcc/testsuite/ChangeLog:

        * gcc.dg/pr87600-2.c: Streamline diag messages.
        * gcc.target/s390/asm-hard-reg-7.c: After moving diagnostics
        from expand into gimplification we can test for further errors
        in the same file.
        * gcc.dg/asm-hard-reg-strict-1.c: New test.
        * gcc.dg/asm-hard-reg-strict-2.c: New test.
        * gcc.dg/asm-hard-reg-strict-3.c: New test.
        * gcc.dg/asm-hard-reg-strict-4.c: New test.
        * gcc.dg/asm-hard-reg-strict-5.c: New test.
        * gcc.dg/asm-hard-reg-strict-6.c: New test.
---
 gcc/cfgexpand.cc                              |  59 ----
 gcc/common.opt                                |   8 +
 gcc/doc/invoke.texi                           |  64 ++++-
 gcc/gimplify.cc                               | 254 +++++++++++++++---
 gcc/gimplify_reg_info.h                       |   2 +
 gcc/output.h                                  |   4 +
 gcc/stmt.cc                                   |  58 +++-
 gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c  |  53 ++++
 gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c  |  54 ++++
 gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c  |  72 +++++
 gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c  |  74 +++++
 gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c  |  65 +++++
 gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c  |  13 +
 gcc/testsuite/gcc.dg/pr87600-2.c              |   2 +-
 .../gcc.target/s390/asm-hard-reg-7.c          |   6 +-
 gcc/varasm.cc                                 |  16 ++
 16 files changed, 686 insertions(+), 118 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
 create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
 create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
 create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c

diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index c5b4a62ace2..1b93f01a161 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -3521,65 +3521,6 @@ expand_asm_stmt (gasm *stmt)
                                    nullptr))
        return;
 
-      /* If the output is a hard register, verify it doesn't conflict with
-        any other operand's possible hard register use.  */
-      if (DECL_P (val)
-         && REG_P (DECL_RTL (val))
-         && HARD_REGISTER_P (DECL_RTL (val)))
-       {
-         unsigned j, output_hregno = REGNO (DECL_RTL (val));
-         bool early_clobber_p = strchr (constraints[i], '&') != NULL;
-         unsigned long match;
-
-         /* Verify the other outputs do not use the same hard register.  */
-         for (j = i + 1; j < noutputs; ++j)
-           if (DECL_P (output_tvec[j])
-               && REG_P (DECL_RTL (output_tvec[j]))
-               && HARD_REGISTER_P (DECL_RTL (output_tvec[j]))
-               && output_hregno == REGNO (DECL_RTL (output_tvec[j])))
-             {
-               error_at (locus, "invalid hard register usage between output "
-                         "operands");
-               error_seen = true;
-             }
-
-         /* Verify matching constraint operands use the same hard register
-            and that the non-matching constraint operands do not use the same
-            hard register if the output is an early clobber operand.  */
-         for (j = 0; j < ninputs; ++j)
-           if (DECL_P (input_tvec[j])
-               && REG_P (DECL_RTL (input_tvec[j]))
-               && HARD_REGISTER_P (DECL_RTL (input_tvec[j])))
-             {
-               unsigned input_hregno = REGNO (DECL_RTL (input_tvec[j]));
-               switch (*constraints[j + noutputs])
-                 {
-                 case '0':  case '1':  case '2':  case '3':  case '4':
-                 case '5':  case '6':  case '7':  case '8':  case '9':
-                   match = strtoul (constraints[j + noutputs], NULL, 10);
-                   break;
-                 default:
-                   match = ULONG_MAX;
-                   break;
-                 }
-               if (i == match
-                   && output_hregno != input_hregno)
-                 {
-                   error_at (locus, "invalid hard register usage between "
-                             "output operand and matching constraint operand");
-                   error_seen = true;
-                 }
-               else if (early_clobber_p
-                        && i != match
-                        && output_hregno == input_hregno)
-                 {
-                   error_at (locus, "invalid hard register usage between "
-                             "earlyclobber operand and input operand");
-                   error_seen = true;
-                 }
-             }
-       }
-
       if (! allows_reg
          && (allows_mem
              || is_inout
diff --git a/gcc/common.opt b/gcc/common.opt
index 1c6ad3aa4e5..e3c071fa369 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -935,6 +935,10 @@ Wzero-init-padding-bits=
 Common Joined RejectNegative Enum(zero_init_padding_bits_kind) 
Var(warn_zero_init_padding_bits) Init(ZERO_INIT_PADDING_BITS_STANDARD) Warning
 -Wzero-init-padding-bits=[standard|unions|all] Warn about initializers that 
might not zero padding bits.
 
+Wstrict-extended-asm
+Common Var(warn_strict_extended_asm) Warning LangEnabledBy(C C++,Wall)
+Warn about constructs for which -fstrict-extended-asm fails.
+
 Xassembler
 Driver Separate
 
@@ -3595,6 +3599,10 @@ fverbose-asm
 Common Var(flag_verbose_asm)
 Add extra commentary to assembler output.
 
+fstrict-extended-asm
+Common Var(flag_strict_extended_asm) Init(0)
+Apply strict rules for extended asm comprising hard register constraint rules.
+
 fvisibility=
 Common Joined RejectNegative Enum(symbol_visibility) Var(default_visibility) 
Init(VISIBILITY_DEFAULT)
 -fvisibility=[default|internal|hidden|protected]       Set the default symbol 
visibility.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 78052b229a5..c4f1efb3d7f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -210,7 +210,7 @@ in the following sections.
 -fpermitted-flt-eval-methods=@var{standard}
 -fplan9-extensions  -fsigned-bitfields  -funsigned-bitfields
 -fsigned-char  -funsigned-char  -fsso-struct=@var{endianness}
--fstrict-flex-arrays[=@var{n}]}
+-fstrict-extended-asm -fstrict-flex-arrays[=@var{n}]}
 
 @item C++ Language Options
 @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
@@ -449,7 +449,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wstrict-aliasing=@var{n}
 -Wstring-compare
 -Wno-stringop-overflow  -Wno-stringop-overread
--Wno-stringop-truncation  -Wstrict-flex-arrays
+-Wno-stringop-truncation  -Wstrict-flex-arrays  -Wstrict-extended-asm
 -Wsuggest-attribute=@var{attribute-name}
 -Wswitch  -Wno-switch-bool  -Wswitch-default  -Wswitch-enum
 -Wno-switch-outside-range  -Wno-switch-unreachable  -Wsync-nand
@@ -3009,6 +3009,17 @@ The @option{-fstrict_flex_arrays} option interacts with 
the
 @option{-Wstrict-flex-arrays} option.  @xref{Warning Options}, for more
 information.
 
+@opindex fstrict-extended-asm
+@opindex fno-strict-extended-asm
+@item -fstrict-extended-asm
+Apply strict rules for extended @code{asm} (@pxref{Extended Asm}).  In
+particular this means that register @code{asm} operands must follow the rules
+for hard register constraints.  For example, don't allow multiple input
+operands to refer to the same register.  Note, any use of an uninitialized
+local register asm input operand is undefined.  See also
+@option{-Wstrict-extended-asm}.  This flag is currently disabled by default and
+will eventually be enabled by default in a future release.
+
 @end table
 
 @node C++ Dialect Options
@@ -9009,6 +9020,55 @@ This option is more effective when @option{-ftree-vrp} 
is active (the
 default for @option{-O2} and above) but some warnings may be diagnosed
 even without optimization.
 
+@opindex Wstrict-extended-asm
+@opindex Wno-strict-extended-asm
+@item -Wstrict-extended-asm
+Warn about extended @code{asm} (@pxref{Extended Asm}) usages which could lead
+to subtle bugs and for which @option{-fstrict-extended-asm} errors out.  For
+example, warn if an lvalue is used by multiple output operands:
+
+@smallexample
+int x;
+asm ("..." : "=r" (x), "=r" (x));
+@end smallexample
+
+Both output operands get a different register assigned but outside of the
+extended @code{asm} only one is bound to @code{x}.
+
+Furthermore, warn about register @code{asm} usages, if multiple operands refer
+to the same register (including overlaps in case of register pairs).
+
+@smallexample
+register int x asm ("r5") = 42;
+register int y asm ("r5") = 24;
+asm ("..." : "=r" (x) : "r" (x), "r" (y));
+@end smallexample
+
+Here @code{x} and @code{y} refer to the same register @code{r5}.  Note, this 
also
+includes more subtle cases as for example:
+
+@smallexample
+register int x asm ("r5") = 42;
+asm ("..." : "+r" (x) : "r" (x));
+@end smallexample
+
+After multiplying out the in-out operand we are faced with two inputs which
+refer to the same register.  One time indirectly via @code{"0" (x)} and the
+other one directly via @code{"r" (x)}.
+
+Also diagnose if a register @code{asm} operand does not coincide with its
+corresponding constraint.  Assume in the following that @code{f5} is a
+floating-point register which is not entailed in the register class associated
+with constraint @code{r}.
+
+@smallexample
+register float x asm ("f5");
+asm ("..." : "=r" (x));
+@end smallexample
+
+Since it is ambiguous whether the operand should be allocated @code{f5} or a
+general-purpose register, this indicates a subtle bug which is diagnosed.
+
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
 @item -Wsuggest-attribute=@var{attribute-name}
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 223b55b7e93..f5d6d11dd05 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -7848,6 +7848,148 @@ num_alternatives (const_tree link)
   return num + 1;
 }
 
+/* Keep track of all local register asm variables for which their constraints
+   at Extnded Asm statements have been replaced by their corresponding hard
+   register constraints.  After all asm statements of a function have been
+   processed, demote those to automatic variables.  */
+static hash_set<tree> demote_register_asm;
+
+/* If -fstrict-extended-asm is specified, rewrite constraints of Extended Asm
+   operands which refer to local register asm variables into hard register
+   constraints.  Also mark those objects to be demoted from register asm
+   variables to automatic variables which is done basically after
+   gimplification of the function body.  */
+
+static void
+gimplify_demote_register_asm (tree link)
+{
+  tree op = TREE_VALUE (link);
+  if (!VAR_P (op) || !DECL_HARD_REGISTER (op) || is_global_var (op))
+    return;
+  tree id = DECL_ASSEMBLER_NAME (op);
+  const char *regname = IDENTIFIER_POINTER (id);
+  ++regname;
+  int regno = decode_reg_name (regname);
+  if (regno < 0)
+    /* This indicates an error and we error out later on.  */
+    return;
+  /* Currently, fixed registers cannot be used for hard register constraints
+     which is why we skip those for the moment.  */
+  if (fixed_regs[regno])
+    return;
+  const char *constraint
+    = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+  auto_vec<char, 64> constraint_new;
+  for (const char *p = constraint; *p; )
+    {
+      bool changed_p = false;
+      enum constraint_num cn = lookup_constraint (p);
+      enum reg_class rclass = reg_class_for_constraint (cn);
+      if (rclass != NO_REGS)
+       {
+         /* During parse_{input,output}_constraint() we ensured that rclass
+            entails all registers required by the register asm operand.
+            Therefore, rewrite the constraint into a corresponding hard
+            register constraint.  */
+         constraint_new.safe_push ('{');
+         size_t len = strlen (regname);
+         for (size_t i = 0; i < len; ++i)
+           constraint_new.safe_push (regname[i]);
+         constraint_new.safe_push ('}');
+         changed_p = true;
+       }
+
+      for (size_t len = CONSTRAINT_LEN (*p, p); len; len--, p++)
+       {
+         if (!changed_p)
+           constraint_new.safe_push (*p);
+         if (*p == '\0')
+           break;
+       }
+    }
+  constraint_new.safe_push ('\0');
+  unsigned int len = constraint_new.length ();
+  tree str = build_string (len, constraint_new.address ());
+  TREE_VALUE (TREE_PURPOSE (link)) = str;
+  demote_register_asm.add (op);
+}
+
+/* There shouldn't be multiple register asm output operands referring to the
+   same register.  For example:
+
+   register int x asm ("r5");
+   register int y asm ("r5");
+   asm ("" : "=r" (x) : "=r" (y));
+
+   is error-prone.  Note, this also includes cases when operands reside in
+   register pairs and overlap only partially (or in general for multi-register
+   operands).
+
+   Previously this was diagnosed for local as well as global register asm
+   during expand with an error which is why we error out here, too.
+
+   Similarly as for outputs there shouldn't be multiple register asm input
+   operands referring to the same register.  In contrast to output operands we
+   didn't diagnose those so far.  Therefore, error out only in case of
+   -fstrict-register-asm.
+
+   OUT is true for output operands and false otherwise.
+
+   Return true in case an error was diagnosed and false otherwise.  */
+
+static bool
+validate_reg_asm_operands (tree op1, tree link_next, bool out)
+{
+  if (!VAR_P (op1)
+      || !DECL_HARD_REGISTER (op1))
+    return false;
+
+  int regno1 = decode_reg_name (op1);
+  if (regno1 < 0)
+    return false;
+  HARD_REG_SET hreg_set1;
+  CLEAR_HARD_REG_SET (hreg_set1);
+  add_to_hard_reg_set (&hreg_set1, TYPE_MODE (TREE_TYPE (op1)), regno1);
+  for (tree i = link_next; i; i = TREE_CHAIN (i))
+    {
+      tree op2 = TREE_VALUE (i);
+      if (VAR_P (op2)
+         && DECL_HARD_REGISTER (op2))
+       {
+         int regno2 = decode_reg_name (op2);
+         if (regno2 < 0)
+           continue;
+         int nregs2
+           = hard_regno_nregs (regno2, TYPE_MODE (TREE_TYPE (op2)));
+         for (int j = regno2; j < regno2 + nregs2; ++j)
+           {
+             if (!TEST_HARD_REG_BIT (hreg_set1, j))
+               continue;
+             /* For legacy reasons do not error out in case multi-register
+                output operands overlap only partially via a non-first
+                register.  For example, if one output operand utilizes single
+                register r1 and another one utilizes pair r0:r1.  This was not
+                diagnosed so far.  Therefore, be graceful and warn only.  */
+             if (flag_strict_extended_asm || (out && regno1 == regno2))
+               {
+                 error ("multiple %s to hard register: %s",
+                        out ? "outputs" : "inputs", reg_names[j]);
+                 return true;
+               }
+             else
+               {
+                 warning (OPT_Wstrict_extended_asm,
+                          "multiple %s to hard register: %s",
+                          out ? "outputs" : "inputs", reg_names[j]);
+                 return false;
+               }
+           }
+       }
+    }
+
+  return false;
+}
+
 /* Gimplify the operands of an ASM_EXPR.  Input operands should be a gimple
    value; output operands should be a gimple lvalue.  */
 
@@ -7935,9 +8077,50 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
          is_inout = false;
        }
 
+      /* In case of -fstrict-extended-asm or for hard register constraints,
+        error out if an lvalue is used for more than one output operand.
+        Otherwise emit at most a warning.  For example, error out for
+
+          asm ("" : "={0}" (x), "={1}" (x));
+          asm ("" : "=r" (x), "={1}" (x));
+
+        and emit a warning for
+
+          int x;
+          asm ("" : "=r" (x), "=r" (x));
+
+        if -fno-strict-extended-asm -Wstrict-register-asm is given.  */
+
+      for (tree i = link_next; i; i = TREE_CHAIN (i))
+       {
+         tree op1 = TREE_VALUE (link);
+         tree op2 = TREE_VALUE (i);
+         if (op1 != op2)
+           continue;
+         const char *constraint2
+           = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (i)));
+         if (flag_strict_extended_asm
+             || strchr (constraint, '{') != nullptr
+             || strchr (constraint2, '{') != nullptr
+             /* We already diagnose global/local register asm cases during
+                expand as errors.  Therefore, do here, too.  */
+             || (VAR_P (op2) && DECL_HARD_REGISTER (op2)))
+           {
+             error ("multiple outputs to lvalue %qE", op2);
+             return GS_ERROR;
+           }
+         else
+           warning (OPT_Wstrict_extended_asm, "multiple outputs to lvalue %qE",
+                    op2);
+       }
+
+      tree outputv = TREE_VALUE (link);
+      tree outtype = TREE_TYPE (outputv);
+
+      validate_reg_asm_operands (outputv, link_next, true);
+
       /* If we can't make copies, we can only accept memory.
         Similarly for VLAs.  */
-      tree outtype = TREE_TYPE (TREE_VALUE (link));
       if (TREE_ADDRESSABLE (outtype)
          || !COMPLETE_TYPE_P (outtype)
          || !tree_fits_poly_uint64_p (TYPE_SIZE_UNIT (outtype)))
@@ -8098,44 +8281,9 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
        }
     }
 
-  /* After all output operands have been gimplified, verify that each output
-     operand is used at most once in case of hard register constraints.  Thus,
-     error out in cases like
-       asm ("" : "={0}" (x), "={1}" (x));
-     or even for
-       asm ("" : "=r" (x), "={1}" (x));
-
-     FIXME: Ideally we would also error out for cases like
-       int x;
-       asm ("" : "=r" (x), "=r" (x));
-     However, since code like that was previously accepted, erroring out now 
might
-     break existing code.  On the other hand, we already error out for register
-     asm like
-       register int x asm ("0");
-       asm ("" : "=r" (x), "=r" (x));
-     Thus, maybe it wouldn't be too bad to also error out in the former
-     non-register-asm case.
-  */
-  for (unsigned i = 0; i < vec_safe_length (outputs); ++i)
-    {
-      tree link = (*outputs)[i];
-      tree op1 = TREE_VALUE (link);
-      const char *constraint
-       = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
-      if (strchr (constraint, '{') != nullptr)
-       for (unsigned j = 0; j < vec_safe_length (outputs); ++j)
-         {
-           if (i == j)
-             continue;
-           tree link2 = (*outputs)[j];
-           tree op2 = TREE_VALUE (link2);
-           if (op1 == op2)
-             {
-               error ("multiple outputs to lvalue %qE", op2);
-               return GS_ERROR;
-             }
-         }
-    }
+  /* We refer to matching output operands while verifying register asm input
+     operands. */
+  reg_info.m_outputs = outputs;
 
   link_next = NULL_TREE;
   int input_num = 0;
@@ -8155,8 +8303,10 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
          is_inout = false;
        }
 
+      tree inputv = TREE_VALUE (link);
+      tree intype = TREE_TYPE (inputv);
+
       /* If we can't make copies, we can only accept memory.  */
-      tree intype = TREE_TYPE (TREE_VALUE (link));
       if (TREE_ADDRESSABLE (intype)
          || !COMPLETE_TYPE_P (intype)
          || !tree_fits_poly_uint64_p (TYPE_SIZE_UNIT (intype)))
@@ -8171,10 +8321,11 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
            }
        }
 
+      validate_reg_asm_operands (inputv, link_next, false);
+
       /* If the operand is a memory input, it should be an lvalue.  */
       if (!allows_reg && allows_mem)
        {
-         tree inputv = TREE_VALUE (link);
          STRIP_NOPS (inputv);
          if (TREE_CODE (inputv) == PREDECREMENT_EXPR
              || TREE_CODE (inputv) == PREINCREMENT_EXPR
@@ -8244,6 +8395,20 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
   /* Do not add ASMs with errors to the gimple IL stream.  */
   if (ret != GS_ERROR)
     {
+      if (flag_strict_extended_asm)
+       {
+         for (unsigned i = 0; i < vec_safe_length (outputs); ++i)
+           {
+             tree link = (*outputs)[i];
+             gimplify_demote_register_asm (link);
+           }
+         for (unsigned i = 0; i < vec_safe_length (inputs); ++i)
+           {
+             tree link = (*inputs)[i];
+             gimplify_demote_register_asm (link);
+           }
+       }
+
       stmt = gimple_build_asm_vec (TREE_STRING_POINTER (ASM_STRING (expr)),
                                   inputs, outputs, clobbers, labels);
 
@@ -21874,6 +22039,13 @@ gimplify_body (tree fndecl, bool do_parms)
          }
     }
 
+  for (auto op : demote_register_asm)
+    {
+      DECL_REGISTER (op) = 0;
+      DECL_HARD_REGISTER (op) = 0;
+    }
+  demote_register_asm.empty ();
+
   if ((flag_openacc || flag_openmp || flag_openmp_simd)
       && gimplify_omp_ctxp)
     {
diff --git a/gcc/gimplify_reg_info.h b/gcc/gimplify_reg_info.h
index b56b225ac01..b68019dc6e2 100644
--- a/gcc/gimplify_reg_info.h
+++ b/gcc/gimplify_reg_info.h
@@ -60,11 +60,13 @@ class gimplify_reg_info
 
 public:
   tree operand;
+  const vec<tree, va_gc> *m_outputs;
 
   gimplify_reg_info (unsigned num_alternatives,
                     unsigned num_outputs)
     : m_num_alternatives{num_alternatives}
     , m_num_outputs{num_outputs}
+    , m_outputs{nullptr}
   {
     CLEAR_HARD_REG_SET (m_reg_asm_output);
     CLEAR_HARD_REG_SET (m_reg_asm_input);
diff --git a/gcc/output.h b/gcc/output.h
index 1f628ea2371..26f0d15d3af 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -161,6 +161,10 @@ extern void weak_finish (void);
    Prefixes such as % are optional.  */
 extern int decode_reg_name (const char *);
 
+/* An overload which takes a tree operand which is expected to be a
+   DECL_HARD_REGISTER.  */
+extern int decode_reg_name (tree);
+
 /* Similar to decode_reg_name, but takes an extra parameter that is a
    pointer to the number of (internal) registers described by the
    external name.  */
diff --git a/gcc/stmt.cc b/gcc/stmt.cc
index 382a2d75c07..0af7a24eb3e 100644
--- a/gcc/stmt.cc
+++ b/gcc/stmt.cc
@@ -249,6 +249,30 @@ hardreg_ok_p (int reg_number, machine_mode mode, int 
operand_num)
   return false;
 }
 
+/* Diagnose if a register asm operand and its corresponding constraint do not
+   coincide.  Return true in case of an error and false otherwise.  */
+
+static bool
+diag_reg_asm_cstr_mismatch (tree op, enum constraint_num cn)
+{
+  int regno = decode_reg_name (op);
+  machine_mode mode = TYPE_MODE (TREE_TYPE (op));
+  enum reg_class rclass = reg_class_for_constraint (cn);
+  if (rclass != NO_REGS
+      && !in_hard_reg_set_p (reg_class_contents[rclass], mode, regno))
+    {
+      const char *msg = "constraint and register %<asm%> do not coincide";
+      if (flag_strict_extended_asm)
+       {
+         error (msg);
+         return true;
+       }
+      else
+       warning (OPT_Wstrict_extended_asm, msg);
+    }
+  return false;
+}
+
 /* Parse the output constraint pointed to by *CONSTRAINT_P.  It is the
    OPERAND_NUMth output operand, indexed from zero.  There are NINPUTS
    inputs and NOUTPUTS outputs to this extended-asm.  Upon return,
@@ -439,9 +463,7 @@ parse_output_constraint (const char **constraint_p, int 
operand_num,
                if (VAR_P (reg_info->operand)
                    && DECL_HARD_REGISTER (reg_info->operand))
                  {
-                     tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
-                     const char *asmspec = IDENTIFIER_POINTER (id) + 1;
-                     int regno_op = decode_reg_name (asmspec);
+                     int regno_op = decode_reg_name (reg_info->operand);
                      if (regno != regno_op)
                        {
                          error ("constraint and register %<asm%> for output "
@@ -482,9 +504,7 @@ parse_output_constraint (const char **constraint_p, int 
operand_num,
              && VAR_P (reg_info->operand)
              && DECL_HARD_REGISTER (reg_info->operand))
            {
-               tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
-               const char *asmspec = IDENTIFIER_POINTER (id) + 1;
-               int regno = decode_reg_name (asmspec);
+               int regno = decode_reg_name (reg_info->operand);
                if (regno < 0)
                  {
                    location_t loc = DECL_SOURCE_LOCATION (reg_info->operand);
@@ -499,6 +519,8 @@ parse_output_constraint (const char **constraint_p, int 
operand_num,
                           reg_names[overlap_regno]);
                    return false;
                  }
+               if (diag_reg_asm_cstr_mismatch (reg_info->operand, cn))
+                 return false;
                reg_info->set_reg_asm_output (regno);
                if (early_clobbered)
                  reg_info->set_early_clobbered (alt, operand_num, regno);
@@ -613,6 +635,20 @@ repeat:
              return false;
            }
 
+         /* Verify matching constraint operands use the same hard register.  */
+         tree match_op;
+         if (reg_info
+             && VAR_P (reg_info->operand)
+             && DECL_HARD_REGISTER (reg_info->operand)
+             && VAR_P (match_op = TREE_VALUE ((*(reg_info->m_outputs))[match]))
+             && DECL_HARD_REGISTER (match_op)
+             && decode_reg_name (reg_info->operand) != decode_reg_name 
(match_op))
+           {
+             error ("invalid hard register usage between output operand and "
+                    "matching constraint operand");
+             return false;
+           }
+
          /* Try and find the real constraint for this dup.  Only do this
             if the matching constraint is the only alternative.  */
          if (*end == '\0'
@@ -691,9 +727,7 @@ repeat:
              if (VAR_P (reg_info->operand)
                  && DECL_HARD_REGISTER (reg_info->operand))
                {
-                   tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
-                   const char *asmspec = IDENTIFIER_POINTER (id) + 1;
-                   int regno_op = decode_reg_name (asmspec);
+                   int regno_op = decode_reg_name (reg_info->operand);
                    if (regno != regno_op)
                      {
                        error ("constraint and register %<asm%> for input "
@@ -739,9 +773,7 @@ repeat:
            && VAR_P (reg_info->operand)
            && DECL_HARD_REGISTER (reg_info->operand))
          {
-             tree id = DECL_ASSEMBLER_NAME (reg_info->operand);
-             const char *asmspec = IDENTIFIER_POINTER (id) + 1;
-             int regno = decode_reg_name (asmspec);
+             int regno = decode_reg_name (reg_info->operand);
              if (regno < 0)
                {
                  location_t loc = DECL_SOURCE_LOCATION (reg_info->operand);
@@ -756,6 +788,8 @@ repeat:
                         reg_names[overlap_regno]);
                  return false;
                }
+             if (diag_reg_asm_cstr_mismatch (reg_info->operand, cn))
+                 return false;
              reg_info->set_reg_asm_input (regno);
              if ((constraint == orig_constraint
                   && reg_info->test_early_clobbered_alt (alt, regno))
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c 
b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
new file mode 100644
index 00000000000..235f0810d3d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
@@ -0,0 +1,53 @@
+/* { dg-do compile { target aarch64*-*-* s390x-*-* x86_64-*-* } } */
+/* { dg-additional-options "-fstrict-extended-asm" } */
+/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
+
+/* Keep this file in sync with asm-hard-reg-strict-2.c.  */
+
+#if __aarch64__
+# define GPR "r5"
+# define FPR "d5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "w"
+#elif __s390x__
+# define GPR "r5"
+# define FPR "f5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "f"
+#elif __x86_64__
+# define GPR "cx"
+# define FPR "xmm5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "x"
+#else
+# error unsupported target
+#endif
+
+int
+test ()
+{
+  register int x __asm__ (GPR) = 42;
+  register float y __asm__ (FPR) = 42;
+
+  __asm__ ("" : "="CSTR_FPR (x)); /* { dg-error "constraint and register 'asm' 
do not coincide" } */
+  __asm__ ("" : "="CSTR_FPR","CSTR_FPR (x)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_GPR","CSTR_FPR (x)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_FPR","CSTR_GPR (x)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+
+  __asm__ __volatile ("" :: CSTR_FPR (x)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_FPR","CSTR_FPR (x)); /* { dg-error 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (x)); /* { dg-error 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (x)); /* { dg-error 
"constraint and register 'asm' do not coincide" } */
+
+  __asm__ ("" : "="CSTR_GPR (y)); /* { dg-error "constraint and register 'asm' 
do not coincide" } */
+  __asm__ ("" : "="CSTR_GPR","CSTR_GPR (y)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_FPR","CSTR_GPR (y)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_GPR","CSTR_FPR (y)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+
+  __asm__ __volatile ("" :: CSTR_GPR (y)); /* { dg-error "constraint and 
register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_GPR","CSTR_GPR (y)); /* { dg-error 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (y)); /* { dg-error 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (y)); /* { dg-error 
"constraint and register 'asm' do not coincide" } */
+
+  return x + y;
+}
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c 
b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
new file mode 100644
index 00000000000..3671171edf0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
@@ -0,0 +1,54 @@
+/* { dg-do compile { target aarch64*-*-* s390x-*-* x86_64-*-* } } */
+/* { dg-additional-options "-fno-strict-extended-asm -Wstrict-extended-asm" } 
*/
+/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
+
+/* This is a copy of asm-hard-reg-strict-1.c for -fno-strict-register-asm where
+   we expect warnings instead of errors.  */
+
+#if __aarch64__
+# define GPR "r5"
+# define FPR "d5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "w"
+#elif __s390x__
+# define GPR "r5"
+# define FPR "f5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "f"
+#elif __x86_64__
+# define GPR "cx"
+# define FPR "xmm5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "x"
+#else
+# error unsupported target
+#endif
+
+int
+test ()
+{
+  register int x __asm__ (GPR) = 42;
+  register float y __asm__ (FPR) = 42;
+
+  __asm__ ("" : "="CSTR_FPR (x)); /* { dg-warning "constraint and register 
'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_FPR","CSTR_FPR (x)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_GPR","CSTR_FPR (x)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_FPR","CSTR_GPR (x)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+
+  __asm__ __volatile ("" :: CSTR_FPR (x)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_FPR","CSTR_FPR (x)); /* { dg-warning 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (x)); /* { dg-warning 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (x)); /* { dg-warning 
"constraint and register 'asm' do not coincide" } */
+
+  __asm__ ("" : "="CSTR_GPR (y)); /* { dg-warning "constraint and register 
'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_GPR","CSTR_GPR (y)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_FPR","CSTR_GPR (y)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+  __asm__ ("" : "="CSTR_GPR","CSTR_FPR (y)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+
+  __asm__ __volatile ("" :: CSTR_GPR (y)); /* { dg-warning "constraint and 
register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_GPR","CSTR_GPR (y)); /* { dg-warning 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (y)); /* { dg-warning 
"constraint and register 'asm' do not coincide" } */
+  __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (y)); /* { dg-warning 
"constraint and register 'asm' do not coincide" } */
+
+  return x + y;
+}
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c 
b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
new file mode 100644
index 00000000000..8abfe1b6380
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
@@ -0,0 +1,72 @@
+/* { dg-do compile { target aarch64*-*-* s390x-*-* x86_64-*-* } } */
+/* { dg-options "-fstrict-extended-asm" } */
+
+/* Keep this file in sync with asm-hard-reg-strict-4.c.  */
+
+#if defined __aarch64__
+# define GPR "x19"
+# define GPR_PAIR_A "x0"
+# define GPR_PAIR_B "x1"
+#elif defined __s390x__
+# define GPR "r7"
+# define GPR_PAIR_A "r4"
+# define GPR_PAIR_B "r5"
+#elif defined __x86_64__
+# define GPR "rbx"
+# define GPR_PAIR_A "rax"
+# define GPR_PAIR_B "rdx"
+#else
+# error unsupported target
+#endif
+
+register int g  __asm__ (GPR);
+register int g2 __asm__ (GPR); /* { dg-warning "register of 'g2' used for 
multiple global register variables" } */
+
+void
+test ()
+{
+  register int x __asm__ (GPR) = 42;
+  register int y __asm__ (GPR) = 24;
+  int z;
+  register __int128 a __asm__ (GPR_PAIR_A) = 42;
+  register      int b __asm__ (GPR_PAIR_B) = 24;
+  register      int c __asm__ (GPR_PAIR_A) = 24;
+
+  /* Overlapping single register asm input operands.  */
+  __asm__ __volatile__ ("" : "+r" (x) : "r" (x));           /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (x) : "0" (x), "r" (x));  /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (x), "r" (x));  /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (x), "r" (y));  /* { dg-error 
"multiple inputs to hard register" } */
+
+  /* Early clobber is somewhat related if input and output operand registers
+     overlap but still distinct.  */
+  __asm__ __volatile__ ("" : "=&r" (x) : "r" (x));          /* { dg-error 
"invalid hard register usage between earlyclobber operand and input operand" } 
*/
+  __asm__ __volatile__ ("" : "=&r" (x) : "r" (y));          /* { dg-error 
"invalid hard register usage between earlyclobber operand and input operand" } 
*/
+  __asm__ __volatile__ ("" : "=&r" (a) : "r" (b));          /* { dg-error 
"invalid hard register usage between earlyclobber operand and input operand" } 
*/
+
+  /* Overlapping register-pair asm input operands.  */
+  __asm__ __volatile__ ("" : "+r" (a) : "r" (b));           /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (a) : "0" (a), "r" (b));  /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (a), "r" (b));  /* { dg-error 
"multiple inputs to hard register" } */
+
+  /* Overlapping global register asm input operands.  */
+  __asm__ __volatile__ ("" : "+r" (g) : "r" (g));           /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (g) : "0" (g), "r" (g));  /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (g), "r" (g));  /* { dg-error 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (x), "r" (g));  /* { dg-error 
"multiple inputs to hard register" } */
+
+  /* Overlapping register asm output operands.  */
+  __asm__ __volatile__ ("" : "=r" (x), "=r" (y));           /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (x), "=r" (g));           /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (g), "=r" (g2));          /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (a), "=r" (c));           /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (a), "=r" (b));           /* { dg-error 
"multiple outputs to hard register" } */
+
+  /* Same lvalue.  */
+  __asm__ __volatile__ ("" : "=r" (x), "=r" (x));           /* { dg-error 
"multiple outputs to lvalue 'x'" } */
+  __asm__ __volatile__ ("" : "=r" (g), "=r" (g));           /* { dg-error 
"multiple outputs to lvalue 'g'" } */
+  __asm__ __volatile__ ("" : "=r" (z), "=r" (z));           /* { dg-error 
"multiple outputs to lvalue 'z'" } */
+  __asm__ __volatile__ ("" : "=m" (z), "=r" (z));           /* { dg-error 
"multiple outputs to lvalue 'z'" } */
+  __asm__ __volatile__ ("" : "=r" (z), "=m" (z));           /* { dg-error 
"multiple outputs to lvalue 'z'" } */
+  __asm__ __volatile__ ("" : "=m" (z), "=m" (z));           /* { dg-error 
"multiple outputs to lvalue 'z'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c 
b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
new file mode 100644
index 00000000000..0e03923839c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
@@ -0,0 +1,74 @@
+/* { dg-do compile { target aarch64*-*-* s390x-*-* x86_64-*-* } } */
+/* { dg-options "-fno-strict-extended-asm -Wstrict-extended-asm" } */
+
+/* This is a copy of asm-hard-reg-strict-3.c for -fno-strict-register-asm where
+   we expect warnings instead of errors (except for a few cases where we
+   errored out even in non-strict extended asm.  */
+
+#if defined __aarch64__
+# define GPR "x19"
+# define GPR_PAIR_A "x0"
+# define GPR_PAIR_B "x1"
+#elif defined __s390x__
+# define GPR "r7"
+# define GPR_PAIR_A "r4"
+# define GPR_PAIR_B "r5"
+#elif defined __x86_64__
+# define GPR "rbx"
+# define GPR_PAIR_A "rax"
+# define GPR_PAIR_B "rdx"
+#else
+# error unsupported target
+#endif
+
+register int g  __asm__ (GPR);
+register int g2 __asm__ (GPR); /* { dg-warning "register of 'g2' used for 
multiple global register variables" } */
+
+void
+test ()
+{
+  register int x __asm__ (GPR) = 42;
+  register int y __asm__ (GPR) = 24;
+  int z;
+  register __int128 a __asm__ (GPR_PAIR_A) = 42;
+  register      int b __asm__ (GPR_PAIR_B) = 24;
+  register      int c __asm__ (GPR_PAIR_A) = 24;
+
+  /* Overlapping single register asm input operands.  */
+  __asm__ __volatile__ ("" : "+r" (x) : "r" (x));           /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (x) : "0" (x), "r" (x));  /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (x), "r" (x));  /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (x), "r" (y));  /* { dg-warning 
"multiple inputs to hard register" } */
+
+  /* Early clobber is somewhat related if input and output operand registers
+     overlap but still distinct.  */
+  __asm__ __volatile__ ("" : "=&r" (x) : "r" (x));          /* { dg-error 
"invalid hard register usage between earlyclobber operand and input operand" } 
*/
+  __asm__ __volatile__ ("" : "=&r" (x) : "r" (y));          /* { dg-error 
"invalid hard register usage between earlyclobber operand and input operand" } 
*/
+  __asm__ __volatile__ ("" : "=&r" (a) : "r" (b));          /* { dg-error 
"invalid hard register usage between earlyclobber operand and input operand" } 
*/
+
+  /* Overlapping register-pair asm input operands.  */
+  __asm__ __volatile__ ("" : "+r" (a) : "r" (b));           /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (a) : "0" (a), "r" (b));  /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (a), "r" (b));  /* { dg-warning 
"multiple inputs to hard register" } */
+
+  /* Overlapping global register asm input operands.  */
+  __asm__ __volatile__ ("" : "+r" (g) : "r" (g));           /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (g) : "0" (g), "r" (g));  /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (g), "r" (g));  /* { dg-warning 
"multiple inputs to hard register" } */
+  __asm__ __volatile__ ("" :          : "r" (x), "r" (g));  /* { dg-warning 
"multiple inputs to hard register" } */
+
+  /* Overlapping register asm output operands.  */
+  __asm__ __volatile__ ("" : "=r" (x), "=r" (y));           /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (x), "=r" (g));           /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (g), "=r" (g2));          /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (a), "=r" (c));           /* { dg-error 
"multiple outputs to hard register" } */
+  __asm__ __volatile__ ("" : "=r" (a), "=r" (b));           /* { dg-warning 
"multiple outputs to hard register" } */
+
+  /* Same lvalue.  */
+  __asm__ __volatile__ ("" : "=r" (x), "=r" (x));           /* { dg-error 
"multiple outputs to lvalue 'x'" } */
+  __asm__ __volatile__ ("" : "=r" (g), "=r" (g));           /* { dg-error 
"multiple outputs to lvalue 'g'" } */
+  __asm__ __volatile__ ("" : "=r" (z), "=r" (z));           /* { dg-warning 
"multiple outputs to lvalue 'z'" } */
+  __asm__ __volatile__ ("" : "=m" (z), "=r" (z));           /* { dg-warning 
"multiple outputs to lvalue 'z'" } */
+  __asm__ __volatile__ ("" : "=r" (z), "=m" (z));           /* { dg-warning 
"multiple outputs to lvalue 'z'" } */
+  __asm__ __volatile__ ("" : "=m" (z), "=m" (z));           /* { dg-warning 
"multiple outputs to lvalue 'z'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c 
b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
new file mode 100644
index 00000000000..fae95ffe75c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
@@ -0,0 +1,65 @@
+/* { dg-do compile { target aarch64*-*-* s390x-*-* x86_64-*-* } } */
+/* { dg-additional-options "-fstrict-extended-asm -fdump-tree-gimple" } */
+/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
+
+/* Test rewriting constraints into hard register constraints and demote
+   register asm objects into ordinary objects.  */
+
+#if __aarch64__
+# define GPR "r5"
+# define FPR "d5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "w"
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{r5\}\" x0\\);" 1 
"gimple" { target aarch64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{r5\}\" x0\\);" 
1 "gimple" { target aarch64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{r5\},g\" x0\\);" 
1 "gimple" { target aarch64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{d5\}\" x1\\);" 1 
"gimple" { target aarch64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{d5\}\" x1\\);" 
1 "gimple" { target aarch64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{d5\},g\" x1\\);" 
1 "gimple" { target aarch64-*-* } } } */
+#elif __s390x__
+# define GPR "r5"
+# define FPR "f5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "f"
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{r5\}\" x0\\);" 1 
"gimple" { target s390x-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{r5\}\" x0\\);" 
1 "gimple" { target s390x-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{r5\},g\" x0\\);" 
1 "gimple" { target s390x-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{f5\}\" x1\\);" 1 
"gimple" { target s390x-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{f5\}\" x1\\);" 
1 "gimple" { target s390x-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{f5\},g\" x1\\);" 
1 "gimple" { target s390x-*-* } } } */
+#elif __x86_64__
+# define GPR "cx"
+# define FPR "xmm5"
+# define CSTR_GPR "r"
+# define CSTR_FPR "x"
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{cx\}\" x0\\);" 1 
"gimple" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{cx\}\" x0\\);" 
1 "gimple" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{cx\},g\" x0\\);" 
1 "gimple" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{xmm5\}\" x1\\);" 1 
"gimple" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{xmm5\}\" 
x1\\);" 1 "gimple" { target x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{xmm5\},g\" 
x1\\);" 1 "gimple" { target x86_64-*-* } } } */
+#else
+# error unsupported target
+#endif
+
+int
+test_gpr (void)
+{
+/* { dg-final { scan-tree-dump-times "int x0;" 1 "gimple" } } */
+  register int x0 __asm__ (GPR);
+  __asm__ ("" : "="CSTR_GPR (x0));
+  __asm__ ("" : "=g,!"CSTR_GPR (x0));
+  __asm__ ("" : "=!"CSTR_GPR",g" (x0));
+  return x0;
+}
+
+float
+test_fpr (void)
+{
+/* { dg-final { scan-tree-dump-times "float x1;" 1 "gimple" } } */
+  register float x1 __asm__ (FPR);
+  __asm__ ("" : "="CSTR_FPR (x1));
+  __asm__ ("" : "=g,!"CSTR_FPR (x1));
+  __asm__ ("" : "=!"CSTR_FPR",g" (x1));
+  return x1;
+}
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c 
b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c
new file mode 100644
index 00000000000..430a8aac612
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c
@@ -0,0 +1,13 @@
+/* { dg-do compile { target aarch64*-*-* s390x-*-* x86_64-*-* } } */
+/* { dg-additional-options "-fstrict-extended-asm -Wuninitialized" } */
+
+/* Since for -fstrict-extended-asm usage of uninitialized register asm input
+   operands is undefined, test for uninit warnings.  */
+
+int
+test (void)
+{
+  register int x __asm__ ("5");
+  __asm__ ("" : "+r" (x)); /* { dg-warning "is used uninitialized" } */
+  return x;
+}
diff --git a/gcc/testsuite/gcc.dg/pr87600-2.c b/gcc/testsuite/gcc.dg/pr87600-2.c
index 822afe04ef1..168e1a7faca 100644
--- a/gcc/testsuite/gcc.dg/pr87600-2.c
+++ b/gcc/testsuite/gcc.dg/pr87600-2.c
@@ -11,7 +11,7 @@ test0 (void)
 {
   register long var1 asm (REG1);
   register long var2 asm (REG1);
-  asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "invalid hard 
register usage between output operands" } */
+  asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "multiple 
outputs to hard register" } */
   return var1;
 }
 
diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-7.c 
b/gcc/testsuite/gcc.target/s390/asm-hard-reg-7.c
index ef9275327e1..f4ffc0c342d 100644
--- a/gcc/testsuite/gcc.target/s390/asm-hard-reg-7.c
+++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-7.c
@@ -13,13 +13,13 @@ test (void)
   long double y;
 
   /* Outputs */
-  __asm__ __volatile__ ("" : "=f" (f0), "=f" (f0f2));
+  __asm__ __volatile__ ("" : "=f" (f0), "=f" (f0f2));    /* { dg-error 
"multiple outputs to hard register: %f0" } */
   __asm__ __volatile__ ("" : "=f" (f0f2), "={f0}" (y));  /* { dg-error 
"multiple outputs to hard register: %f0" } */
   __asm__ __volatile__ ("" : "={f0}" (x), "=f" (f0f2));  /* { dg-error 
"multiple outputs to hard register: %f0" } */
 
   __asm__ __volatile__ ("" : "=f" (f2), "=f" (f0f2));
   __asm__ __volatile__ ("" : "={f2}" (x), "={f0}" (y));  /* { dg-error 
"multiple outputs to hard register: %f2" } */
-  __asm__ __volatile__ ("" : "=f" (f2), "={f0}" (y));  /* { dg-error "multiple 
outputs to hard register: %f2" } */
+  __asm__ __volatile__ ("" : "=f" (f2), "={f0}" (y));    /* { dg-error 
"multiple outputs to hard register: %f2" } */
   __asm__ __volatile__ ("" : "={f2}" (x), "=f" (f0f2));  /* { dg-error 
"multiple outputs to hard register: %f2" } */
 
   /* Inputs */
@@ -29,6 +29,6 @@ test (void)
 
   __asm__ __volatile__ ("" :: "f" (f2), "f" (f0f2));
   __asm__ __volatile__ ("" :: "{f2}" (x), "{f0}" (y));  /* { dg-error 
"multiple inputs to hard register: %f2" } */
-  __asm__ __volatile__ ("" :: "f" (f2), "{f0}" (y));  /* { dg-error "multiple 
inputs to hard register: %f2" } */
+  __asm__ __volatile__ ("" :: "f" (f2), "{f0}" (y));    /* { dg-error 
"multiple inputs to hard register: %f2" } */
   __asm__ __volatile__ ("" :: "{f2}" (x), "f" (f0f2));  /* { dg-error 
"multiple inputs to hard register: %f2" } */
 }
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index 0dbc35d926a..88a12407756 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -1100,6 +1100,22 @@ decode_reg_name (const char *name)
   return decode_reg_name_and_count (name, &count);
 }
 
+/* Decode and return the register number referred to by a register asm.  The
+   return value is negative in case the argument is not a register asm or in
+   cases described in the comment for decode_reg_name_and_count.  */
+
+int
+decode_reg_name (tree x)
+{
+  if (!VAR_P (x) || !DECL_HARD_REGISTER (x))
+    return -1;
+  tree id = DECL_ASSEMBLER_NAME (x);
+  const char *asmspec = IDENTIFIER_POINTER (id);
+  /* Skip asterisk marker.  */
+  ++asmspec;
+  return decode_reg_name (asmspec);
+}
+
 
 /* Return true if DECL's initializer is suitable for a BSS section.  */
 
-- 
2.54.0

Reply via email to