@@ -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
@@ -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.
@@ -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}
@@ -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)
{
@@ -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);
@@ -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. */
@@ -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))
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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'" } */
+}
new file mode 100644
@@ -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'" } */
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
@@ -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;
}
@@ -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" } */
}
@@ -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. */