This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Generate switch statements in genpreds.c


Whilst getting up to speed on recog's constraint handling while
investigating the regression PR21299, I noticed that we were
generating slightly inefficient code in GCC's new genpreds.c
generator program.

For example, on x86 the arith_or_logical_operator predicate is
currently synthesized as:

  return (GET_CODE (op) == PLUS || GET_CODE (op) == MULT
          || GET_CODE (op) == AND || GET_CODE (op) == IOR
          || GET_CODE (op) == XOR || GET_CODE (op) == SMIN
          || GET_CODE (op) == SMAX || GET_CODE (op) == UMIN
          || GET_CODE (op) == UMAX || GET_CODE (op) == COMPARE
          || GET_CODE (op) == MINUS || GET_CODE (op) == DIV
          || GET_CODE (op) == MOD || GET_CODE (op) == UDIV
          || GET_CODE (op) == UMOD || GET_CODE (op) == ASHIFT
          || GET_CODE (op) == ROTATE || GET_CODE (op) == ASHIFTRT
          || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATERT)
         && ((mode == VOIDmode || GET_MODE (op) == mode));

This is effectively a minor compile-time regression as the same
predicate in gcc-3.4 was hand-written as:

  return ((mode == VOIDmode || GET_MODE (op) == mode)
          && (GET_RTX_CLASS (GET_CODE (op)) == 'c'
              || GET_RTX_CLASS (GET_CODE (op)) == '2'));

The patch below addresses some of the performance loss (on all
platforms) by enhancing genpreds.c to write switch statements
when testing the not-uncommon case of checking against a list of
RTX codes.  With this change, arith_or_logical_operator is now
written as:

  switch (GET_CODE (op))
    {
    case PLUS:
    case MULT:
    case AND:
    case IOR:
    case XOR:
    case SMIN:
    case SMAX:
    case UMIN:
    case UMAX:
    case COMPARE:
    case MINUS:
    case DIV:
    case MOD:
    case UDIV:
    case UMOD:
    case ASHIFT:
    case ROTATE:
    case ASHIFTRT:
    case LSHIFTRT:
    case ROTATERT:
      break;
    default:
      return false;
    }
  return (mode == VOIDmode || GET_MODE (op) == mode);

This allows the middle-end more flexibility in the implementation
strategy used to match the list of RTX codes, and should always be
at least as efficient as a sequential run of comparisons.  We only
use switches for lists of more that one RTX code, and use the
existing "return <expr>" mechanism for testing a single code, or
when the MATCH_CODE is buried deep inside a complex Boolean
expression.

I've found similar code improvements in the rs6000's insn-preds.c.

One potential benefit/side-effect/fallout of this change, is that
the compilation of insn-preds.c will diagnose incorrect/suspicous
machine descriptions with duplicate rtx codes in their match_code
lists.


The following patch has been tested on i686-pc-linux-gnu, all
default languages including Ada, and powerpc-apple-darwin7.9.0,
all default languages, with a full "make check" and regression
tested with a top-level "make -k check" with no new failures.

Ok for mainline?  Alas there are no official maintainers of the
generator programs, so this falls to the GWP folks.  Thanks in
advance.


2006-06-27  Roger Sayle  <roger@eyesopen.com>

	* genpreds.c (generate_switch_p): New function.
	(add_mode_tests): Push the new mode test down inside an AND expr
	if this allows the switch-suitable MATCH_CODE to be near the root.
	(write_match_code_switch): New function to write a MATCH_CODE as
	a switch statement.
	(write_predicate_stmts): New function to write a predicate RTX
	expression as a sequence of statements.
	(write_one_predicate_function): Use write_predicate_stmts.
	(write_tm_constrs_h): Likewise.


Index: genpreds.c
===================================================================
*** genpreds.c	(revision 114815)
--- genpreds.c	(working copy)
*************** mark_mode_tests (rtx exp)
*** 316,321 ****
--- 316,330 ----
      }
  }

+ /* Determine whether the expression EXP is a MATCH_CODE that should
+    be written as a switch statement.  */
+ static bool
+ generate_switch_p (rtx exp)
+ {
+   return GET_CODE (exp) == MATCH_CODE
+ 	 && strchr (XSTR (exp, 0), ',');
+ }
+
  /* Given a predicate, work out where in its RTL expression to add
     tests for proper modes.  Special predicates do not get any such
     tests.  We try to avoid adding tests when we don't have to; in
*************** add_mode_tests (struct pred_data *p)
*** 361,366 ****
--- 370,384 ----

        switch (GET_CODE (subexp))
  	{
+ 	case AND:
+ 	  /* The switch code generation in write_predicate_stmts prefers
+ 	     rtx code tests to be at the top of the expression tree.  So
+ 	     push this AND down into the second operand of an exisiting
+ 	     AND expression.  */
+ 	  if (generate_switch_p (XEXP (subexp, 0)))
+ 	    pos = &XEXP (subexp, 1);
+ 	  goto break_loop;
+
  	case IOR:
  	  {
  	    int test0 = NO_MODE_TEST (XEXP (subexp, 0));
*************** write_predicate_expr (rtx exp)
*** 520,525 ****
--- 538,635 ----
      }
  }

+ /* Write the MATCH_CODE expression EXP as a switch statement.  */
+
+ static void
+ write_match_code_switch (rtx exp)
+ {
+   const char *codes = (const char *) XEXP (exp, 0);
+   const char *path = (const char *) XEXP (exp, 1);
+   const char *code;
+
+   fputs ("  switch (GET_CODE (", stdout);
+   write_extract_subexp (path);
+   fputs ("))\n    {\n", stdout);
+
+   while ((code = scan_comma_elt (&codes)) != 0)
+     {
+       fputs ("    case ", stdout);
+       while (code < codes)
+ 	{
+ 	  putchar (TOUPPER (*code));
+ 	  code++;
+ 	}
+       fputs(":\n", stdout);
+     }
+ }
+
+ /* Given a predictate expression EXP, write out a sequence of stmts
+    to evaluate it.  This is similar to write_predicate_expr but can
+    generate efficient switch statements.  */
+
+ static void
+ write_predicate_stmts (rtx exp)
+ {
+   switch (GET_CODE (exp))
+     {
+     case MATCH_CODE:
+       if (generate_switch_p (exp))
+ 	{
+ 	  write_match_code_switch (exp);
+ 	  puts ("      return true;\n"
+ 		"    default:\n"
+ 		"      break;\n"
+ 		"    }\n"
+ 		"  return false;");
+ 	  return;
+ 	}
+       break;
+
+     case AND:
+       if (generate_switch_p (XEXP (exp, 0)))
+ 	{
+ 	  write_match_code_switch (XEXP (exp, 0));
+ 	  puts ("      break;\n"
+ 		"    default:\n"
+ 		"      return false;\n"
+ 		"    }");
+ 	  exp = XEXP (exp, 1);
+ 	}
+       break;
+
+     case IOR:
+       if (generate_switch_p (XEXP (exp, 0)))
+ 	{
+ 	  write_match_code_switch (XEXP (exp, 0));
+ 	  puts ("      return true;\n"
+ 		"    default:\n"
+ 		"      break;\n"
+ 		"    }");
+ 	  exp = XEXP (exp, 1);
+ 	}
+
+     case NOT:
+       if (generate_switch_p (XEXP (exp, 0)))
+ 	{
+ 	  write_match_code_switch (XEXP (exp, 0));
+ 	  puts ("      return false;\n"
+ 		"    default:\n"
+ 		"      break;\n"
+ 		"    }\n"
+ 		"  return true;");
+ 	  return;
+ 	}
+       break;
+
+     default:
+       break;
+     }
+
+   fputs("  return ",stdout);
+   write_predicate_expr (exp);
+   fputs(";\n", stdout);
+ }
+
  /* Given a predicate, write out a complete C function to compute it.  */
  static void
  write_one_predicate_function (struct pred_data *p)
*************** write_one_predicate_function (struct pre
*** 532,542 ****

    /* A normal predicate can legitimately not look at enum machine_mode
       if it accepts only CONST_INTs and/or CONST_DOUBLEs.  */
!   printf ("int\n%s (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)\n"
! 	  "{\n  return ",
  	  p->name);
!   write_predicate_expr (p->exp);
!   fputs (";\n}\n\n", stdout);
  }

  /* Constraints fall into two categories: register constraints
--- 642,651 ----

    /* A normal predicate can legitimately not look at enum machine_mode
       if it accepts only CONST_INTs and/or CONST_DOUBLEs.  */
!   printf ("int\n%s (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)\n{\n",
  	  p->name);
!   write_predicate_stmts (p->exp);
!   fputs ("}\n\n", stdout);
  }

  /* Constraints fall into two categories: register constraints
*************** write_tm_constrs_h (void)
*** 995,1004 ****
  	if (needs_rval)
  	  puts ("  if (GET_CODE (op) == CONST_DOUBLE && mode != VOIDmode)"
  		"    rval = CONST_DOUBLE_REAL_VALUE (op);");
!
! 	fputs ("  return ", stdout);
! 	write_predicate_expr (c->exp);
! 	fputs (";\n}\n", stdout);
        }
    puts ("#endif /* tm-constrs.h */");
  }
--- 1104,1112 ----
  	if (needs_rval)
  	  puts ("  if (GET_CODE (op) == CONST_DOUBLE && mode != VOIDmode)"
  		"    rval = CONST_DOUBLE_REAL_VALUE (op);");
!
! 	write_predicate_stmts (c->exp);
! 	fputs ("}\n", stdout);
        }
    puts ("#endif /* tm-constrs.h */");
  }


Roger
--


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]