On Tue, Jun 30, 2026 at 02:53:32PM +0200, Richard Biener wrote:
> On Sun, Jun 28, 2026 at 2:25 PM Stefan Schulze Frielinghaus
> <[email protected]> wrote:
> >
> > From: Stefan Schulze Frielinghaus <[email protected]>
> >
> > This is a follow-up to
> > https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717805.html
> > with the following changes:
> >
> > - Make usage of uninitialized register asm input operands undefined by
> > removing the hack during gimplify_asm_expr() and document it as
> > undefined.
> > - Remove redundant helper stmt.cc:rclass_entails_registers and use
> > existing function in_hard_reg_set_p instead which computes the same.
> > - Add testsuite/gcc.dg/asm-hard-reg-strict-6.c in order to test for an
> > uninit diagnostics. Of course, in case of -fno-strict-extended-asm
> > -Wstrict-extended-asm -W{maybe-,}uninitialized we still don't get a
> > diagnostics.
> >
> > Cheers,
> > Stefan
> >
> > -- 8< --
> >
> > 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_NAMES 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.
>
> Some comments inline
>
> > 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_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.
> > (gimplify_asm_expr): Add diagnostics logic for new warning.
> > (gimplify_body): Cleanup helper hash set.
> > * stmt.cc (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.dg/asm-hard-reg-strict-6.c: New test.
> > ---
> > gcc/common.opt | 8 +
> > gcc/doc/invoke.texi | 63 +++++-
> > gcc/gimplify.cc | 213 +++++++++++++++----
> > gcc/stmt.cc | 32 +++
> > 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 ++++++
> > gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c | 13 ++
> > 10 files changed, 503 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
> > create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.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..c98887cc5b3 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,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 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. Note, any use of an uninitialized
> > +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.
> > +
> > @opindex fsso-struct
> > @item -fsso-struct=@var{endianness}
> > Set the default scalar storage order of structures and unions to the
> > @@ -8990,6 +9001,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..d524a82c0bb 100644
> > --- a/gcc/gimplify.cc
> > +++ b/gcc/gimplify.cc
> > @@ -7976,6 +7976,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)
> > + {
> > + /* 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);
> > +}
> > +
> > /* Gimplify the operands of an ASM_EXPR. Input operands should be a gimple
> > value; output operands should be a gimple lvalue. */
> >
> > @@ -8063,6 +8128,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 +8328,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 +8346,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 +8364,55 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p,
> > gimple_seq *post_p)
> > }
> > }
> >
>
> A comment before this block would be useful
>
> > + 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))
>
> Seeing this, I wonder what we do to
>
> void foo()
> {
> register int x asm ("%rax");
> void bar()
> {
> asm ("" : : "r" (x));
> }
> }
>
> aka register asm "crossing" nested function context? Because !is_global_var
> isn't the same as auto_var_in_fn_p (inputv2, current_function_decl).
>
> Maybe a local_register_var_p (tree) predicate would help making this less
> repetitive?
>
> > + {
> > + tree id2 = DECL_ASSEMBLER_NAME (inputv2);
> > + const char *regname2 = IDENTIFIER_POINTER (id2);
> > + ++regname2;
> > + int regno2 = decode_reg_name (regname2);
> > + if (regno2 < 0)
> > + continue;
>
> And an overload to decode_reg_name taking the decl?
>
> Otherwise LGTM.
>
> Thanks,
> Richard.
>
> > + 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]);
While skimming over this again: this needs a return GS_ERROR;
Cheers,
Stefan
> > + 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 +8482,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 +21998,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..97cb55ea775 100644
> > --- a/gcc/stmt.cc
> > +++ b/gcc/stmt.cc
> > @@ -499,6 +499,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));
> > + if (rclass != NO_REGS
> > + && !in_hard_reg_set_p (reg_class_contents[rclass], mode,
> > + regno))
> > + {
> > + 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 +773,21 @@ 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));
> > + if (rclass != NO_REGS
> > + && !in_hard_reg_set_p (reg_class_contents[rclass], mode,
> > + regno))
> > + {
> > + 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;
> > +}
> > 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..bb6fe71d5f5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-6.c
> > @@ -0,0 +1,13 @@
> > +/* { dg-do compile { target aarch64*-*-* s390*-*-* 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;
> > +}
> > --
> > 2.54.0
> >