Today output operand modifiers are limited to a single character, as in
%w0 or %c1. That works, but it makes the modifier namespace very small
and encourages target ports either to use cryptic letters or to fall back
to C output blocks for cases that are really just target-specific
printing.
This patch adds a longer spelling for the same mechanism:
%[name]0
The bracketed spelling is intentionally familiar rather than completely new:
extended asm already uses %[Value] to refer to an operand with a symbolic name,
as in:
asm ("add %[out], %[in], %[imm]"
: [out] "=r" (out)
: [in] "r" (in), [imm] "I" (1));
That existing form keeps its meaning. A bare %[Value] is still resolved by
the front end as a symbolic operand reference. The new print-operand form is
recognized when the closing bracket is followed by an operand number, as in
%[name]0.
The modifier syntax can also be combined with symbolic operand names. In user
asm, %[modifier:Value] means "print operand Value using the multi-character
modifier named modifier". The front end resolves Value to an operand number,
so final output sees the same thing it would have seen for %[modifier:N]. For
example:
asm ("mov %[w:out], %[w:in]"
: [out] "=r" (out)
: [in] "r" (in));
is resolved as if the template had used %[w]0 and %[w]1.
GCC parses the bracketed modifier name, interns it, and passes PRINT_OPERAND an
opaque integer code for it. Old-style modifiers keep their existing values, so
existing target hooks continue to see 'w', 'c', etc. for the single-character
forms. Backends that want to handle the new form can call
output_operand_modifier_name (code) to recover "name".
For example, a machine description can now write something like:
"extract\t%[lowpart]0, %1"
or if preferred
"extract\t%[lowpart:0], %1"
and the target's PRINT_OPERAND hook can handle "lowpart" as a named modifier.
The parser also allows comma-separated nonempty components in the brackets, but
deliberately leaves the interpretation of those components to the target.
The patch also records whether an extended modifier came from user asm or from
an internal machine-description output template. Targets can use
output_operand_modifier_user_p (code) to reject internal-only names in
user-facing asm while still using the clearer names internally.
This addresses Richard Earnshaw's suggestion by keeping the target hook
interface as an int code and mapping the longer spelling onto values outside
the single-character range. Targets that do not opt in do not need a new hook
or a new parser path, and existing hard-coded character uses continue to work.
It also addresses Richard Sandiford's feedback by giving the new %[...] syntax
its own namespace rather than automatically exposing every single-letter
modifier through the new spelling. The user-asm bit lets a target decide which
named modifiers are public and which are only for compiler-generated templates.
The comma-separated spelling is accepted as a target-defined modifier string,
so a port can later treat, say, %[gpr,zero]0 as a composition of
target-specific printing choices without requiring more middle-end syntax.
Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.
OK for master?
Thanks,
Tamar
gcc/ChangeLog:
* doc/extend.texi: Document multi-character output operand
modifiers in asm templates, including their use with symbolic
operand names.
* doc/md.texi: Document multi-character output operand modifiers.
Document their use with symbolic operand names in user asm.
* doc/tm.texi: Likewise.
* doc/tm.texi.in: Likewise.
* final.cc: Include selftest.h.
(parse_operand_number): New function.
(valid_operand_modifier_p): New function.
(bracketed_operand): New struct.
(parse_bracketed_operand): New function.
(test_parse_bracketed_operand): New function.
(final_cc_tests): New function.
(output_operand_modifier_names): New variable.
(output_operand_modifier_base): New constant.
(output_operand_modifier_code): New function.
(output_operand_modifier_name): New function.
(output_operand_modifier_user_p): New function.
(output_asm_insn): Parse bracketed operand forms and pass extended
modifier codes to output_operand.
(output_operand): Use the code argument.
* output.h (output_operand_modifier_name): Declare.
(output_operand_modifier_user_p): Declare.
* selftest-run-tests.cc (selftest::run_tests): Call final_cc_tests
and stmt_cc_tests.
* selftest.h (final_cc_tests): Declare.
(stmt_cc_tests): Declare.
* stmt.cc: Include selftest.h when checking is enabled.
(asm_operand_name_start): Declare.
(asm_operand_number_p): New function.
(asm_operand_name_start): New function.
(resolve_asm_operand_names): Use asm_operand_name_start.
(resolve_operand_name_1): Handle %[modifier:name] symbolic operand
references.
(build_asm_operand): New function.
(build_asm_label): New function.
(test_resolve_asm_operand_names): New function.
(stmt_cc_tests): New function.
---
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index
88213d25a899ed5bff25af732220c54d2359b492..1e6f5ddb0a815101f6b6c57bc8f610766e035895
100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12057,7 +12057,14 @@ instead of simply @samp{%2}).
@ref{GenericOperandmodifiers,
Generic Operand modifiers} lists the modifiers that are available
on all targets. Other modifiers are hardware dependent.
For example, the list of supported modifiers for x86 is found at
-@ref{x86Operandmodifiers,x86 Operand modifiers}.
+@ref{x86Operandmodifiers,x86 Operand modifiers}. Targets can also provide
+multi-character modifiers, written as @samp{%[@var{modifier}:2]}.
+
+If the operand has a symbolic name, write
+@samp{%[@var{modifier}:@var{asmSymbolicName}]}. The older spelling
+@samp{%[@var{modifier}]2} is also accepted for numbered operands.
+@samp{%[@var{name}]} without a modifier separator continues to mean the
+operand named @var{name}.
If the C code that follows the @code{asm} makes no use of any of the output
operands, use @code{volatile} for the @code{asm} statement to prevent the
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index
80761b0a8a62d3e22f2c52de372f876396ab15c7..21c9238bf7f77e1a49f7a7379a5ca9f1e92c7914
100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -524,6 +524,42 @@ alternate fashion. Four letters have standard, built-in
meanings described
below. The machine description macro @code{PRINT_OPERAND} can define
additional letters with nonstandard meanings.
+@samp{%[@var{n}]} is another way of writing operand @var{n}. This form is
+useful for operand numbers that have more than one digit.
+
+@samp{%[@var{name}:@var{n}]} is the same idea as
+@samp{%@var{letter}@var{n}}, but with a multi-character target-specific
+modifier named @var{name}. GCC parses the bracketed name and passes it to
+@code{PRINT_OPERAND} as an opaque integer code. The target can use
+@code{output_operand_modifier_name} to recover @var{name}. This form is
+useful when a single-letter modifier would be too cryptic, or when a target
+has used most of its single-letter namespace. The modifier name can include
+text substituted by the machine description reader, such as mode attributes.
+It can also contain comma-separated nonempty components; GCC records the
+full text before the colon and leaves any further interpretation to the
+target.
+
+@samp{%[@var{name}]@var{n}} is also accepted as an alternate spelling in
+machine descriptions.
+
+In user @code{asm} statements, the same modifier can be used with a symbolic
+operand name by writing @samp{%[@var{name}:@var{asmSymbolicName}]}. The
+front end resolves the symbolic name to an operand number before final
+output.
+
+For example, a target could write:
+
+@example
+(define_insn "example_extract"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operand:SI 1 "register_operand" "r"))]
+ ""
+ "extract\t%[lowpart:0], %1")
+@end example
+
+@noindent
+and then define @samp{lowpart} in its @code{PRINT_OPERAND} hook.
+
@samp{%c@var{digit}} can be used to substitute an operand that is a
constant value without the syntax that normally indicates an immediate
operand.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index
97afd0175220dd34750b69b9e1e020456ca0ca98..d22706292681e5747a3223680d8f3f9e8d26b9cd
100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -9863,6 +9863,37 @@ the @samp{%} specification that was used to request
printing of the
operand. If the specification was just @samp{%@var{digit}} then
@var{code} is 0; if the specification was @samp{%@var{ltr}
@var{digit}} then @var{code} is the ASCII code for @var{ltr}.
+If the specification used the multi-character form
+@samp{%[@var{name}:@var{digit}]}, then @var{code} is an opaque value.
+@samp{%[@var{name}]@var{digit}} is accepted as an alternate spelling.
+
+User @code{asm} statements can combine this form with symbolic operand names
+by writing @samp{%[@var{name}:@var{asmSymbolicName}]}. The front end
+resolves the symbolic operand name before final output, so the
+@code{PRINT_OPERAND} implementation still sees the same @var{code} as it
+would for @samp{%[@var{name}:@var{digit}]}.
+
+@findex output_operand_modifier_name
+Use @code{output_operand_modifier_name} to recover the text between the
+brackets. The function returns a null pointer for codes that do not name a
+multi-character modifier.
+
+@findex output_operand_modifier_user_p
+For multi-character modifiers,
+@code{output_operand_modifier_user_p} tests whether the modifier came from a
+user-facing @code{asm} statement; this allows targets to reject internal-only
+modifiers while still using them in machine description output templates.
+
+For example:
+
+@example
+const char *name = output_operand_modifier_name (code);
+if (name && strcmp (name, "lowpart") == 0)
+ @{
+ print_lowpart_operand (stream, x);
+ return;
+ @}
+@end example
@findex reg_names
If @var{x} is a register, this macro should print the register's name.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index
2113b9c679a3ef7c938d00cf93be7b0d57c2898f..43e66509341c78b8d41cd4507c0c950a10eb6acc
100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -6358,6 +6358,37 @@ the @samp{%} specification that was used to request
printing of the
operand. If the specification was just @samp{%@var{digit}} then
@var{code} is 0; if the specification was @samp{%@var{ltr}
@var{digit}} then @var{code} is the ASCII code for @var{ltr}.
+If the specification used the multi-character form
+@samp{%[@var{name}:@var{digit}]}, then @var{code} is an opaque value.
+@samp{%[@var{name}]@var{digit}} is accepted as an alternate spelling.
+
+User @code{asm} statements can combine this form with symbolic operand names
+by writing @samp{%[@var{name}:@var{asmSymbolicName}]}. The front end
+resolves the symbolic operand name before final output, so the
+@code{PRINT_OPERAND} implementation still sees the same @var{code} as it
+would for @samp{%[@var{name}:@var{digit}]}.
+
+@findex output_operand_modifier_name
+Use @code{output_operand_modifier_name} to recover the text between the
+brackets. The function returns a null pointer for codes that do not name a
+multi-character modifier.
+
+@findex output_operand_modifier_user_p
+For multi-character modifiers,
+@code{output_operand_modifier_user_p} tests whether the modifier came from a
+user-facing @code{asm} statement; this allows targets to reject internal-only
+modifiers while still using them in machine description output templates.
+
+For example:
+
+@example
+const char *name = output_operand_modifier_name (code);
+if (name && strcmp (name, "lowpart") == 0)
+ @{
+ print_lowpart_operand (stream, x);
+ return;
+ @}
+@end example
@findex reg_names
If @var{x} is a register, this macro should print the register's name.
diff --git a/gcc/final.cc b/gcc/final.cc
index
b6be9be99b7f12d69345de3c4edf99d33dfa6087..9953e1d44d9d66672b5daacc5a6200de3ba1df8c
100644
--- a/gcc/final.cc
+++ b/gcc/final.cc
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3. If not see
#include "common/common-target.h"
#include "diagnostic.h"
#include "diagnostics/file-cache.h"
+#include "selftest.h"
#include "dwarf2out.h"
@@ -3395,6 +3396,216 @@ do_assembler_dialects (const char *p, int *dialect)
}
#endif
+/* Return true if the half-open range [START, END) contains only decimal
+ digits, parsing it as an operand number into OPNUM. */
+
+static bool
+parse_operand_number (const char *start, const char *end,
+ unsigned long &opnum)
+{
+ if (start == end)
+ return false;
+
+ unsigned long result = 0;
+ for (const char *q = start; q != end; ++q)
+ {
+ if (!ISDIGIT (*q))
+ return false;
+ result = result * 10 + (*q - '0');
+ }
+ opnum = result;
+ return true;
+}
+
+/* Return true if the half-open range [START, END) is a valid
+ multi-character operand modifier. Commas separate nonempty components. */
+
+static bool
+valid_operand_modifier_p (const char *start, const char *end)
+{
+ if (start == end)
+ return false;
+
+ const char *component_start = start;
+ for (const char *q = start; q != end; ++q)
+ {
+ if (*q == ':')
+ return false;
+ if (*q == ',')
+ {
+ if (component_start == q)
+ return false;
+ component_start = q + 1;
+ }
+ }
+
+ return component_start != end;
+}
+
+struct bracketed_operand
+{
+ string_slice modifier;
+ unsigned long opnum;
+ bool has_modifier;
+};
+
+/* Parse an operand reference that starts with %[...].
+
+ The preferred forms are %[N] and %[MODIFIER:N]. For compatibility with
+ existing machine descriptions, also accept %[MODIFIER]N. */
+
+static bool
+parse_bracketed_operand (const char **p, bracketed_operand &operand)
+{
+ const char *start = *p;
+ gcc_assert (*start == '[');
+
+ const char *end = strchr (start + 1, ']');
+ if (!end)
+ return false;
+
+ const char *contents = start + 1;
+ const char *colon = nullptr;
+ for (const char *q = contents; q != end; ++q)
+ if (*q == ':')
+ {
+ colon = q;
+ break;
+ }
+
+ if (colon)
+ {
+ if (!valid_operand_modifier_p (contents, colon)
+ || !parse_operand_number (colon + 1, end, operand.opnum))
+ return false;
+ operand.modifier = string_slice (contents, colon);
+ operand.has_modifier = true;
+ *p = end + 1;
+ return true;
+ }
+
+ if (parse_operand_number (contents, end, operand.opnum))
+ {
+ operand.has_modifier = false;
+ *p = end + 1;
+ return true;
+ }
+
+ if (!valid_operand_modifier_p (contents, end))
+ return false;
+
+ const char *opnum_start = end + 1;
+ const char *opnum_end = opnum_start;
+ while (ISDIGIT (*opnum_end))
+ opnum_end++;
+ if (!parse_operand_number (opnum_start, opnum_end, operand.opnum))
+ return false;
+
+ operand.modifier = string_slice (contents, end);
+ operand.has_modifier = true;
+ *p = opnum_end;
+ return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_parse_bracketed_operand ()
+{
+ bracketed_operand operand;
+ const char *p = "[10]";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_FALSE (operand.has_modifier);
+ ASSERT_EQ (10UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[fmt:11]";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_TRUE (operand.has_modifier);
+ ASSERT_TRUE (operand.modifier == "fmt");
+ ASSERT_EQ (11UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[fmt]12";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_TRUE (operand.has_modifier);
+ ASSERT_TRUE (operand.modifier == "fmt");
+ ASSERT_EQ (12UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[gpr,zero:13]";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_TRUE (operand.has_modifier);
+ ASSERT_TRUE (operand.modifier == "gpr,zero");
+ ASSERT_EQ (13UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[fmt]";
+ ASSERT_FALSE (parse_bracketed_operand (&p, operand));
+ p = "[fmt:]";
+ ASSERT_FALSE (parse_bracketed_operand (&p, operand));
+ p = "[]0";
+ ASSERT_FALSE (parse_bracketed_operand (&p, operand));
+}
+
+void
+final_cc_tests ()
+{
+ test_parse_bracketed_operand ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
+
+static vec<const char *> output_operand_modifier_names;
+
+static const int output_operand_modifier_base = UCHAR_MAX + 1;
+
+/* Return a PRINT_OPERAND code for MODIFIER, allocating one if necessary. */
+
+static int
+output_operand_modifier_code (string_slice modifier, bool user_p)
+{
+ for (unsigned int i = 0; i < output_operand_modifier_names.length (); ++i)
+ if (modifier == output_operand_modifier_names[i])
+ return output_operand_modifier_base + i * 2 + user_p;
+
+ char *name = XNEWVEC (char, modifier.size () + 1);
+ memcpy (name, modifier.begin (), modifier.size ());
+ name[modifier.size ()] = 0;
+ output_operand_modifier_names.safe_push (name);
+ return output_operand_modifier_base + (output_operand_modifier_names.length
()
+ - 1) * 2 + user_p;
+}
+
+/* If CODE is an extended PRINT_OPERAND code, return its original %[...]
+ modifier list. Return null otherwise. */
+
+const char *
+output_operand_modifier_name (int code)
+{
+ if (code < output_operand_modifier_base)
+ return nullptr;
+
+ unsigned int index = (code - output_operand_modifier_base) / 2;
+ if (index >= output_operand_modifier_names.length ())
+ return nullptr;
+
+ return output_operand_modifier_names[index];
+}
+
+/* Return true if extended PRINT_OPERAND code CODE came from user asm. */
+
+bool
+output_operand_modifier_user_p (int code)
+{
+ gcc_checking_assert (code >= output_operand_modifier_base);
+ return (code - output_operand_modifier_base) & 1;
+}
+
/* Output text from TEMPLATE to the assembler output file,
obeying %-directions to substitute operands taken from
the vector OPERANDS.
@@ -3488,11 +3699,37 @@ output_asm_insn (const char *templ, rtx *operands)
p++;
fprintf (asm_out_file, "%d", insn_counter);
}
+ else if (*p == '[')
+ {
+ bracketed_operand operand;
+ if (!parse_bracketed_operand (&p, operand))
+ output_operand_lossage ("invalid %%[...] operand modifier");
+
+ unsigned long opnum = operand.opnum;
+ if (this_is_asm_operands && opnum >= insn_noperands)
+ {
+ /* Force the opnum in bounds to a bogus location. */
+ opnum = MAX_RECOG_OPERANDS;
+ output_operand_lossage ("operand number out of range");
+ }
+ else if (operand.has_modifier)
+ output_operand (operands[opnum],
+ output_operand_modifier_code
+ (operand.modifier, this_is_asm_operands));
+ else
+ output_operand (operands[opnum], 0);
+
+ if (!opoutput[opnum])
+ oporder[ops++] = opnum;
+ opoutput[opnum] = 1;
+
+ c = *p;
+ }
/* % followed by a letter and some digits
outputs an operand in a special way depending on the letter.
Letters `acln' are implemented directly.
Other letters are passed to `output_operand' so that
- the TARGET_PRINT_OPERAND hook can define them. */
+ the selected target operand hook can define them. */
else if (ISALPHA (*p))
{
int letter = *p++;
@@ -3522,7 +3759,7 @@ output_asm_insn (const char *templ, rtx *operands)
if (letter2 == 'c' || CONSTANT_ADDRESS_P (operands[opnum]))
output_addr_const (asm_out_file, operands[opnum]);
else
- output_operand (operands[opnum], 'c');
+ output_operand (operands[opnum], letter);
}
else if (letter == 'n')
{
@@ -3570,9 +3807,12 @@ output_asm_insn (const char *templ, rtx *operands)
}
/* % followed by punctuation: output something for that
punctuation character alone, with no operand. The
- TARGET_PRINT_OPERAND hook decides what is actually done. */
+ selected target operand hook decides what is actually done. */
else if (targetm.asm_out.print_operand_punct_valid_p ((unsigned char)
*p))
- output_operand (NULL_RTX, *p++);
+ {
+ const char *ptr = p++;
+ output_operand (NULL_RTX, *ptr);
+ }
else
output_operand_lossage ("invalid %%-code");
break;
@@ -3638,7 +3878,7 @@ mark_symbol_refs_as_used (rtx x)
by TARGET_PRINT_OPERAND. */
void
-output_operand (rtx x, int code ATTRIBUTE_UNUSED)
+output_operand (rtx x, int code)
{
if (x && GET_CODE (x) == SUBREG)
x = alter_subreg (&x, true);
diff --git a/gcc/output.h b/gcc/output.h
index
1f628ea2371aeca006d686e521a04d4f7ddb6e2d..76f30a83b5d12534dd538c1b9026189d07f988c5
100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -80,6 +80,8 @@ extern rtx alter_subreg (rtx *, bool);
/* Print an operand using machine-dependent assembler syntax. */
extern void output_operand (rtx, int);
+extern const char *output_operand_modifier_name (int);
+extern bool output_operand_modifier_user_p (int);
/* Report inconsistency between the assembler template and the operands.
In an `asm', it's the user's fault; otherwise, the compiler's fault. */
diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc
index
07eb984e918eeb56b7c832c0c87794647b47b5c6..53877889a0f5b3509004b061f8474e2c394e160b
100644
--- a/gcc/selftest-run-tests.cc
+++ b/gcc/selftest-run-tests.cc
@@ -106,11 +106,13 @@ selftest::run_tests ()
/* Higher-level tests, or for components that other selftests don't
rely on. */
fold_const_cc_tests ();
+ final_cc_tests ();
spellcheck_cc_tests ();
spellcheck_tree_cc_tests ();
tree_cfg_cc_tests ();
simple_diagnostic_path_cc_tests ();
attribs_cc_tests ();
+ stmt_cc_tests ();
path_coverage_cc_tests ();
/* This one relies on most of the above. */
diff --git a/gcc/selftest.h b/gcc/selftest.h
index
acfc4a46e49b42cb1a1dde7043f94b22bc134181..3e47ac77d1ed1a65059e2bfcd4f7f52dc654dd7c
100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -225,6 +225,7 @@ extern void digraph_cc_tests ();
extern void dumpfile_cc_tests ();
extern void et_forest_cc_tests ();
extern void fibonacci_heap_cc_tests ();
+extern void final_cc_tests ();
extern void fold_const_cc_tests ();
extern void function_tests_cc_tests ();
extern void gcc_attribute_urlifier_cc_tests ();
@@ -264,6 +265,7 @@ extern void spellcheck_cc_tests ();
extern void spellcheck_tree_cc_tests ();
extern void splay_tree_cc_tests ();
extern void sreal_cc_tests ();
+extern void stmt_cc_tests ();
extern void store_merging_cc_tests ();
extern void tree_cc_tests ();
extern void tree_cfg_cc_tests ();
diff --git a/gcc/stmt.cc b/gcc/stmt.cc
index
382a2d75c07609c0d82882ed646941a1f00d572f..dc685a4e9676b879ca4941f2189333bdce4d070c
100644
--- a/gcc/stmt.cc
+++ b/gcc/stmt.cc
@@ -57,6 +57,10 @@ along with GCC; see the file COPYING3. If not see
#include "cfgexpand.h"
#include "output.h"
+#if CHECKING_P
+#include "selftest.h"
+#endif
+
/* Functions and data structures for expanding case statements. */
@@ -87,6 +91,7 @@ public:
};
static bool check_unique_operand_names (tree, tree, tree);
+static const char *asm_operand_name_start (const char *);
static char *resolve_operand_name_1 (char *, tree, tree, tree);
/* Return the rtx-label that corresponds to a LABEL_DECL,
@@ -870,6 +875,67 @@ check_unique_operand_names (tree outputs, tree inputs,
tree labels)
return false;
}
+/* P points to a '%' in an asm template. Return the '[' for the symbolic
+ operand name that should be resolved, or null if this % code does not
+ contain a symbolic operand name.
+
+ The %[...] syntax has two uses in user asm: %[name] refers to a named
+ operand, whereas %[modifier:N] and %[modifier]N are multi-character output
+ modifiers. When both a modifier and a symbolic operand name are needed,
+ use %[modifier:name]. */
+
+static bool
+asm_operand_number_p (const char *start, const char *end)
+{
+ if (start == end)
+ return false;
+
+ for (const char *q = start; q != end; ++q)
+ if (!ISDIGIT (*q))
+ return false;
+ return true;
+}
+
+static const char *
+asm_operand_name_start (const char *p)
+{
+ gcc_checking_assert (p[0] == '%');
+
+ if (p[1] == '[')
+ {
+ const char *q = strchr (p + 2, ']');
+ if (q)
+ {
+ const char *colon = nullptr;
+ for (const char *r = p + 2; r != q; ++r)
+ if (*r == ':')
+ {
+ colon = r;
+ break;
+ }
+
+ if (colon)
+ {
+ if (asm_operand_number_p (colon + 1, q))
+ return NULL;
+ return colon + 1;
+ }
+
+ if (asm_operand_number_p (p + 2, q))
+ return NULL;
+ if (ISDIGIT (q[1]))
+ return NULL;
+ }
+ return p + 1;
+ }
+
+ if (ISALPHA (p[1])
+ && (p[2] == '[' || (ISALPHA (p[2]) && p[3] == '[')))
+ return p + 2 + (p[2] != '[');
+
+ return NULL;
+}
+
/* Resolve the names of the operands in *POUTPUTS and *PINPUTS to numbers,
and replace the name expansions in STRING and in the constraints to
those numbers. This is generally done in the front end while creating
@@ -905,10 +971,7 @@ resolve_asm_operand_names (tree string, tree outputs, tree
inputs, tree labels)
c = TREE_STRING_POINTER (string);
while ((c = strchr (c, '%')) != NULL)
{
- if (c[1] == '[')
- break;
- else if (ISALPHA (c[1])
- && (c[2] == '[' || (ISALPHA (c[2]) && c[3] == '[')))
+ if (asm_operand_name_start (c))
break;
else
{
@@ -920,26 +983,23 @@ resolve_asm_operand_names (tree string, tree outputs,
tree inputs, tree labels)
if (c)
{
/* OK, we need to make a copy so we can perform the substitutions.
- Assume that we will not need extra space--we get to remove '['
- and ']', which means we cannot have a problem until we have more
- than 999 operands. */
- buffer = xstrdup (TREE_STRING_POINTER (string));
+ Allocate some spare room for %[modifier:name], where the replacement
+ number can be longer than NAME. */
+ size_t length = strlen (TREE_STRING_POINTER (string));
+ buffer = XNEWVEC (char, length * 2 + 1);
+ memcpy (buffer, TREE_STRING_POINTER (string), length + 1);
p = buffer + (c - TREE_STRING_POINTER (string));
while ((p = strchr (p, '%')) != NULL)
{
- if (p[1] == '[')
- p += 1;
- else if (ISALPHA (p[1]) && p[2] == '[')
- p += 2;
- else if (ISALPHA (p[1]) && ISALPHA (p[2]) && p[3] == '[')
- p += 3;
- else
+ const char *name_start = asm_operand_name_start (p);
+ if (!name_start)
{
p += 1 + (p[1] == '%');
continue;
}
+ p += name_start - p;
p = resolve_operand_name_1 (p, outputs, inputs, labels);
}
@@ -958,24 +1018,40 @@ resolve_asm_operand_names (tree string, tree outputs,
tree inputs, tree labels)
static char *
resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
{
- char *q;
+ char *q, *name_start, *replace_start, *replace_end;
int op, op_inout;
tree t;
- /* Collect the operand name. */
- q = strchr (++p, ']');
+ /* Collect the operand name. P either points to the '[' in %[name],
+ or to the first character of NAME in %[modifier:name]. */
+ if (*p == '[')
+ {
+ replace_start = p;
+ name_start = p + 1;
+ q = strchr (name_start, ']');
+ replace_end = q ? q + 1 : nullptr;
+ }
+ else
+ {
+ replace_start = p;
+ name_start = p;
+ q = strchr (name_start, ']');
+ replace_end = q;
+ }
+
if (!q)
{
error ("missing close brace for named operand");
- return strchr (p, '\0');
+ return strchr (name_start, '\0');
}
+ char end_marker = *q;
*q = '\0';
/* Resolve the name to a number. */
for (op_inout = op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
- if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ if (name && strcmp (TREE_STRING_POINTER (name), name_start) == 0)
goto found;
tree constraint = TREE_VALUE (TREE_PURPOSE (t));
if (constraint && strchr (TREE_STRING_POINTER (constraint), '+') != NULL)
@@ -984,35 +1060,89 @@ resolve_operand_name_1 (char *p, tree outputs, tree
inputs, tree labels)
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
- if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ if (name && strcmp (TREE_STRING_POINTER (name), name_start) == 0)
goto found;
}
op += op_inout;
for (t = labels; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (t);
- if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ if (name && strcmp (TREE_STRING_POINTER (name), name_start) == 0)
goto found;
}
- error ("undefined named operand %qs", identifier_to_locale (p));
+ error ("undefined named operand %qs", identifier_to_locale (name_start));
op = 0;
found:
- /* Replace the name with the number. Unfortunately, not all libraries
- get the return value of sprintf correct, so search for the end of the
- generated string by hand. */
- sprintf (--p, "%d", op);
- p = strchr (p, '\0');
+ *q = end_marker;
+ char number[HOST_BITS_PER_INT * 3 / 10 + 3];
+ sprintf (number, "%d", op);
+ size_t number_len = strlen (number);
+ memmove (replace_start + number_len, replace_end,
+ strlen (replace_end) + 1);
+ memcpy (replace_start, number, number_len);
+
+ return replace_start + number_len;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static tree
+build_asm_operand (const char *name, const char *constraint)
+{
+ tree name_tree = name ? build_string (strlen (name), name) : NULL_TREE;
+ tree constraint_tree = build_string (strlen (constraint), constraint);
+ return tree_cons (tree_cons (name_tree, constraint_tree, NULL_TREE),
+ integer_zero_node, NULL_TREE);
+}
- /* Verify the no extra buffer space assumption. */
- gcc_assert (p <= q);
+static tree
+build_asm_label (const char *name)
+{
+ return tree_cons (build_string (strlen (name), name), integer_zero_node,
+ NULL_TREE);
+}
- /* Shift the rest of the buffer down to fill the gap. */
- memmove (p, q + 1, strlen (q + 1) + 1);
+static void
+test_resolve_asm_operand_names ()
+{
+ tree outputs = build_asm_operand ("out", "=r");
+ tree inputs = build_asm_operand ("in", "r");
+ tree labels = build_asm_label ("lab");
+
+ const char *templ = "%[out] %w[out] %cc[in] %l[lab]";
+ tree string = build_string (strlen (templ), templ);
+ tree result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%0 %w0 %cc1 %l2", TREE_STRING_POINTER (result));
+
+ templ = "%[fmt]0 %[fmt]1";
+ string = build_string (strlen (templ), templ);
+ result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%[fmt]0 %[fmt]1", TREE_STRING_POINTER (result));
+
+ templ = "%[10] %[fmt:0] %[fmt:1]";
+ string = build_string (strlen (templ), templ);
+ result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%[10] %[fmt:0] %[fmt:1]", TREE_STRING_POINTER (result));
+
+ templ = "%[fmt:out] %[fmt:in]";
+ string = build_string (strlen (templ), templ);
+ result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%[fmt:0] %[fmt:1]", TREE_STRING_POINTER (result));
+}
- return p;
+void
+stmt_cc_tests ()
+{
+ test_resolve_asm_operand_names ();
}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
/* Generate RTL to return directly from the current function.
--
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 88213d25a899ed5bff25af732220c54d2359b492..1e6f5ddb0a815101f6b6c57bc8f610766e035895 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12057,7 +12057,14 @@ instead of simply @samp{%2}). @ref{GenericOperandmodifiers,
Generic Operand modifiers} lists the modifiers that are available
on all targets. Other modifiers are hardware dependent.
For example, the list of supported modifiers for x86 is found at
-@ref{x86Operandmodifiers,x86 Operand modifiers}.
+@ref{x86Operandmodifiers,x86 Operand modifiers}. Targets can also provide
+multi-character modifiers, written as @samp{%[@var{modifier}:2]}.
+
+If the operand has a symbolic name, write
+@samp{%[@var{modifier}:@var{asmSymbolicName}]}. The older spelling
+@samp{%[@var{modifier}]2} is also accepted for numbered operands.
+@samp{%[@var{name}]} without a modifier separator continues to mean the
+operand named @var{name}.
If the C code that follows the @code{asm} makes no use of any of the output
operands, use @code{volatile} for the @code{asm} statement to prevent the
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 80761b0a8a62d3e22f2c52de372f876396ab15c7..21c9238bf7f77e1a49f7a7379a5ca9f1e92c7914 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -524,6 +524,42 @@ alternate fashion. Four letters have standard, built-in meanings described
below. The machine description macro @code{PRINT_OPERAND} can define
additional letters with nonstandard meanings.
+@samp{%[@var{n}]} is another way of writing operand @var{n}. This form is
+useful for operand numbers that have more than one digit.
+
+@samp{%[@var{name}:@var{n}]} is the same idea as
+@samp{%@var{letter}@var{n}}, but with a multi-character target-specific
+modifier named @var{name}. GCC parses the bracketed name and passes it to
+@code{PRINT_OPERAND} as an opaque integer code. The target can use
+@code{output_operand_modifier_name} to recover @var{name}. This form is
+useful when a single-letter modifier would be too cryptic, or when a target
+has used most of its single-letter namespace. The modifier name can include
+text substituted by the machine description reader, such as mode attributes.
+It can also contain comma-separated nonempty components; GCC records the
+full text before the colon and leaves any further interpretation to the
+target.
+
+@samp{%[@var{name}]@var{n}} is also accepted as an alternate spelling in
+machine descriptions.
+
+In user @code{asm} statements, the same modifier can be used with a symbolic
+operand name by writing @samp{%[@var{name}:@var{asmSymbolicName}]}. The
+front end resolves the symbolic name to an operand number before final
+output.
+
+For example, a target could write:
+
+@example
+(define_insn "example_extract"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operand:SI 1 "register_operand" "r"))]
+ ""
+ "extract\t%[lowpart:0], %1")
+@end example
+
+@noindent
+and then define @samp{lowpart} in its @code{PRINT_OPERAND} hook.
+
@samp{%c@var{digit}} can be used to substitute an operand that is a
constant value without the syntax that normally indicates an immediate
operand.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 97afd0175220dd34750b69b9e1e020456ca0ca98..d22706292681e5747a3223680d8f3f9e8d26b9cd 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -9863,6 +9863,37 @@ the @samp{%} specification that was used to request printing of the
operand. If the specification was just @samp{%@var{digit}} then
@var{code} is 0; if the specification was @samp{%@var{ltr}
@var{digit}} then @var{code} is the ASCII code for @var{ltr}.
+If the specification used the multi-character form
+@samp{%[@var{name}:@var{digit}]}, then @var{code} is an opaque value.
+@samp{%[@var{name}]@var{digit}} is accepted as an alternate spelling.
+
+User @code{asm} statements can combine this form with symbolic operand names
+by writing @samp{%[@var{name}:@var{asmSymbolicName}]}. The front end
+resolves the symbolic operand name before final output, so the
+@code{PRINT_OPERAND} implementation still sees the same @var{code} as it
+would for @samp{%[@var{name}:@var{digit}]}.
+
+@findex output_operand_modifier_name
+Use @code{output_operand_modifier_name} to recover the text between the
+brackets. The function returns a null pointer for codes that do not name a
+multi-character modifier.
+
+@findex output_operand_modifier_user_p
+For multi-character modifiers,
+@code{output_operand_modifier_user_p} tests whether the modifier came from a
+user-facing @code{asm} statement; this allows targets to reject internal-only
+modifiers while still using them in machine description output templates.
+
+For example:
+
+@example
+const char *name = output_operand_modifier_name (code);
+if (name && strcmp (name, "lowpart") == 0)
+ @{
+ print_lowpart_operand (stream, x);
+ return;
+ @}
+@end example
@findex reg_names
If @var{x} is a register, this macro should print the register's name.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 2113b9c679a3ef7c938d00cf93be7b0d57c2898f..43e66509341c78b8d41cd4507c0c950a10eb6acc 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -6358,6 +6358,37 @@ the @samp{%} specification that was used to request printing of the
operand. If the specification was just @samp{%@var{digit}} then
@var{code} is 0; if the specification was @samp{%@var{ltr}
@var{digit}} then @var{code} is the ASCII code for @var{ltr}.
+If the specification used the multi-character form
+@samp{%[@var{name}:@var{digit}]}, then @var{code} is an opaque value.
+@samp{%[@var{name}]@var{digit}} is accepted as an alternate spelling.
+
+User @code{asm} statements can combine this form with symbolic operand names
+by writing @samp{%[@var{name}:@var{asmSymbolicName}]}. The front end
+resolves the symbolic operand name before final output, so the
+@code{PRINT_OPERAND} implementation still sees the same @var{code} as it
+would for @samp{%[@var{name}:@var{digit}]}.
+
+@findex output_operand_modifier_name
+Use @code{output_operand_modifier_name} to recover the text between the
+brackets. The function returns a null pointer for codes that do not name a
+multi-character modifier.
+
+@findex output_operand_modifier_user_p
+For multi-character modifiers,
+@code{output_operand_modifier_user_p} tests whether the modifier came from a
+user-facing @code{asm} statement; this allows targets to reject internal-only
+modifiers while still using them in machine description output templates.
+
+For example:
+
+@example
+const char *name = output_operand_modifier_name (code);
+if (name && strcmp (name, "lowpart") == 0)
+ @{
+ print_lowpart_operand (stream, x);
+ return;
+ @}
+@end example
@findex reg_names
If @var{x} is a register, this macro should print the register's name.
diff --git a/gcc/final.cc b/gcc/final.cc
index b6be9be99b7f12d69345de3c4edf99d33dfa6087..9953e1d44d9d66672b5daacc5a6200de3ba1df8c 100644
--- a/gcc/final.cc
+++ b/gcc/final.cc
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3. If not see
#include "common/common-target.h"
#include "diagnostic.h"
#include "diagnostics/file-cache.h"
+#include "selftest.h"
#include "dwarf2out.h"
@@ -3395,6 +3396,216 @@ do_assembler_dialects (const char *p, int *dialect)
}
#endif
+/* Return true if the half-open range [START, END) contains only decimal
+ digits, parsing it as an operand number into OPNUM. */
+
+static bool
+parse_operand_number (const char *start, const char *end,
+ unsigned long &opnum)
+{
+ if (start == end)
+ return false;
+
+ unsigned long result = 0;
+ for (const char *q = start; q != end; ++q)
+ {
+ if (!ISDIGIT (*q))
+ return false;
+ result = result * 10 + (*q - '0');
+ }
+ opnum = result;
+ return true;
+}
+
+/* Return true if the half-open range [START, END) is a valid
+ multi-character operand modifier. Commas separate nonempty components. */
+
+static bool
+valid_operand_modifier_p (const char *start, const char *end)
+{
+ if (start == end)
+ return false;
+
+ const char *component_start = start;
+ for (const char *q = start; q != end; ++q)
+ {
+ if (*q == ':')
+ return false;
+ if (*q == ',')
+ {
+ if (component_start == q)
+ return false;
+ component_start = q + 1;
+ }
+ }
+
+ return component_start != end;
+}
+
+struct bracketed_operand
+{
+ string_slice modifier;
+ unsigned long opnum;
+ bool has_modifier;
+};
+
+/* Parse an operand reference that starts with %[...].
+
+ The preferred forms are %[N] and %[MODIFIER:N]. For compatibility with
+ existing machine descriptions, also accept %[MODIFIER]N. */
+
+static bool
+parse_bracketed_operand (const char **p, bracketed_operand &operand)
+{
+ const char *start = *p;
+ gcc_assert (*start == '[');
+
+ const char *end = strchr (start + 1, ']');
+ if (!end)
+ return false;
+
+ const char *contents = start + 1;
+ const char *colon = nullptr;
+ for (const char *q = contents; q != end; ++q)
+ if (*q == ':')
+ {
+ colon = q;
+ break;
+ }
+
+ if (colon)
+ {
+ if (!valid_operand_modifier_p (contents, colon)
+ || !parse_operand_number (colon + 1, end, operand.opnum))
+ return false;
+ operand.modifier = string_slice (contents, colon);
+ operand.has_modifier = true;
+ *p = end + 1;
+ return true;
+ }
+
+ if (parse_operand_number (contents, end, operand.opnum))
+ {
+ operand.has_modifier = false;
+ *p = end + 1;
+ return true;
+ }
+
+ if (!valid_operand_modifier_p (contents, end))
+ return false;
+
+ const char *opnum_start = end + 1;
+ const char *opnum_end = opnum_start;
+ while (ISDIGIT (*opnum_end))
+ opnum_end++;
+ if (!parse_operand_number (opnum_start, opnum_end, operand.opnum))
+ return false;
+
+ operand.modifier = string_slice (contents, end);
+ operand.has_modifier = true;
+ *p = opnum_end;
+ return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_parse_bracketed_operand ()
+{
+ bracketed_operand operand;
+ const char *p = "[10]";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_FALSE (operand.has_modifier);
+ ASSERT_EQ (10UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[fmt:11]";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_TRUE (operand.has_modifier);
+ ASSERT_TRUE (operand.modifier == "fmt");
+ ASSERT_EQ (11UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[fmt]12";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_TRUE (operand.has_modifier);
+ ASSERT_TRUE (operand.modifier == "fmt");
+ ASSERT_EQ (12UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[gpr,zero:13]";
+ ASSERT_TRUE (parse_bracketed_operand (&p, operand));
+ ASSERT_TRUE (operand.has_modifier);
+ ASSERT_TRUE (operand.modifier == "gpr,zero");
+ ASSERT_EQ (13UL, operand.opnum);
+ ASSERT_EQ ('\0', *p);
+
+ p = "[fmt]";
+ ASSERT_FALSE (parse_bracketed_operand (&p, operand));
+ p = "[fmt:]";
+ ASSERT_FALSE (parse_bracketed_operand (&p, operand));
+ p = "[]0";
+ ASSERT_FALSE (parse_bracketed_operand (&p, operand));
+}
+
+void
+final_cc_tests ()
+{
+ test_parse_bracketed_operand ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
+
+static vec<const char *> output_operand_modifier_names;
+
+static const int output_operand_modifier_base = UCHAR_MAX + 1;
+
+/* Return a PRINT_OPERAND code for MODIFIER, allocating one if necessary. */
+
+static int
+output_operand_modifier_code (string_slice modifier, bool user_p)
+{
+ for (unsigned int i = 0; i < output_operand_modifier_names.length (); ++i)
+ if (modifier == output_operand_modifier_names[i])
+ return output_operand_modifier_base + i * 2 + user_p;
+
+ char *name = XNEWVEC (char, modifier.size () + 1);
+ memcpy (name, modifier.begin (), modifier.size ());
+ name[modifier.size ()] = 0;
+ output_operand_modifier_names.safe_push (name);
+ return output_operand_modifier_base + (output_operand_modifier_names.length ()
+ - 1) * 2 + user_p;
+}
+
+/* If CODE is an extended PRINT_OPERAND code, return its original %[...]
+ modifier list. Return null otherwise. */
+
+const char *
+output_operand_modifier_name (int code)
+{
+ if (code < output_operand_modifier_base)
+ return nullptr;
+
+ unsigned int index = (code - output_operand_modifier_base) / 2;
+ if (index >= output_operand_modifier_names.length ())
+ return nullptr;
+
+ return output_operand_modifier_names[index];
+}
+
+/* Return true if extended PRINT_OPERAND code CODE came from user asm. */
+
+bool
+output_operand_modifier_user_p (int code)
+{
+ gcc_checking_assert (code >= output_operand_modifier_base);
+ return (code - output_operand_modifier_base) & 1;
+}
+
/* Output text from TEMPLATE to the assembler output file,
obeying %-directions to substitute operands taken from
the vector OPERANDS.
@@ -3488,11 +3699,37 @@ output_asm_insn (const char *templ, rtx *operands)
p++;
fprintf (asm_out_file, "%d", insn_counter);
}
+ else if (*p == '[')
+ {
+ bracketed_operand operand;
+ if (!parse_bracketed_operand (&p, operand))
+ output_operand_lossage ("invalid %%[...] operand modifier");
+
+ unsigned long opnum = operand.opnum;
+ if (this_is_asm_operands && opnum >= insn_noperands)
+ {
+ /* Force the opnum in bounds to a bogus location. */
+ opnum = MAX_RECOG_OPERANDS;
+ output_operand_lossage ("operand number out of range");
+ }
+ else if (operand.has_modifier)
+ output_operand (operands[opnum],
+ output_operand_modifier_code
+ (operand.modifier, this_is_asm_operands));
+ else
+ output_operand (operands[opnum], 0);
+
+ if (!opoutput[opnum])
+ oporder[ops++] = opnum;
+ opoutput[opnum] = 1;
+
+ c = *p;
+ }
/* % followed by a letter and some digits
outputs an operand in a special way depending on the letter.
Letters `acln' are implemented directly.
Other letters are passed to `output_operand' so that
- the TARGET_PRINT_OPERAND hook can define them. */
+ the selected target operand hook can define them. */
else if (ISALPHA (*p))
{
int letter = *p++;
@@ -3522,7 +3759,7 @@ output_asm_insn (const char *templ, rtx *operands)
if (letter2 == 'c' || CONSTANT_ADDRESS_P (operands[opnum]))
output_addr_const (asm_out_file, operands[opnum]);
else
- output_operand (operands[opnum], 'c');
+ output_operand (operands[opnum], letter);
}
else if (letter == 'n')
{
@@ -3570,9 +3807,12 @@ output_asm_insn (const char *templ, rtx *operands)
}
/* % followed by punctuation: output something for that
punctuation character alone, with no operand. The
- TARGET_PRINT_OPERAND hook decides what is actually done. */
+ selected target operand hook decides what is actually done. */
else if (targetm.asm_out.print_operand_punct_valid_p ((unsigned char) *p))
- output_operand (NULL_RTX, *p++);
+ {
+ const char *ptr = p++;
+ output_operand (NULL_RTX, *ptr);
+ }
else
output_operand_lossage ("invalid %%-code");
break;
@@ -3638,7 +3878,7 @@ mark_symbol_refs_as_used (rtx x)
by TARGET_PRINT_OPERAND. */
void
-output_operand (rtx x, int code ATTRIBUTE_UNUSED)
+output_operand (rtx x, int code)
{
if (x && GET_CODE (x) == SUBREG)
x = alter_subreg (&x, true);
diff --git a/gcc/output.h b/gcc/output.h
index 1f628ea2371aeca006d686e521a04d4f7ddb6e2d..76f30a83b5d12534dd538c1b9026189d07f988c5 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -80,6 +80,8 @@ extern rtx alter_subreg (rtx *, bool);
/* Print an operand using machine-dependent assembler syntax. */
extern void output_operand (rtx, int);
+extern const char *output_operand_modifier_name (int);
+extern bool output_operand_modifier_user_p (int);
/* Report inconsistency between the assembler template and the operands.
In an `asm', it's the user's fault; otherwise, the compiler's fault. */
diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc
index 07eb984e918eeb56b7c832c0c87794647b47b5c6..53877889a0f5b3509004b061f8474e2c394e160b 100644
--- a/gcc/selftest-run-tests.cc
+++ b/gcc/selftest-run-tests.cc
@@ -106,11 +106,13 @@ selftest::run_tests ()
/* Higher-level tests, or for components that other selftests don't
rely on. */
fold_const_cc_tests ();
+ final_cc_tests ();
spellcheck_cc_tests ();
spellcheck_tree_cc_tests ();
tree_cfg_cc_tests ();
simple_diagnostic_path_cc_tests ();
attribs_cc_tests ();
+ stmt_cc_tests ();
path_coverage_cc_tests ();
/* This one relies on most of the above. */
diff --git a/gcc/selftest.h b/gcc/selftest.h
index acfc4a46e49b42cb1a1dde7043f94b22bc134181..3e47ac77d1ed1a65059e2bfcd4f7f52dc654dd7c 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -225,6 +225,7 @@ extern void digraph_cc_tests ();
extern void dumpfile_cc_tests ();
extern void et_forest_cc_tests ();
extern void fibonacci_heap_cc_tests ();
+extern void final_cc_tests ();
extern void fold_const_cc_tests ();
extern void function_tests_cc_tests ();
extern void gcc_attribute_urlifier_cc_tests ();
@@ -264,6 +265,7 @@ extern void spellcheck_cc_tests ();
extern void spellcheck_tree_cc_tests ();
extern void splay_tree_cc_tests ();
extern void sreal_cc_tests ();
+extern void stmt_cc_tests ();
extern void store_merging_cc_tests ();
extern void tree_cc_tests ();
extern void tree_cfg_cc_tests ();
diff --git a/gcc/stmt.cc b/gcc/stmt.cc
index 382a2d75c07609c0d82882ed646941a1f00d572f..dc685a4e9676b879ca4941f2189333bdce4d070c 100644
--- a/gcc/stmt.cc
+++ b/gcc/stmt.cc
@@ -57,6 +57,10 @@ along with GCC; see the file COPYING3. If not see
#include "cfgexpand.h"
#include "output.h"
+#if CHECKING_P
+#include "selftest.h"
+#endif
+
/* Functions and data structures for expanding case statements. */
@@ -87,6 +91,7 @@ public:
};
static bool check_unique_operand_names (tree, tree, tree);
+static const char *asm_operand_name_start (const char *);
static char *resolve_operand_name_1 (char *, tree, tree, tree);
/* Return the rtx-label that corresponds to a LABEL_DECL,
@@ -870,6 +875,67 @@ check_unique_operand_names (tree outputs, tree inputs, tree labels)
return false;
}
+/* P points to a '%' in an asm template. Return the '[' for the symbolic
+ operand name that should be resolved, or null if this % code does not
+ contain a symbolic operand name.
+
+ The %[...] syntax has two uses in user asm: %[name] refers to a named
+ operand, whereas %[modifier:N] and %[modifier]N are multi-character output
+ modifiers. When both a modifier and a symbolic operand name are needed,
+ use %[modifier:name]. */
+
+static bool
+asm_operand_number_p (const char *start, const char *end)
+{
+ if (start == end)
+ return false;
+
+ for (const char *q = start; q != end; ++q)
+ if (!ISDIGIT (*q))
+ return false;
+ return true;
+}
+
+static const char *
+asm_operand_name_start (const char *p)
+{
+ gcc_checking_assert (p[0] == '%');
+
+ if (p[1] == '[')
+ {
+ const char *q = strchr (p + 2, ']');
+ if (q)
+ {
+ const char *colon = nullptr;
+ for (const char *r = p + 2; r != q; ++r)
+ if (*r == ':')
+ {
+ colon = r;
+ break;
+ }
+
+ if (colon)
+ {
+ if (asm_operand_number_p (colon + 1, q))
+ return NULL;
+ return colon + 1;
+ }
+
+ if (asm_operand_number_p (p + 2, q))
+ return NULL;
+ if (ISDIGIT (q[1]))
+ return NULL;
+ }
+ return p + 1;
+ }
+
+ if (ISALPHA (p[1])
+ && (p[2] == '[' || (ISALPHA (p[2]) && p[3] == '[')))
+ return p + 2 + (p[2] != '[');
+
+ return NULL;
+}
+
/* Resolve the names of the operands in *POUTPUTS and *PINPUTS to numbers,
and replace the name expansions in STRING and in the constraints to
those numbers. This is generally done in the front end while creating
@@ -905,10 +971,7 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
c = TREE_STRING_POINTER (string);
while ((c = strchr (c, '%')) != NULL)
{
- if (c[1] == '[')
- break;
- else if (ISALPHA (c[1])
- && (c[2] == '[' || (ISALPHA (c[2]) && c[3] == '[')))
+ if (asm_operand_name_start (c))
break;
else
{
@@ -920,26 +983,23 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
if (c)
{
/* OK, we need to make a copy so we can perform the substitutions.
- Assume that we will not need extra space--we get to remove '['
- and ']', which means we cannot have a problem until we have more
- than 999 operands. */
- buffer = xstrdup (TREE_STRING_POINTER (string));
+ Allocate some spare room for %[modifier:name], where the replacement
+ number can be longer than NAME. */
+ size_t length = strlen (TREE_STRING_POINTER (string));
+ buffer = XNEWVEC (char, length * 2 + 1);
+ memcpy (buffer, TREE_STRING_POINTER (string), length + 1);
p = buffer + (c - TREE_STRING_POINTER (string));
while ((p = strchr (p, '%')) != NULL)
{
- if (p[1] == '[')
- p += 1;
- else if (ISALPHA (p[1]) && p[2] == '[')
- p += 2;
- else if (ISALPHA (p[1]) && ISALPHA (p[2]) && p[3] == '[')
- p += 3;
- else
+ const char *name_start = asm_operand_name_start (p);
+ if (!name_start)
{
p += 1 + (p[1] == '%');
continue;
}
+ p += name_start - p;
p = resolve_operand_name_1 (p, outputs, inputs, labels);
}
@@ -958,24 +1018,40 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
static char *
resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
{
- char *q;
+ char *q, *name_start, *replace_start, *replace_end;
int op, op_inout;
tree t;
- /* Collect the operand name. */
- q = strchr (++p, ']');
+ /* Collect the operand name. P either points to the '[' in %[name],
+ or to the first character of NAME in %[modifier:name]. */
+ if (*p == '[')
+ {
+ replace_start = p;
+ name_start = p + 1;
+ q = strchr (name_start, ']');
+ replace_end = q ? q + 1 : nullptr;
+ }
+ else
+ {
+ replace_start = p;
+ name_start = p;
+ q = strchr (name_start, ']');
+ replace_end = q;
+ }
+
if (!q)
{
error ("missing close brace for named operand");
- return strchr (p, '\0');
+ return strchr (name_start, '\0');
}
+ char end_marker = *q;
*q = '\0';
/* Resolve the name to a number. */
for (op_inout = op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
- if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ if (name && strcmp (TREE_STRING_POINTER (name), name_start) == 0)
goto found;
tree constraint = TREE_VALUE (TREE_PURPOSE (t));
if (constraint && strchr (TREE_STRING_POINTER (constraint), '+') != NULL)
@@ -984,35 +1060,89 @@ resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
- if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ if (name && strcmp (TREE_STRING_POINTER (name), name_start) == 0)
goto found;
}
op += op_inout;
for (t = labels; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (t);
- if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ if (name && strcmp (TREE_STRING_POINTER (name), name_start) == 0)
goto found;
}
- error ("undefined named operand %qs", identifier_to_locale (p));
+ error ("undefined named operand %qs", identifier_to_locale (name_start));
op = 0;
found:
- /* Replace the name with the number. Unfortunately, not all libraries
- get the return value of sprintf correct, so search for the end of the
- generated string by hand. */
- sprintf (--p, "%d", op);
- p = strchr (p, '\0');
+ *q = end_marker;
+ char number[HOST_BITS_PER_INT * 3 / 10 + 3];
+ sprintf (number, "%d", op);
+ size_t number_len = strlen (number);
+ memmove (replace_start + number_len, replace_end,
+ strlen (replace_end) + 1);
+ memcpy (replace_start, number, number_len);
+
+ return replace_start + number_len;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static tree
+build_asm_operand (const char *name, const char *constraint)
+{
+ tree name_tree = name ? build_string (strlen (name), name) : NULL_TREE;
+ tree constraint_tree = build_string (strlen (constraint), constraint);
+ return tree_cons (tree_cons (name_tree, constraint_tree, NULL_TREE),
+ integer_zero_node, NULL_TREE);
+}
- /* Verify the no extra buffer space assumption. */
- gcc_assert (p <= q);
+static tree
+build_asm_label (const char *name)
+{
+ return tree_cons (build_string (strlen (name), name), integer_zero_node,
+ NULL_TREE);
+}
- /* Shift the rest of the buffer down to fill the gap. */
- memmove (p, q + 1, strlen (q + 1) + 1);
+static void
+test_resolve_asm_operand_names ()
+{
+ tree outputs = build_asm_operand ("out", "=r");
+ tree inputs = build_asm_operand ("in", "r");
+ tree labels = build_asm_label ("lab");
+
+ const char *templ = "%[out] %w[out] %cc[in] %l[lab]";
+ tree string = build_string (strlen (templ), templ);
+ tree result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%0 %w0 %cc1 %l2", TREE_STRING_POINTER (result));
+
+ templ = "%[fmt]0 %[fmt]1";
+ string = build_string (strlen (templ), templ);
+ result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%[fmt]0 %[fmt]1", TREE_STRING_POINTER (result));
+
+ templ = "%[10] %[fmt:0] %[fmt:1]";
+ string = build_string (strlen (templ), templ);
+ result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%[10] %[fmt:0] %[fmt:1]", TREE_STRING_POINTER (result));
+
+ templ = "%[fmt:out] %[fmt:in]";
+ string = build_string (strlen (templ), templ);
+ result = resolve_asm_operand_names (string, outputs, inputs, labels);
+ ASSERT_STREQ ("%[fmt:0] %[fmt:1]", TREE_STRING_POINTER (result));
+}
- return p;
+void
+stmt_cc_tests ()
+{
+ test_resolve_asm_operand_names ();
}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
/* Generate RTL to return directly from the current function.