Tamar Christina <[email protected]> writes:
> 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
Thanks for doing this. It mostly looks good to me FWIW, but some
comments below:
> 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]}.
> +
Very minor, but: I think it'd read better if this was part of the following
paragraph instead, so that it's more obvious that new paragraph is referring
specifically to the new syntax.
> +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/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)
It'd be good to export this and reuse it instead of asm_operand_number_p.
Maybe that would mean moving it too, although I'm not sure where.
> +{
> + 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;
> +}
> [...]
> +#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 */
I think self-tests are conventially at the end of the file. Putting it
in the middle would artificially restrict where the code under test can go.
> @@ -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. */
Is this comment change left over from an earlier version? I wasn't sure
what the "selected target operand hook" was referring to.
> 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;
Same question for the changes in the two hunks above. I wasn't sure why
they were necessary.
> [...]
> @@ -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;
> +}
> +
Missing function comment.
> +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;
I was going to suggest that we disallow the trailing digit syntax in
asms (and perhaps then in general), since it would reinterpret what
"%[name]0" means. However, it seems that what "%[name]0" currently
means isn't useful: "%[name]" is replaced with "%<opnum>" and so we get
a double-digit operand number. For example:
void f() {
asm volatile ("foo %[number]0" :: [x] "i" (1), [number] "r" (100));
}
is treated as equivalent to "foo %10" and so gives an out-of-range error.
Still, I'm somewhat sceptical of the "%[modifier]opno" syntax in general.
It's a new syntax with no existing users and I don't see why it would
ever be preferred over the more explicit %[modifier:opno]".
> + }
> + return p + 1;
> + }
> +
> + if (ISALPHA (p[1])
> + && (p[2] == '[' || (ISALPHA (p[2]) && p[3] == '[')))
> + return p + 2 + (p[2] != '[');
Heh. I hadn't realised that we were so loose about where the modifier ended.
But clearly that's pre-existing and the patch is doing the right thing by
preserving it.
> +
> + 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);
Not sure about the * 2. More below.
> + 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;
Isn't that just "p = name_start;"? If so, we could use name_start directly
in the statement below instead.
> p = resolve_operand_name_1 (p, outputs, inputs, labels);
Since this patch is rewriting this code anyway, it would be good to make
it work regardless of MAX_RECOG_OPERANDS (which won't exist once the
wasm backend goes in).
So I think at this point we should just abandon the preallocation approach
and use an obstack instead. That is: iterate over the original input
string, growing the obstack with output characters as we go. That
would avoid the need to calculate a length in advance, would cope
with any number of digits, and would remove the complex shuffle in
resolve_operand_name_1.
Thanks,
Richard
> }
>
> @@ -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.