[PATCH] Hard register asm constraint

Stefan Schulze Frielinghaus stefansf@linux.ibm.com
Mon Jun 10 05:19:18 GMT 2024


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
> 


More information about the Gcc-patches mailing list