On Thu, Jun 25, 2026 at 03:26:36PM +0200, Richard Biener wrote:
> On Sun, May 24, 2026 at 7:40 PM Stefan Schulze Frielinghaus
> <[email protected]> wrote:
> >
> > From: Stefan Schulze Frielinghaus <[email protected]>
> >
> > This is a followup to
> > https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717097.html
> > with the following changes:
> >
> > I have renamed -fdemote-register-asm into -fstrict-extended-asm.  This 
> > should
> > give users a better intuition if things break.  I'm using
> > -fstrict-extended-asm here instead of -fstrict-register-asm since the
> > analysis happens at extended asms, or in other words you could still have
> > unusual usages of register asm across multiple extended asms which are not
> > diagnosed, i.e., the analysis is limited to a single extended asm statement.
> >
> > Speaking of diagnostics, I have added -Wstrict-extended-asm which is 
> > enabled by
> > -Wall for C/C++ in order to diagnose cases for which we error out with
> > -fstrict-register-asm (see new tests asm-hard-reg-strict-*.c).
> >
> > I have generalized the odd ball cases if an lvalue is used for multiple 
> > output
> > operands.  Previously, we diagnosed
> >
> > register int x __asm__ ("5");
> > __asm__ __volatile__ ("" : "=r" (x), "=r" (x));
> >
> > with an error: "invalid hard register usage between output operands"
> > This was done only for register asm objects during expand.  Now, I diagnose 
> > if
> > any two output operands refer to the same lvalue.  This is done 
> > independently
> > of constraints and types of output operands.  In order to align all those
> > different combinations I went for diagnosing those with: "multiple outputs 
> > to
> > lvalue 'x'".  Therefore, for the register asm example from above the
> > diagnostics change.  I guess having "non-stable" error messages for the 
> > sake of
> > streamlining is fine ... just wanted to make it clear here.
> >
> > While working on the diagnostics I somehow thought that it would be nice 
> > here
> > and there to refer to a concrete operand.  For example, with this patch I
> > diagnose
> >
> > register int x __asm__ ("5") = 42;
> > asm ("" : "+r" (x) : "r" (x));
> >
> > with
> >
> > t.c:11:3: warning: multiple inputs to hard register: %r5 
> > [-Wstrict-extended-asm]
> >    11 |   __asm__ __volatile__ ("" : "+r" (x) : "r" (x));
> >       |   ^~~~~~~
> >
> > Instead of just pointing to the asm statement it would be nice to have some
> > ascii art in order to point to the concrete operands as for example
> >
> > t.c:11:3: warning: multiple inputs to hard register: %r5 
> > [-Wstrict-extended-asm]
> >    11 |   __asm__ __volatile__ ("" : "+r" (x) : "r" (x));
> >       |                                    ^~~~~~~~~~^
> >
> > Of course, this example behaved good in the sense that it is easy to draw a
> > line between operands.  It would be more complicated if operands were 
> > aligned
> > in different rows and what not.  So maybe this is just wishful thinking.
> > Anyhow, I'm not familiar with the diagnostics system at all, however, if
> > someone has a pointer for me, I'm happy to take a look-see.
> >
> > Cheers,
> > Stefan
> >
> > -- >8 --
> >
> > Currently local register asm assignments materialize during expand into
> > assignments utilizing hard registers.  Since 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 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)
> > {
> >   register int tmp asm ("r5");
> >   int x = x0;
> >   int y = tmp;
> >
> >   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;
> > }
> >
> > 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.  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.
> >
> > 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 warning.
> >
> > Note, uninitialized register asm objects may be used as inputs.  Thus,
> > if naively translated into hard register constraints, this would
> > introduce reads from uninitialized objects which init-regs pass would
> > fix up which in turn would mean that once hard register constraints
> > materialize, respective registers would be zeroed (see comment in
> > gimplify.cc for more details).  This is solved by initializing every
> > uninitialized register asm object by a fresh register asm object
> > ensuring that it contains the respective register value.  Subsequent
> > passes remove dead stores in case those objects are eventually
> > initialized at later points or are used exclusively as output operands.
> > Therefore, in most cases, those temporary register asm objects won't
> > materialize.  This is not pretty at all but required in order to compile
> > real world applications as e.g. glibc for target powerpc64le.
> 
> I wonder if we can fix init-regs to not consider an asm use as use
> and skip initialization?  (init-regs should go away anyway)
> Is there user-level documentation about this "feature"?

Even if I disable init-regs temporarily and prevent asmcons to introduce
any manual reload, LRA will do and assign a different register to the
uninitialized pseudo.  For example, prior LRA we have

(insn 6 7 12 2 (set (reg:SI 62 [ x ])
        (asm_operands:SI ("# %0") ("={0}") 0 [
                (reg/v:SI 60 [ x ])
            ]
             [
                (asm_input:SI ("0") t.c:5)
            ]
             [] t.c:5)) "t.c":5:3 -1
     (expr_list:REG_DEAD (reg/v:SI 60 [ x ])
        (nil)))

where pseudo 60 has no other use.  During LRA, an input reload is
introduced and pseudo 60 is assigned a different register than 0 which
renders this even worse than just zeroing:

(insn 17 7 6 2 (set (reg/v:SI 0 %r0 [orig:60 x ] [60])
        (reg/v:SI 2 %r2 [orig:60 x ] [60])) "t.c":5:3 1949 {*movsi_zarch}
     (nil))
(insn 6 17 18 2 (set (reg/v:SI 0 %r0 [orig:60 x ] [60])
        (asm_operands:SI ("# %0") ("={0}") 0 [
                (reg/v:SI 0 %r0 [orig:60 x ] [60])
            ]
             [
                (asm_input:SI ("0") t.c:5)
            ]
             [] t.c:5)) "t.c":5:3 -1
     (nil))

Now, r2 is copied into r0 instead of just zeroing it.

> 
> I'd like to see us enable -Wstrict-extended-asm by default

With this patch version -Wstrict-extended-asm is enabled if -Wall is
given.  Do you mean by enabling by default also in case -Wall is not
given?

> and
> document that -fstrict-extended-asm while default off for now
> will be enabled by default in a future release.
> 
> The patch looks good to me apart from the uninit reg case which indeed
> is ugly.  We can eventually live with this, but it would keep pre-RA
> local hardregs which is ... unwanted (and fragile as well).

Absolutely agree.  I was a bit shivering when implementing it ;-) It is
a pity that we didn't warn about this previously.  Though, what I
haven't mentioned so far is that if -Wall is used we of course warn
about this.  Assuming that this hack is removed for:

int
test ()
{
  register int x __asm__ ("0");
  __asm__ ("# %0" : "+r" (x));
  return x;
}

we get:

t.c: In function 'test':
t.c:5:3: warning: 'x' is used uninitialized [-Wuninitialized]
    5 |   __asm__ ("# %0" : "+r" (x));
      |   ^~~~~~~
t.c:4:16: note: 'x' was declared here
    4 |   register int x __asm__ ("0");
      |                ^

My initial intentions were to keep the current behaviour in order to
have a broader acceptance ... maybe I went a little bit to far here
since preserving the behaviour of uninitialized reads doesn't resonate
well on IL.  If there is consensus to keep the current behaviour maybe
another solution would be to tackle this during LRA ... something I
would need to think about.

Cheers,
Stefan

> 
> Richard.
> 
> >
> > gcc/ChangeLog:
> >
> >         * common.opt (Wstrict-extended-asm): New warning.
> >         (fstrict-extended-asm) New flag.
> >         * doc/invoke.texi: Document new warning and flag.
> >         * gimplify.cc (gimplify_decl_expr): Deal with uninitialized
> >         register asm objects.
> >         (gimplify_demote_register_asm): Helper for gimplify_asm_expr in
> >         order to prepare demotion of a register asm object into an 
> > orderinary
> >         one by rewriting constaints.
> >         (gimplify_asm_expr): Add diagnostics logic for new warning.
> >         (gimplify_body): Cleanup helper hash set.
> >         * stmt.cc (rclass_entails_registers): Helper while parsing
> >         constraints.
> >         (parse_output_constraint): Diagnose if register asm and
> >         constraint do not coincide.
> >         (parse_input_constraint): Ditto.
> >
> > gcc/testsuite/ChangeLog:
> >
> >         * 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/common.opt                               |   8 +
> >  gcc/doc/invoke.texi                          |  61 ++++-
> >  gcc/gimplify.cc                              | 247 ++++++++++++++++---
> >  gcc/stmt.cc                                  |  42 ++++
> >  gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c |  51 ++++
> >  gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c |  54 ++++
> >  gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c |  22 ++
> >  gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c |  25 ++
> >  gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c |  65 +++++
> >  9 files changed, 532 insertions(+), 43 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
> >
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index 3ad1444cc88..933f791aeb4 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -936,6 +936,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
> >
> > @@ -3588,6 +3592,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 6d1da9f610e..c394c8e864f 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  -fstrict-flex-arrays[=@var{n}]
> > --fsso-struct=@var{endianness}  --ansi}
> > +-fstrict-extended-asm -fsso-struct=@var{endianness}  --ansi}
> >
> >  @item C++ Language Options
> >  @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
> > @@ -448,7 +448,7 @@ Objective-C and Objective-C++ Dialects}.
> >  -Wstrict-aliasing=@var{n}  -Wstrict-overflow  -Wstrict-overflow=@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
> > @@ -2976,6 +2976,15 @@ 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}).  Among 
> > other
> > +things, this means that local 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.  See also
> > +@option{-Wstrict-extended-asm}.
> > +
> >  @opindex fsso-struct
> >  @item -fsso-struct=@var{endianness}
> >  Set the default scalar storage order of structures and unions to the
> > @@ -8990,6 +8999,54 @@ 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
> > +
> > +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 e4db4b1d9bd..951fd69a98b 100644
> > --- a/gcc/gimplify.cc
> > +++ b/gcc/gimplify.cc
> > @@ -2246,6 +2246,40 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
> >               && clear_padding_type_may_have_padding_p (TREE_TYPE (decl)))
> >             gimple_add_padding_init_for_auto_var (decl, is_vla, seq_p);
> >         }
> > +      else if (flag_strict_extended_asm && !init && DECL_HARD_REGISTER 
> > (decl))
> > +       {
> > +         /* Register asm objects may be used uninitialized as inputs.
> > +            Therefore, a naive translation into hard register constraints
> > +            would render the demoted objects to be default initialized by
> > +            init-regs and as a consequence, once hard register constraints
> > +            materialize for an Extended Asm, hard registers would be zeroed
> > +            which didn't happen before.  Therefore, to overcome this,
> > +            initialize each demoted object with the current contents of the
> > +            hard register it previously referred to.  For example, 
> > translate
> > +
> > +            register int x asm ("r5");
> > +            int y;
> > +            asm ("..." : "=r" (y) : "r" (x));
> > +
> > +            to
> > +
> > +            register int tmp asm ("r5");
> > +            int x = tmp;
> > +            int y;
> > +            asm ("..." : "=r" (y) : "{r5}" (x));
> > +
> > +            Do this unconditionally for all uninitialized register asm 
> > objects
> > +            and let subsequent passes remove dead stores in case those 
> > objects
> > +            are initialized at later points or are used exclusively as 
> > output
> > +            operands.  */
> > +         tree tmp = create_tmp_var (TREE_TYPE (decl), "regasm");
> > +         SET_DECL_ASSEMBLER_NAME (tmp, DECL_ASSEMBLER_NAME (decl));
> > +         DECL_REGISTER (tmp) = 1;
> > +         DECL_HARD_REGISTER (tmp) = 1;
> > +         DECL_INITIAL (decl) = tmp;
> > +         *stmt_p = stmt;
> > +         return gimplify_decl_expr (stmt_p, seq_p);
> > +       }
> >      }
> >
> >    return GS_ALL_DONE;
> > @@ -7976,6 +8010,71 @@ num_alternatives (const_tree link)
> >    return num + 1;
> >  }
> >
> > +/* Keep track of all register asm which have been replaced by hard register
> > +   constraints.  After all asm statements of a function have been 
> > processed,
> > +   demote those to ordinary objects.  */
> > +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 objects into hard register
> > +   constraints.  Also mark those objects to be demoted from register asm
> > +   objects to ordinary objects 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)
> > +       {
> > +         /* At this point we have a constraint which entails all the
> > +            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);
> > +}
> > +
> >  /* Gimplify the operands of an ASM_EXPR.  Input operands should be a gimple
> >     value; output operands should be a gimple lvalue.  */
> >
> > @@ -8063,6 +8162,43 @@ 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);
> > +       }
> > +
> >        /* If we can't make copies, we can only accept memory.
> >          Similarly for VLAs.  */
> >        tree outtype = TREE_TYPE (TREE_VALUE (link));
> > @@ -8226,45 +8362,6 @@ 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;
> > -             }
> > -         }
> > -    }
> > -
> >    link_next = NULL_TREE;
> >    int input_num = 0;
> >    for (link = ASM_INPUTS (expr); link; ++input_num, ++i, link = link_next)
> > @@ -8283,8 +8380,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)))
> > @@ -8299,10 +8398,55 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, 
> > gimple_seq *post_p)
> >             }
> >         }
> >
> > +      if (VAR_P (inputv)
> > +         && DECL_HARD_REGISTER (inputv)
> > +         && !is_global_var (inputv))
> > +       {
> > +         tree id1 = DECL_ASSEMBLER_NAME (inputv);
> > +         const char *regname1 = IDENTIFIER_POINTER (id1);
> > +         ++regname1;
> > +         int regno1 = decode_reg_name (regname1);
> > +         if (regno1 >= 0)
> > +           {
> > +             HARD_REG_SET hreg_set1;
> > +             CLEAR_HARD_REG_SET (hreg_set1);
> > +             add_to_hard_reg_set (&hreg_set1, TYPE_MODE (intype), regno1);
> > +             for (tree i = link_next; i; i = TREE_CHAIN (i))
> > +               {
> > +                 tree inputv2 = TREE_VALUE (i);
> > +                 if (VAR_P (inputv2)
> > +                     && DECL_HARD_REGISTER (inputv2)
> > +                     && !is_global_var (inputv2))
> > +                   {
> > +                     tree id2 = DECL_ASSEMBLER_NAME (inputv2);
> > +                     const char *regname2 = IDENTIFIER_POINTER (id2);
> > +                     ++regname2;
> > +                     int regno2 = decode_reg_name (regname2);
> > +                     if (regno2 < 0)
> > +                       continue;
> > +                     tree intype2 = TREE_TYPE (inputv2);
> > +                     int nregs2
> > +                       = hard_regno_nregs (regno2, TYPE_MODE (intype2));
> > +                     for (int j = regno2; j < regno2 + nregs2; ++j)
> > +                       {
> > +                         if (!TEST_HARD_REG_BIT (hreg_set1, j))
> > +                           continue;
> > +                         if (flag_strict_extended_asm)
> > +                           error ("multiple inputs to hard register: %s",
> > +                                  reg_names[j]);
> > +                         else
> > +                           warning (OPT_Wstrict_extended_asm,
> > +                                    "multiple inputs to hard register: %s",
> > +                                    reg_names[j]);
> > +                       }
> > +                   }
> > +               }
> > +           }
> > +       }
> > +
> >        /* 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
> > @@ -8372,6 +8516,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 +22032,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/stmt.cc b/gcc/stmt.cc
> > index 382a2d75c07..cb98d6197fa 100644
> > --- a/gcc/stmt.cc
> > +++ b/gcc/stmt.cc
> > @@ -249,6 +249,15 @@ hardreg_ok_p (int reg_number, machine_mode mode, int 
> > operand_num)
> >    return false;
> >  }
> >
> > +static inline bool
> > +rclass_entails_registers (enum reg_class rclass, int regno, int nregs)
> > +{
> > +  for (int i = regno; i < regno + nregs ; ++i)
> > +    if (!TEST_HARD_REG_BIT (reg_class_contents[rclass], i))
> > +      return false;
> > +  return true;
> > +}
> > +
> >  /* 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,
> > @@ -499,6 +508,23 @@ parse_output_constraint (const char **constraint_p, 
> > int operand_num,
> >                            reg_names[overlap_regno]);
> >                     return false;
> >                   }
> > +               enum reg_class rclass = reg_class_for_constraint (cn);
> > +               machine_mode mode = TYPE_MODE (TREE_TYPE 
> > (reg_info->operand));
> > +               int nregs = hard_regno_nregs (regno, mode);
> > +               if (rclass != NO_REGS
> > +                   && !rclass_entails_registers (rclass, regno, nregs))
> > +                 {
> > +                   if (flag_strict_extended_asm)
> > +                     {
> > +                       error
> > +                         ("constraint and register %<asm%> do not 
> > coincide");
> > +                       return false;
> > +                     }
> > +                   else
> > +                     warning
> > +                       (OPT_Wstrict_extended_asm,
> > +                        "constraint and register %<asm%> do not coincide");
> > +                 }
> >                 reg_info->set_reg_asm_output (regno);
> >                 if (early_clobbered)
> >                   reg_info->set_early_clobbered (alt, operand_num, regno);
> > @@ -756,6 +782,22 @@ repeat:
> >                          reg_names[overlap_regno]);
> >                   return false;
> >                 }
> > +             enum reg_class rclass = reg_class_for_constraint (cn);
> > +             machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand));
> > +             int nregs = hard_regno_nregs (regno, mode);
> > +             if (rclass != NO_REGS
> > +                 && !rclass_entails_registers (rclass, regno, nregs))
> > +               {
> > +                 if (flag_strict_extended_asm)
> > +                   {
> > +                     error ("constraint and register %<asm%> do not 
> > coincide");
> > +                     return false;
> > +                   }
> > +                 else
> > +                   warning
> > +                     (OPT_Wstrict_extended_asm,
> > +                      "constraint and register %<asm%> do not coincide");
> > +               }
> >               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..f8c7cad48a5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
> > @@ -0,0 +1,51 @@
> > +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
> > +/* { dg-additional-options "-fstrict-extended-asm" } */
> > +/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
> > +
> > +#if __aarch64__
> > +# define GPR "r5"
> > +# define FPR "d5"
> > +# define CSTR_GPR "r"
> > +# define CSTR_FPR "w"
> > +#elif __s390__
> > +# 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..cfa783d4e94
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
> > @@ -0,0 +1,54 @@
> > +/* { dg-do compile { target aarch64*-*-* s390*-*-* 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 __s390__
> > +# 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..f3732a028a5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
> > @@ -0,0 +1,22 @@
> > +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
> > +/* { dg-additional-options "-fstrict-extended-asm" } */
> > +
> > +void
> > +test ()
> > +{
> > +  register int x __asm__ ("5") = 42;
> > +  register int y __asm__ ("5") = 24;
> > +  int z;
> > +
> > +  __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" } */
> > +
> > +  __asm__ __volatile__ ("" : "=r" (x), "=r" (x)); /* { dg-error "multiple 
> > outputs to lvalue 'x'" } */
> > +
> > +  __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..98eb0386b3b
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
> > @@ -0,0 +1,25 @@
> > +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
> > +/* { dg-additional-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.  */
> > +
> > +void
> > +test ()
> > +{
> > +  register int x __asm__ ("5") = 42;
> > +  register int y __asm__ ("5") = 24;
> > +  int z;
> > +
> > +  __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" } */
> > +
> > +  __asm__ __volatile__ ("" : "=r" (x), "=r" (x)); /* { dg-error "multiple 
> > outputs to lvalue 'x'" } */
> > +
> > +  __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..699bed70a43
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
> > @@ -0,0 +1,65 @@
> > +/* { dg-do compile { target aarch64*-*-* s390*-*-* 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 __s390__
> > +# 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 s390*-*-* } } } */
> > +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{r5\}\" 
> > x0\\);" 1 "gimple" { target s390*-*-* } } } */
> > +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{r5\},g\" 
> > x0\\);" 1 "gimple" { target s390*-*-* } } } */
> > +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{f5\}\" x1\\);" 
> > 1 "gimple" { target s390*-*-* } } } */
> > +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{f5\}\" 
> > x1\\);" 1 "gimple" { target s390*-*-* } } } */
> > +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{f5\},g\" 
> > x1\\);" 1 "gimple" { target s390*-*-* } } } */
> > +#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;
> > +}
> > --
> > 2.54.0
> >

Reply via email to