Ping.
On Mon, Jun 10, 2024 at 07:19:19AM +0200, Stefan Schulze Frielinghaus wrote: > Ping. > > On Fri, May 24, 2024 at 11:13:12AM +0200, Stefan Schulze Frielinghaus wrote: > > This implements hard register constraints for inline asm. A hard register > > constraint is of the form {regname} where regname is any valid register. > > This > > basically renders register asm superfluous. For example, the snippet > > > > int test (int x, int y) > > { > > register int r4 asm ("r4") = x; > > register int r5 asm ("r5") = y; > > unsigned int copy = y; > > asm ("foo %0,%1,%2" : "+d" (r4) : "d" (r5), "d" (copy)); > > return r4; > > } > > > > could be rewritten into > > > > int test (int x, int y) > > { > > asm ("foo %0,%1,%2" : "+{r4}" (x) : "{r5}" (y), "d" (y)); > > return x; > > } > > > > As a side-effect this also solves the problem of call-clobbered registers. > > That being said, I was wondering whether we could utilize this feature in > > order > > to get rid of local register asm automatically? For example, converting > > > > // Result will be in r2 on s390 > > extern int bar (void); > > > > void test (void) > > { > > register int x asm ("r2") = 42; > > bar (); > > asm ("foo %0\n" :: "r" (x)); > > } > > > > into > > > > void test (void) > > { > > int x = 42; > > bar (); > > asm ("foo %0\n" :: "{r2}" (x)); > > } > > > > in order to get rid of the limitation of call-clobbered registers which may > > lead to subtle bugs---especially if you think of non-obvious calls e.g. > > introduced by sanitizer/tracer/whatever. Since such a transformation has > > the > > potential to break existing code do you see any edge cases where this might > > be > > problematic or even show stoppers? Currently, even > > > > int test (void) > > { > > register int x asm ("r2") = 42; > > register int y asm ("r2") = 24; > > asm ("foo %0,%1\n" :: "r" (x), "r" (y)); > > } > > > > is allowed which seems error prone to me. Thus, if 100% backwards > > compatibility would be required, then automatically converting every > > register > > asm to the new mechanism isn't viable. Still quite a lot could be > > transformed. > > Any thoughts? > > > > Currently I allow multiple alternatives as demonstrated by > > gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c. However, since a hard > > register > > constraint is pretty specific I could also think of erroring out in case of > > alternatives. Are there any real use cases out there for multiple > > alternatives where one would like to use hard register constraints? > > > > With the current implementation we have a "user visible change" in the sense > > that for > > > > void test (void) > > { > > register int x asm ("r2") = 42; > > register int y asm ("r2") = 24; > > asm ("foo %0,%1\n" : "=r" (x), "=r" (y)); > > } > > > > we do not get the error > > > > "invalid hard register usage between output operands" > > > > anymore but rather > > > > "multiple outputs to hard register: %r2" > > > > This is due to the error handling in gimplify_asm_expr (). Speaking of > > errors, > > I also error out earlier as before which means that e.g. in pr87600-2.c only > > the first error is reported and processing is stopped afterwards which means > > the subsequent tests fail. > > > > I've been skimming through all targets and it looks to me as if none is > > using > > curly brackets for their constraints. Of course, I may have missed > > something. > > > > Cheers, > > Stefan > > > > PS: Current state for Clang: https://reviews.llvm.org/D105142 > > > > --- > > gcc/cfgexpand.cc | 42 ------- > > gcc/genpreds.cc | 4 +- > > gcc/gimplify.cc | 115 +++++++++++++++++- > > gcc/lra-constraints.cc | 17 +++ > > gcc/recog.cc | 14 ++- > > gcc/stmt.cc | 102 +++++++++++++++- > > gcc/stmt.h | 10 +- > > .../gcc.target/s390/asm-hard-reg-1.c | 103 ++++++++++++++++ > > .../gcc.target/s390/asm-hard-reg-2.c | 29 +++++ > > .../gcc.target/s390/asm-hard-reg-3.c | 24 ++++ > > gcc/testsuite/lib/scanasm.exp | 4 + > > 11 files changed, 407 insertions(+), 57 deletions(-) > > create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c > > create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c > > create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c > > > > diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc > > index 557cb28733b..47f71a2e803 100644 > > --- a/gcc/cfgexpand.cc > > +++ b/gcc/cfgexpand.cc > > @@ -2955,44 +2955,6 @@ expand_asm_loc (tree string, int vol, location_t > > locus) > > emit_insn (body); > > } > > > > -/* Return the number of times character C occurs in string S. */ > > -static int > > -n_occurrences (int c, const char *s) > > -{ > > - int n = 0; > > - while (*s) > > - n += (*s++ == c); > > - return n; > > -} > > - > > -/* A subroutine of expand_asm_operands. Check that all operands have > > - the same number of alternatives. Return true if so. */ > > - > > -static bool > > -check_operand_nalternatives (const vec<const char *> &constraints) > > -{ > > - unsigned len = constraints.length(); > > - if (len > 0) > > - { > > - int nalternatives = n_occurrences (',', constraints[0]); > > - > > - if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES) > > - { > > - error ("too many alternatives in %<asm%>"); > > - return false; > > - } > > - > > - for (unsigned i = 1; i < len; ++i) > > - if (n_occurrences (',', constraints[i]) != nalternatives) > > - { > > - error ("operand constraints for %<asm%> differ " > > - "in number of alternatives"); > > - return false; > > - } > > - } > > - return true; > > -} > > - > > /* Check for overlap between registers marked in CLOBBERED_REGS and > > anything inappropriate in T. Emit error and return the register > > variable definition for error, NULL_TREE for ok. */ > > @@ -3158,10 +3120,6 @@ expand_asm_stmt (gasm *stmt) > > = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); > > } > > > > - /* ??? Diagnose during gimplification? */ > > - if (! check_operand_nalternatives (constraints)) > > - return; > > - > > /* Count the number of meaningful clobbered registers, ignoring what > > we would ignore later. */ > > auto_vec<rtx> clobber_rvec; > > diff --git a/gcc/genpreds.cc b/gcc/genpreds.cc > > index 55d149e8a40..f0d59cb0846 100644 > > --- a/gcc/genpreds.cc > > +++ b/gcc/genpreds.cc > > @@ -1148,7 +1148,7 @@ write_insn_constraint_len (void) > > unsigned int i; > > > > puts ("static inline size_t\n" > > - "insn_constraint_len (char fc, const char *str ATTRIBUTE_UNUSED)\n" > > + "insn_constraint_len (char fc, const char *str)\n" > > "{\n" > > " switch (fc)\n" > > " {"); > > @@ -1181,6 +1181,8 @@ write_insn_constraint_len (void) > > > > puts (" default: break;\n" > > " }\n" > > + " if (str[0] == '{')\n" > > + " return ((const char *)rawmemchr (str + 1, '}') - str) + 1;\n" > > " return 1;\n" > > "}\n"); > > } > > diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc > > index b0ed58ed0f9..b4b16e75023 100644 > > --- a/gcc/gimplify.cc > > +++ b/gcc/gimplify.cc > > @@ -70,6 +70,9 @@ along with GCC; see the file COPYING3. If not see > > #include "omp-offload.h" > > #include "context.h" > > #include "tree-nested.h" > > +#include "insn-config.h" > > +#include "recog.h" > > +#include "output.h" > > > > /* Identifier for a basic condition, mapping it to other basic conditions > > of > > its Boolean expression. Basic conditions given the same uid (in the > > same > > @@ -6952,6 +6955,42 @@ gimplify_addr_expr (tree *expr_p, gimple_seq *pre_p, > > gimple_seq *post_p) > > return ret; > > } > > > > +/* Return the number of times character C occurs in string S. */ > > + > > +static int > > +num_occurrences (int c, const char *s) > > +{ > > + int n = 0; > > + while (*s) > > + n += (*s++ == c); > > + return n; > > +} > > + > > +/* A subroutine of gimplify_asm_expr. Check that all operands have > > + the same number of alternatives. Return -1 if this is violated. > > Otherwise > > + return the number of alternatives. */ > > + > > +static int > > +num_alternatives (const_tree link) > > +{ > > + if (link == nullptr) > > + return 0; > > + > > + const char *constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE > > (link))); > > + int num = num_occurrences (',', constraint); > > + > > + if (num + 1 > MAX_RECOG_ALTERNATIVES) > > + return -1; > > + > > + for (link = TREE_CHAIN (link); link; link = TREE_CHAIN (link)) > > + { > > + constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); > > + if (num_occurrences (',', constraint) != num) > > + return -1; > > + } > > + return num + 1; > > +} > > + > > /* Gimplify the operands of an ASM_EXPR. Input operands should be a gimple > > value; output operands should be a gimple lvalue. */ > > > > @@ -6982,6 +7021,35 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, > > gimple_seq *post_p) > > clobbers = NULL; > > labels = NULL; > > > > + int num_alternatives_out = num_alternatives (ASM_OUTPUTS (expr)); > > + int num_alternatives_in = num_alternatives (ASM_INPUTS (expr)); > > + if (num_alternatives_out == -1 || num_alternatives_in == -1 > > + || (num_alternatives_out > 0 && num_alternatives_in > 0 > > + && num_alternatives_out != num_alternatives_in)) > > + { > > + error ("operand constraints for %<asm%> differ " > > + "in number of alternatives"); > > + return GS_ERROR; > > + } > > + int num_alternatives = MAX (num_alternatives_out, num_alternatives_in); > > + > > + /* Regarding hard register constraints ensure that each hard register is > > used > > + at most once over all inputs/outputs and each alternative. Keep > > track in > > + hardregs[0] which hard register is used via an asm register over all > > + inputs/outputs. hardregs[i] for i >= 2 describes which hard > > registers are > > + used for alternative i-2 over all inputs/outputs. hardregs[1] is a > > + reduction of all alternatives, i.e., hardregs[1] |= hardregs[i] for i > > >= 2 > > + and describes whether a hard register is used in any alternative. > > This is > > + just a shortcut instead of recomputing the union over all > > alternatives; > > + possibly multiple times. */ > > + auto_vec<HARD_REG_SET> hardregs (num_alternatives + 2); > > + for (int i = 0; i < num_alternatives + 2; ++i) > > + { > > + HARD_REG_SET hregset; > > + CLEAR_HARD_REG_SET (hregset); > > + hardregs.quick_push (hregset); > > + } > > + > > ret = GS_ALL_DONE; > > link_next = NULL_TREE; > > for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = link_next) > > @@ -6998,8 +7066,8 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, > > gimple_seq *post_p) > > if (constraint_len == 0) > > continue; > > > > - ok = parse_output_constraint (&constraint, i, 0, 0, > > - &allows_mem, &allows_reg, &is_inout); > > + ok = parse_output_constraint (&constraint, i, 0, 0, &allows_mem, > > + &allows_reg, &is_inout, &hardregs); > > if (!ok) > > { > > ret = GS_ERROR; > > @@ -7062,6 +7130,24 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, > > gimple_seq *post_p) > > TREE_VALUE (link) = tem; > > tret = GS_OK; > > } > > + if (VAR_P (op) && DECL_HARD_REGISTER (op)) > > + { > > + tree id = DECL_ASSEMBLER_NAME (op); > > + const char *asmspec = IDENTIFIER_POINTER (id) + 1; > > + int hardreg = decode_reg_name (asmspec); > > + if (hardreg >= 0) > > + { > > + if (TEST_HARD_REG_BIT (hardregs[0], hardreg) > > + || TEST_HARD_REG_BIT (hardregs[1], hardreg)) > > + { > > + error ("multiple outputs to hard register: %s", > > + reg_names[hardreg]); > > + return GS_ERROR; > > + } > > + else > > + SET_HARD_REG_BIT (hardregs[0], hardreg); > > + } > > + } > > } > > > > vec_safe_push (outputs, link); > > @@ -7161,13 +7247,16 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, > > gimple_seq *post_p) > > } > > } > > > > + for (unsigned int i = 0; i < hardregs.length (); ++i) > > + CLEAR_HARD_REG_SET (hardregs[i]); > > + > > link_next = NULL_TREE; > > for (link = ASM_INPUTS (expr); link; ++i, link = link_next) > > { > > link_next = TREE_CHAIN (link); > > constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); > > - parse_input_constraint (&constraint, 0, 0, noutputs, 0, > > - oconstraints, &allows_mem, &allows_reg); > > + parse_input_constraint (&constraint, 0, 0, noutputs, 0, oconstraints, > > + &allows_mem, &allows_reg, &hardregs); > > > > /* If we can't make copies, we can only accept memory. */ > > tree intype = TREE_TYPE (TREE_VALUE (link)); > > @@ -7241,6 +7330,24 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, > > gimple_seq *post_p) > > is_gimple_asm_val, fb_rvalue); > > if (tret == GS_ERROR) > > ret = tret; > > + tree inputv = TREE_VALUE (link); > > + if (VAR_P (inputv) && DECL_HARD_REGISTER (inputv)) > > + { > > + tree id = DECL_ASSEMBLER_NAME (inputv); > > + const char *asmspec = IDENTIFIER_POINTER (id) + 1; > > + int hardreg = decode_reg_name (asmspec); > > + if (hardreg >= 0) > > + { > > + if (TEST_HARD_REG_BIT (hardregs[1], hardreg)) > > + { > > + error ("multiple inputs to hard register: %s", > > + reg_names[hardreg]); > > + return GS_ERROR; > > + } > > + else > > + SET_HARD_REG_BIT (hardregs[0], hardreg); > > + } > > + } > > } > > > > TREE_CHAIN (link) = NULL_TREE; > > diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc > > index e945a4da451..d81753eefaa 100644 > > --- a/gcc/lra-constraints.cc > > +++ b/gcc/lra-constraints.cc > > @@ -114,6 +114,7 @@ > > #include "target.h" > > #include "rtl.h" > > #include "tree.h" > > +#include "stmt.h" > > #include "predict.h" > > #include "df.h" > > #include "memmodel.h" > > @@ -2165,6 +2166,7 @@ process_alt_operands (int only_alternative) > > bool costly_p; > > enum reg_class cl; > > const HARD_REG_SET *cl_filter; > > + HARD_REG_SET hregset; > > > > /* Calculate some data common for all alternatives to speed up the > > function. */ > > @@ -2536,6 +2538,21 @@ process_alt_operands (int only_alternative) > > cl_filter = nullptr; > > goto reg; > > > > + case '{': > > + { > > + /* Currently this form of constraint is only allowed in > > + asm statements which are verified during gimplify, > > + i.e., regno >= 0 holds for those. genoutput fails on > > + it. For future proofness assert it. */ > > + int regno = parse_constraint_regname (p); > > + gcc_assert (regno >= 0); > > + cl = REGNO_REG_CLASS (regno); > > + CLEAR_HARD_REG_SET (hregset); > > + SET_HARD_REG_BIT (hregset, regno); > > + cl_filter = &hregset; > > + goto reg; > > + } > > + > > default: > > cn = lookup_constraint (p); > > switch (get_constraint_type (cn)) > > diff --git a/gcc/recog.cc b/gcc/recog.cc > > index a6799e3f5e6..8a474cfb8a7 100644 > > --- a/gcc/recog.cc > > +++ b/gcc/recog.cc > > @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see > > #include "target.h" > > #include "rtl.h" > > #include "tree.h" > > +#include "stmt.h" > > #include "cfghooks.h" > > #include "df.h" > > #include "memmodel.h" > > @@ -2296,10 +2297,11 @@ asm_operand_ok (rtx op, const char *constraint, > > const char **constraints) > > switch (get_constraint_type (cn)) > > { > > case CT_REGISTER: > > - if (!result > > - && reg_class_for_constraint (cn) != NO_REGS > > - && GET_MODE (op) != BLKmode > > - && register_operand (op, VOIDmode)) > > + if ((!result > > + && reg_class_for_constraint (cn) != NO_REGS > > + && GET_MODE (op) != BLKmode > > + && register_operand (op, VOIDmode)) > > + || constraint[0] == '{') > > result = 1; > > break; > > > > @@ -3231,6 +3233,10 @@ constrain_operands (int strict, alternative_mask > > alternatives) > > win = true; > > break; > > > > + case '{': > > + win = true; > > + break; > > + > > default: > > { > > enum constraint_num cn = lookup_constraint (p); > > diff --git a/gcc/stmt.cc b/gcc/stmt.cc > > index ae1527f0a19..1f20b09f90e 100644 > > --- a/gcc/stmt.cc > > +++ b/gcc/stmt.cc > > @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see > > #include "emit-rtl.h" > > #include "pretty-print.h" > > #include "diagnostic-core.h" > > +#include "output.h" > > > > #include "fold-const.h" > > #include "varasm.h" > > @@ -174,6 +175,32 @@ expand_label (tree label) > > maybe_set_first_label_num (label_r); > > } > > > > +/* Parse a hard register constraint and return its number or -1 in case of > > an > > + error. BEGIN should point to a string of the form "{regname}". For the > > + sake of simplicity assume that a register name is not longer than 31 > > + characters, if not error out. */ > > + > > +int > > +parse_constraint_regname (const char *begin) > > +{ > > + if (*begin != '{') > > + return -1; > > + ++begin; > > + const char *end = begin; > > + while (*end != '}' && *end != '\0') > > + ++end; > > + if (*end != '}' || end == begin) > > + return -1; > > + ptrdiff_t len = end - begin; > > + if (len >= 31) > > + return -1; > > + char regname[32]; > > + memcpy (regname, begin, len); > > + regname[len] = '\0'; > > + int regno = decode_reg_name (regname); > > + return regno; > > +} > > + > > /* 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, > > @@ -190,7 +217,8 @@ expand_label (tree label) > > bool > > parse_output_constraint (const char **constraint_p, int operand_num, > > int ninputs, int noutputs, bool *allows_mem, > > - bool *allows_reg, bool *is_inout) > > + bool *allows_reg, bool *is_inout, > > + vec<HARD_REG_SET> *hardregs) > > { > > const char *constraint = *constraint_p; > > const char *p; > > @@ -244,6 +272,8 @@ parse_output_constraint (const char **constraint_p, int > > operand_num, > > constraint = *constraint_p; > > } > > > > + unsigned int alternative = 2; > > + > > /* Loop through the constraint string. */ > > for (p = constraint + 1; *p; ) > > { > > @@ -268,7 +298,11 @@ parse_output_constraint (const char **constraint_p, > > int operand_num, > > case 'E': case 'F': case 'G': case 'H': > > case 's': case 'i': case 'n': > > case 'I': case 'J': case 'K': case 'L': case 'M': > > - case 'N': case 'O': case 'P': case ',': > > + case 'N': case 'O': case 'P': > > + break; > > + > > + case ',': > > + ++alternative; > > break; > > > > case '0': case '1': case '2': case '3': case '4': > > @@ -289,6 +323,33 @@ parse_output_constraint (const char **constraint_p, > > int operand_num, > > *allows_mem = true; > > break; > > > > + case '{': > > + { > > + int regno = parse_constraint_regname (p); > > + if (regno < 0) > > + { > > + error ("invalid output constraint: %s", p); > > + return false; > > + } > > + if (hardregs) > > + { > > + if (TEST_HARD_REG_BIT ((*hardregs)[0], regno) > > + || TEST_HARD_REG_BIT ((*hardregs)[alternative], regno)) > > + { > > + error ("multiple outputs to hard register: %s", > > + reg_names[regno]); > > + return false; > > + } > > + else > > + { > > + SET_HARD_REG_BIT ((*hardregs)[1], regno); > > + SET_HARD_REG_BIT ((*hardregs)[alternative], regno); > > + } > > + } > > + *allows_reg = true; > > + break; > > + } > > + > > default: > > if (!ISALPHA (*p)) > > break; > > @@ -317,7 +378,8 @@ bool > > parse_input_constraint (const char **constraint_p, int input_num, > > int ninputs, int noutputs, int ninout, > > const char * const * constraints, > > - bool *allows_mem, bool *allows_reg) > > + bool *allows_mem, bool *allows_reg, > > + vec<HARD_REG_SET> *hardregs) > > { > > const char *constraint = *constraint_p; > > const char *orig_constraint = constraint; > > @@ -332,6 +394,8 @@ parse_input_constraint (const char **constraint_p, int > > input_num, > > > > /* Make sure constraint has neither `=', `+', nor '&'. */ > > > > + unsigned int alternative = 2; > > + > > for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j)) > > switch (constraint[j]) > > { > > @@ -358,7 +422,11 @@ parse_input_constraint (const char **constraint_p, int > > input_num, > > case 'E': case 'F': case 'G': case 'H': > > case 's': case 'i': case 'n': > > case 'I': case 'J': case 'K': case 'L': case 'M': > > - case 'N': case 'O': case 'P': case ',': > > + case 'N': case 'O': case 'P': > > + break; > > + > > + case ',': > > + ++alternative; > > break; > > > > /* Whether or not a numeric constraint allows a register is > > @@ -408,6 +476,32 @@ parse_input_constraint (const char **constraint_p, int > > input_num, > > *allows_mem = true; > > break; > > > > + case '{': > > + { > > + int regno = parse_constraint_regname (constraint + j); > > + if (regno < 0) > > + { > > + error ("invalid input constraint: %s", constraint + j); > > + return false; > > + } > > + if (hardregs) > > + { > > + if (TEST_HARD_REG_BIT ((*hardregs)[0], regno) > > + || TEST_HARD_REG_BIT ((*hardregs)[alternative], regno)) > > + { > > + error ("multiple inputs to hard register: %s", > > + reg_names[regno]); > > + } > > + else > > + { > > + SET_HARD_REG_BIT ((*hardregs)[1], regno); > > + SET_HARD_REG_BIT ((*hardregs)[alternative], regno); > > + } > > + } > > + *allows_reg = true; > > + break; > > + } > > + > > default: > > if (! ISALPHA (constraint[j])) > > { > > diff --git a/gcc/stmt.h b/gcc/stmt.h > > index a2caae7121b..05889ff3798 100644 > > --- a/gcc/stmt.h > > +++ b/gcc/stmt.h > > @@ -20,11 +20,17 @@ along with GCC; see the file COPYING3. If not see > > #ifndef GCC_STMT_H > > #define GCC_STMT_H > > > > +#include "target.h" > > +#include "hard-reg-set.h" > > + > > extern void expand_label (tree); > > extern bool parse_output_constraint (const char **, int, int, int, > > - bool *, bool *, bool *); > > + bool *, bool *, bool *, > > + vec<HARD_REG_SET> * = nullptr); > > extern bool parse_input_constraint (const char **, int, int, int, int, > > - const char * const *, bool *, bool *); > > + const char * const *, bool *, bool *, > > + vec<HARD_REG_SET> * = nullptr); > > +extern int parse_constraint_regname (const char *); > > extern tree resolve_asm_operand_names (tree, tree, tree, tree); > > #ifdef HARD_CONST > > /* Silly ifdef to avoid having all includers depend on hard-reg-set.h. */ > > diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c > > b/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c > > new file mode 100644 > > index 00000000000..53895d98663 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c > > @@ -0,0 +1,103 @@ > > +/* { dg-do compile { target { lp64 } } } */ > > +/* { dg-options "-O2 -march=z13" } */ > > +/* { dg-final { check-function-bodies "**" "" "" } } */ > > + > > +/* > > +** test_in_1: > > +** foo %r2 > > +** br %r14 > > +*/ > > + > > +int > > +test_in_1 (int x) > > +{ > > + asm ("foo %0" :: "{r2}" (x)); > > + return x; > > +} > > + > > +/* > > +** test_in_2: > > +** lgr (%r[0-9]+),%r2 > > +** lhi %r2,42 > > +** foo %r2 > > +** lgr %r2,\1 > > +** br %r14 > > +*/ > > + > > +int > > +test_in_2 (int x) > > +{ > > + asm ("foo %0" :: "{r2}" (42)); > > + return x; > > +} > > + > > +/* > > +** test_in_3: > > +** stmg %r12,%r15,96\(%r15\) > > +** lay %r15,-160\(%r15\) > > +** lgr (%r[0-9]+),%r2 > > +** ahi %r2,1 > > +** lgfr %r2,%r2 > > +** brasl %r14,foo@PLT > > +** lr %r3,%r2 > > +** lr %r2,\1 > > +** foo %r3,%r2 > > +** lgr %r2,\1 > > +** lmg %r12,%r15,256\(%r15\) > > +** br %r14 > > +*/ > > + > > +extern int foo (int); > > + > > +int > > +test_in_3 (int x) > > +{ > > + asm ("foo %0,%1\n" :: "{r3}" (foo (x + 1)), "{r2}" (x)); > > + return x; > > +} > > + > > +/* > > +** test_out_1: > > +** foo %r3 > > +** lgfr %r2,%r3 > > +** br %r14 > > +*/ > > + > > +int > > +test_out_1 (void) > > +{ > > + int x; > > + asm ("foo %0" : "={r3}" (x)); > > + return x; > > +} > > + > > +/* > > +** test_out_2: > > +** lgr (%r[0-9]+),%r2 > > +** foo %r2 > > +** ark (%r[0-9]+),\1,%r2 > > +** lgfr %r2,\2 > > +** br %r14 > > +*/ > > + > > +int > > +test_out_2 (int x) > > +{ > > + int y; > > + asm ("foo %0" : "={r2}" (y)); > > + return x + y; > > +} > > + > > +/* > > +** test_inout_1: > > +** foo %r2 > > +** lgfr %r2,%r2 > > +** br %r14 > > +*/ > > + > > +int > > +test_inout_1 (int x) > > +{ > > + asm ("foo %0" : "+{r2}" (x)); > > + return x; > > +} > > diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c > > b/gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c > > new file mode 100644 > > index 00000000000..9f3c221b937 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c > > @@ -0,0 +1,29 @@ > > +/* { dg-do compile } */ > > +/* { dg-options "-O2" } */ > > +/* { dg-final { check-function-bodies "**" "" "" } } */ > > + > > +/* > > +** test_1: > > +** lr %r5,%r2 > > +** foo %r5,%r3 > > +** br %r14 > > +*/ > > + > > +void > > +test_1 (int x, int *y) > > +{ > > + asm ("foo %0,%1" :: "m{r4},{r5}" (x), "m,r" (y)); > > +} > > + > > +/* > > +** test_2: > > +** lr %r4,%r2 > > +** foo %r4,0\(%r3\) > > +** br %r14 > > +*/ > > + > > +void > > +test_2 (int x, int *y) > > +{ > > + asm ("foo %0,%1" :: "m{r4},{r5}" (x), "m,r" (*y)); > > +} > > diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c > > b/gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c > > new file mode 100644 > > index 00000000000..0edcdd3cfde > > --- /dev/null > > +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c > > @@ -0,0 +1,24 @@ > > +/* { dg-do compile } */ > > +/* { dg-options "-O2" } */ > > + > > +void > > +test (void) > > +{ > > + int x, y; > > + register int r4 asm ("r4") = 0; > > + > > + asm ("" :: "{}" (42)); /* { dg-error "invalid input constraint: \{\}" } > > */ > > + asm ("" :: "{r4" (42)); /* { dg-error "invalid input constraint: \{r4" } > > */ > > + asm ("" :: "{r17}" (42)); /* { dg-error "invalid input constraint: > > \{r17\}" } */ > > + > > + asm ("" :: "r" (r4), "{r4}" (42)); /* { dg-error "multiple inputs to > > hard register: %r4" } */ > > + asm ("" :: "{r4}" (42), "r" (r4)); /* { dg-error "multiple inputs to > > hard register: %r4" } */ > > + asm ("" :: "{r4}" (42), "{r4}" (42)); /* { dg-error "multiple inputs to > > hard register: %r4" } */ > > + asm ("" :: "{r2},{r4}" (42), "{r4},{r3}" (42)); > > + asm ("" :: "{r2},{r4}" (42), "{r3},{r4}" (42)); /* { dg-error "multiple > > inputs to hard register: %r4" } */ > > + asm ("" :: "{r4},{r2}" (42), "{r4},{r3}" (42)); /* { dg-error "multiple > > inputs to hard register: %r4" } */ > > + asm ("" :: "{r3}{r4}" (42), "{r4}" (42)); /* { dg-error "multiple inputs > > to hard register: %r4" } */ > > + asm ("" : "+{r4}" (x), "={r4}" (y)); /* { dg-error "multiple outputs to > > hard register: %r4" } */ > > + asm ("" : "={r4}" (y) : "{r4}" (42), "0" (42)); /* { dg-error "multiple > > inputs to hard register: %r4" } */ > > + asm ("" : "+{r4}" (x) : "{r4}" (y)); /* { dg-error "multiple inputs to > > hard register: %r4" } */ > > +} > > diff --git a/gcc/testsuite/lib/scanasm.exp b/gcc/testsuite/lib/scanasm.exp > > index 6cf9997240d..d09372096a5 100644 > > --- a/gcc/testsuite/lib/scanasm.exp > > +++ b/gcc/testsuite/lib/scanasm.exp > > @@ -896,6 +896,10 @@ proc configure_check-function-bodies { config } { > > set up_config(fluff) {^\s*(?://)} > > } elseif { [istarget *-*-darwin*] } { > > set up_config(fluff) {^\s*(?:\.|//|@)|^L[0-9ABCESV]} > > + } elseif { [istarget s390*-*-*] } { > > + # Additionally to the defaults skip lines beginning with a # resulting > > + # from inline asm. > > + set up_config(fluff) {^\s*(?:\.|//|@|$|#)} > > } else { > > # Skip lines beginning with labels ('.L[...]:') or other directives > > # ('.align', '.cfi_startproc', '.quad [...]', '.text', etc.), '//' or > > -- > > 2.45.1 > >