public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: Building of generated parser files
@ 1997-08-22 10:48 Niklas Hallqvist
  1997-08-22 13:28 ` Some Haifa scheduler bugs Bernd Schmidt
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Niklas Hallqvist @ 1997-08-22 10:48 UTC (permalink / raw)
  To: egcs

Ok I dived into doing the stuff I proposed some hours ago.  To me it
seems like a way to go generally in GNU sw.  I enclose a patch below
that shows the ideas.  I only handle the --with-makeinfo switch as
this is more of a proof of concept than anything else.  Similar
patches can be made for --with-bison, --with-texi2dvi, etc.

Comments?

Niklas

Index: configure.in
===================================================================
RCS file: /l/cvs/egcs/configure.in,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 configure.in
--- configure.in	1997/08/22 08:53:13	1.1.1.1
+++ configure.in	1997/08/22 09:52:09
@@ -44,6 +44,12 @@
 gas=yes,
 gas=no)
 
+# With GNU makeinfo
+AC_ARG_WITH(makeinfo,
+[  --with-makeinfo         arrange to work with GNU makeinfo.],
+makeinfo=yes,
+makeinfo=no)
+
 # With stabs
 AC_ARG_WITH(stabs,
 [  --with-stabs            arrange to use stabs instead of host debug format.],
@@ -3034,6 +3040,16 @@
     done
 fi
 
+# Customize depending on if we want info files generated or not.
+if [[ x$makeinfo = xyes ]]
+then
+    info_target=info
+    geninfodir=$srcdir
+else
+    info_targer=
+    geninfodir=.
+fi
+
 # Process the language and host/target makefile fragments.
 ${CONFIG_SHELL-/bin/sh} $srcdir/configure.frag $srcdir "$subdirs" "$dep_host_xmake_file" "$dep_tmake_file"
 
@@ -3077,6 +3093,8 @@
 AC_SUBST(maybe_use_collect2)
 AC_SUBST(cc_set_by_configure)
 AC_SUBST(stage_prefix_set_by_configure)
+AC_SUBST(geninfodir)
+AC_SUBST(info_target)
 
 AC_SUBST_FILE(target_overrides)
 AC_SUBST_FILE(host_overrides)
Index: Makefile.in
===================================================================
RCS file: /l/cvs/egcs/Makefile.in,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Makefile.in
--- Makefile.in	1997/08/22 08:53:04	1.1.1.1
+++ Makefile.in	1997/08/22 10:17:26
@@ -235,6 +235,14 @@
 # Dir for temp files.
 tmpdir = /tmp
 
+# Section controlling generated files that are being shipped in releases.
+# The tools for generating these are not always available, so the building
+# of them should be optional even if they appear out-of-date.
+# Directories where generated files are to be found
+geninfodir = @geninfodir@
+# Targets controlling whether the files should be generated at all.
+info_target = @info_target@
+
 # Additional system libraries to link with.
 CLIB=
 
@@ -1971,15 +1979,17 @@
 #\f
 # Remake the info files.
 
-doc: info
-info: $(srcdir)/cpp.info $(srcdir)/gcc.info lang.info
+doc: $(info_target)
+info: $(geninfodir)/cpp.info $(geninfodir)/gcc.info lang.info
 
-$(srcdir)/cpp.info: cpp.texi
-	cd $(srcdir); $(MAKEINFO) $(MAKEINFOFLAGS) cpp.texi
+$(geninfodir)/cpp.info: cpp.texi
+	cd $(geninfodir); $(MAKEINFO) $(MAKEINFOFLAGS) -P $(srcdir) \
+		$(srcdir)/cpp.texi
 
-$(srcdir)/gcc.info: gcc.texi extend.texi install.texi invoke.texi \
+$(geninfodir)/gcc.info: gcc.texi extend.texi install.texi invoke.texi \
 		md.texi rtl.texi tm.texi gcov.texi
-	cd $(srcdir); $(MAKEINFO) $(MAKEINFOFLAGS) gcc.texi
+	cd $(geninfodir); $(MAKEINFO) $(MAKEINFOFLAGS) -P $(srcdir) \
+		$(srcdir)/gcc.texi
 
 dvi: $(srcdir)/gcc.dvi $(srcdir)/cpp.dvi lang.dvi
 
@@ -1993,8 +2003,8 @@
 	$(TEXI2DVI) $<
 
 $(srcdir)/INSTALL: install1.texi install.texi
-	$(MAKEINFO) -D INSTALLONLY --no-header --no-split \
-	  `echo $(srcdir)/install1.texi | sed 's,^\./,,'`
+	$(MAKEINFO) $(MAKEINFOFLAGS) -D INSTALLONLY --no-header \
+		--no-split `echo $(srcdir)/install1.texi | sed 's,^\./,,'`
 #\f
 # Deletion of files made during compilation.
 # There are four levels of this:
@@ -2244,7 +2254,7 @@
 # Install the info files.
 install-info: doc installdirs lang.install-info
 	-rm -f $(infodir)/cpp.info* $(infodir)/gcc.info*
-	cd $(srcdir); for f in cpp.info* gcc.info*; \
+	cd $(geninfodir); for f in cpp.info* gcc.info*; \
 	do $(INSTALL_DATA) $$f $(infodir)/$$f; done
 	-chmod a-x $(infodir)/cpp.info* $(infodir)/gcc.info*
 
Index: f/Make-lang.in
===================================================================
RCS file: /l/cvs/egcs/f/Make-lang.in,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Make-lang.in
--- Make-lang.in	1997/08/22 08:54:50	1.1.1.1
+++ Make-lang.in	1997/08/22 10:22:35
@@ -317,14 +317,17 @@
 f77.start.encap: g77 maybe-f2c
 f77.rest.encap: f77-runtime
 
-f77.info: $(srcdir)/f/g77.info
+f77.info: $(geninfodir)/f/g77.info
 f77.dvi: $(srcdir)/f/g77.dvi
 
 # g77 documentation.
-$(srcdir)/f/g77.info: f/g77.texi f/bugs.texi f/install.texi f/news.texi f/intdoc.texi
-	cd $(srcdir)/f; $(MAKEINFO) g77.texi
+$(geninfodir)/f/g77.info: f/g77.texi f/bugs.texi f/install.texi f/news.texi \
+  f/intdoc.texi
+	$(MAKEINFO) $(MAKEINFOFLAGS) -P $(srcdir)/f \
+		-o $(geninfodir)/f/g77.info $(srcdir)/f/g77.texi
 
-$(srcdir)/f/g77.dvi: f/g77.texi f/bugs.texi f/install.texi f/news.texi f/intdoc.texi
+$(srcdir)/f/g77.dvi: f/g77.texi f/bugs.texi f/install.texi f/news.texi \
+  f/intdoc.texi
 	cd $(srcdir)/f; $(TEXI2DVI) g77.texi
 
 $(srcdir)/f/intdoc.texi: f/intdoc.c f/intdoc.h f/intrin.def f/intrin.h
@@ -333,17 +336,20 @@
 	f/intdoc > $(srcdir)/f/intdoc.texi
 	rm f/intdoc
 
-$(srcdir)/f/BUGS: f/bugs0.texi f/bugs.texi
-	cd $(srcdir)/f; $(MAKEINFO) -D BUGSONLY --no-header --no-split \
-	  --no-validate bugs0.texi -o BUGS
-
-$(srcdir)/f/INSTALL: f/install0.texi f/install.texi
-	cd $(srcdir)/f; $(MAKEINFO) -D INSTALLONLY --no-header --no-split \
-	  --no-validate install0.texi -o INSTALL
-
-$(srcdir)/f/NEWS: f/news0.texi f/news.texi
-	cd $(srcdir)/f; $(MAKEINFO) -D NEWSONLY --no-header --no-split \
-	  --no-validate news0.texi -o NEWS
+$(geninfodir)/f/BUGS: f/bugs0.texi f/bugs.texi
+	cd $(geninfodir)/f; $(MAKEINFO) $(MAKEINFOFLAGS) -P $(srcdir)/f \
+		-D BUGSONLY --no-header --no-split --no-validate \
+		$(srcdir)/f/bugs0.texi -o BUGS
+
+$(geninfodir)/f/INSTALL: f/install0.texi f/install.texi
+	cd $(geninfodir)/f; $(MAKEINFO) $(MAKEINFOFLAGS)  -P $(srcdir)/f \
+		-D INSTALLONLY --no-header --no-split --no-validate \
+		$(srcdir)/f/install0.texi -o INSTALL
+
+$(geninfodir)/f/NEWS: f/news0.texi f/news.texi
+	cd $(geninfodir)/f; $(MAKEINFO) $(MAKEINFOFLAGS)  -P $(srcdir)/f \
+		-D NEWSONLY --no-header --no-split --no-validate \
+		$(srcdir)/f/news0.texi -o NEWS
 
 $(srcdir)/f/runtime/configure: $(srcdir)/f/runtime/configure.in 
 	cd f/runtime && $(MAKE) srcdir=../../$(srcdir)/f/runtime -f ../../$(srcdir)/f/runtime/Makefile.in rebuilt
@@ -437,7 +443,7 @@
 
 f77.install-info:
 	-rm -f $(infodir)/g77.info*
-	cd $(srcdir)/f; for f in g77.info*; \
+	cd $(geninfodir)/f; for f in g77.info*; \
 	do $(INSTALL_DATA) $$f $(infodir)/$$f; done
 	-chmod a-x $(infodir)/g77.info*
 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Some Haifa scheduler bugs
  1997-08-22 10:48 Building of generated parser files Niklas Hallqvist
@ 1997-08-22 13:28 ` Bernd Schmidt
  1997-08-22 13:53 ` Probable bug in gcc/reg-stack.c (example given) Bernd Schmidt
  1997-08-22 13:53 ` reload_cse_regs improvement Bernd Schmidt
  2 siblings, 0 replies; 7+ messages in thread
From: Bernd Schmidt @ 1997-08-22 13:28 UTC (permalink / raw)
  To: egcs

> Good point.  However, the more I look at that SCHED_GROUP_P loop,
> the more I think it must be broken.
> 
> Consider if we have Z <-A <-B <-C, all with SCHED_GROUP_P set on A B & C,
> but not Z.
> 
>   insn = C
>   We start the while loop (SCHED_GROUP_P (A) == 1)
>     prev = B
>     move_insn1 (C, last)
>     insn = B
>    2nd iteration (SCHED_GROUP_P (B) == 1)
>     prev = A
>     move_insn1 (B, last)
>     insn = A
>    3rd iteration (SCHED_GROUP_P (A) == 1)
>     prev = Z
>     move_insn_1 (A, last)
>     insn = Z
>    loop terminates (SCHED_GROUP_P (Z) == 0)
> 
>   move_insn1 (Z, last)
>   
>   
> So we end up moving one insn more than we should right?

No.
`SCHED_GROUP_P (INSN)'
     During instruction scheduling, in an insn, indicates that the
     previous insn must be scheduled together with this insn.  This is
     used to ensure that certain groups of instructions will not be
     split up by the instruction scheduling pass, for example, `use'
     insns before a `call_insn' may not be separated from the
     `call_insn'.  Stored in the `in_struct' field and printed as `/s'.

This means that the first insn in a SCHED_GROUP doesn't have the
SCHED_GROUP_P bit set, so Z _is_ supposed to be scheduled. The group
consists of all four insns.

> static rtx
> move_insn (insn, last)
>      rtx insn, last;
> {
> 
>   while (SCHED_GROUP_P (PREV_INSN (insn)))
>     {
>       rtx prev = PREV_INSN (insn);
>       move_insn1 (insn, last);
>       reemit_notes (insn, insn);
>       insn = prev;
>     }
> 
>   move_insn1 (insn, last);
>   return reemit_notes (insn, insn);
> }

Apart from what I said above, that might move insns that belong to a
SCHED_GROUP that starts before insn.

> Can you try it?  Or give me the testcase that's currently failing
> for you so that I can try it myself?

I didn't make any notes, unfortunately.  I can certainly try it, but
I doubt it will work.  I'll re-run c-torture with the unmodified
scheduler and report all failures.

Bernd

^ permalink raw reply	[flat|nested] 7+ messages in thread

* reload_cse_regs improvement
  1997-08-22 10:48 Building of generated parser files Niklas Hallqvist
  1997-08-22 13:28 ` Some Haifa scheduler bugs Bernd Schmidt
  1997-08-22 13:53 ` Probable bug in gcc/reg-stack.c (example given) Bernd Schmidt
@ 1997-08-22 13:53 ` Bernd Schmidt
  2 siblings, 0 replies; 7+ messages in thread
From: Bernd Schmidt @ 1997-08-22 13:53 UTC (permalink / raw)
  To: egcs

Here's the patch that I'm currently using to make reload_cse_regs replace
operands with equivalent registers if possible.  It fixes code like the
following on the ix86

movl %eax,16(%esp)
movl %eax,some_symbol
addl 16(%esp),%ebx

gcc-2.5.8 used to optimize this sort of thing better because find_equiv_reg
wasn't quite as conservative back then. This led to the "a.out faster than
ELF" discussion on linux-kernel where people compared a.out binaries
compiled with 2.5.8 to ELF binaries compiled with newer kernels.

I've sent an earlier version of this to Kenner and it will apparently be
in the next gcc2 snaphost.

How about generating some sort of pre-parsed form of the operand constraint
strings? For example, it would be possible to calculate things like which 
operands are input/output/inout, how bad each alternative is, or the class
of accepted registers for all insns and all alternatives once before
compilation (or even in one of the gen*.c files) so that functions in recog.c
and reload.c wouldn't have to parse the strings each time. If people think
this is a good idea, I'll try to come up with a patch.

Bernd

	* reload1.c (reload_cse_simplify_operands, reload_cse_no_longer_dead,
	reload_cse_delete_death_notes): New static functions.
	(no_longer_dead_regs): New static variable.
	(reload_cse_simplify_set): Declare as returning an int, return 1 if
	a replacement was made. Don't delete death notes on previous insns,
	call reload_cse_no_longer_dead instead. Call validate_change with
	nonzero value for in_group.
	(reload_cse_noop_set_p): Don't delete death notes on previous insns,
	call reload_cse_no_longer_dead instead.
	(reload_cse_regs): Initialize no_longer_dead_regs and call
	reload_cse_delete_death_notes as appropriate.
	Call apply_change_group after calling reload_cse_simplify_set.
	Call reload_cse_simplify_set on elements of a PARALLEL.
	Call reload_cse_simplify_operands if reload_cse_simplify_set could
	not simplify things.

	* recog.c (operand_matches_constraint_p): New function, split off
	from constrain_operands.
	(constrain_operands): Call operand_matches_constraint_p to see whether
	the operand matches the constraint.

*** recog.c.orig	Tue Aug 19 14:44:24 1997
--- recog.c	Thu Aug 21 20:16:51 1997
*************** struct funny_match
*** 1621,1626 ****
--- 1621,1869 ----
  };
  
  int
+ operand_matches_constraint_p (insn_code_num, opno, p, strict)
+      int insn_code_num;
+      int opno;
+      char *p;
+      int strict;
+ {
+   register rtx op = recog_operand[opno];
+   enum machine_mode mode = GET_MODE (op);
+   int offset = 0;
+   int val;
+   int c;
+ 
+   /* An empty constraint or empty alternative
+      allows anything which matched the pattern.  */
+   if (*p == 0 || *p == ',')
+     return 1;
+ 
+   /* A unary operator may be accepted by the predicate, but it
+      is irrelevant for matching contraints.  */
+   if (GET_RTX_CLASS (GET_CODE (op)) == '1')
+     op = XEXP (op, 0);
+ 
+   if (GET_CODE (op) == SUBREG)
+     {
+       if (GET_CODE (SUBREG_REG (op)) == REG
+ 	  && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER)
+ 	offset = SUBREG_WORD (op);
+       op = SUBREG_REG (op);
+     }
+ 
+   while (*p && (c = *p++) != ',')
+     switch (c)
+       {
+       case '?':
+       case '!':
+       case '*':
+       case '%':
+       case '=':
+       case '+':
+       case '&':
+ 	break;
+ 
+       case '#':
+ 	/* Ignore rest of this alternative as far as
+ 	   constraint checking is concerned.  */
+ 	return 0;
+ 
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+ 	/* This operand must be the same as a previous one.
+ 	   This kind of constraint is used for instructions such
+ 	   as add when they take only two operands.
+ 
+ 	   Note that the lower-numbered operand is passed first.
+ 
+ 	   If we are not testing strictly, assume that this constraint
+ 	   will be satisfied.  */
+ 	if (strict < 0)
+ 	  return 1;
+ 
+ 	val = operands_match_p (recog_operand[c - '0'],
+ 				recog_operand[opno]);
+ 
+ 	if (val == 0)
+ 	  break;
+ 	return val;
+ 
+       case 'p':
+ 	/* p is used for address_operands.  When we are called by
+ 	   gen_reload, no one will have checked that the address is
+ 	   strictly valid, i.e., that all pseudos requiring hard regs
+ 	   have gotten them.  */
+ 	if (strict <= 0
+ 	    || (strict_memory_address_p
+ 		(insn_operand_mode[insn_code_num][opno], op)))
+ 	  return 1;
+ 	break;
+ 
+ 	/* No need to check general_operand again;
+ 	   it was done in insn-recog.c.  */
+       case 'g':
+ 	/* Anything goes unless it is a REG and really has a hard reg
+ 	   but the hard reg is not in the class GENERAL_REGS.  */
+ 	if (strict < 0
+ 	    || GENERAL_REGS == ALL_REGS
+ 	    || GET_CODE (op) != REG
+ 	    || (reload_in_progress
+ 		&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ 	    || reg_fits_class_p (op, GENERAL_REGS, offset, mode))
+ 	  return 1;
+ 	break;
+ 
+       case 'r':
+ 	if (strict < 0
+ 	    || (strict == 0
+ 		&& GET_CODE (op) == REG
+ 		&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ 	    || (strict == 0 && GET_CODE (op) == SCRATCH)
+ 	    || (GET_CODE (op) == REG
+ 		&& ((GENERAL_REGS == ALL_REGS
+ 		     && REGNO (op) < FIRST_PSEUDO_REGISTER)
+ 		    || reg_fits_class_p (op, GENERAL_REGS,
+ 					 offset, mode))))
+ 	  return 1;
+ 	break;
+ 
+       case 'X':
+ 	/* This is used for a MATCH_SCRATCH in the cases when
+ 	   we don't actually need anything.  So anything goes
+ 	   any time.  */
+ 	return 1;
+ 
+       case 'm':
+ 	if (GET_CODE (op) == MEM
+ 	    /* Before reload, accept what reload can turn into mem.  */
+ 	    || (strict < 0 && CONSTANT_P (op))
+ 	    /* During reload, accept a pseudo  */
+ 	    || (reload_in_progress && GET_CODE (op) == REG
+ 		&& REGNO (op) >= FIRST_PSEUDO_REGISTER))
+ 	  return 1;
+ 	break;
+ 
+       case '<':
+ 	if (GET_CODE (op) == MEM
+ 	    && (GET_CODE (XEXP (op, 0)) == PRE_DEC
+ 		|| GET_CODE (XEXP (op, 0)) == POST_DEC))
+ 	  return 1;
+ 	break;
+ 
+       case '>':
+ 	if (GET_CODE (op) == MEM
+ 	    && (GET_CODE (XEXP (op, 0)) == PRE_INC
+ 		|| GET_CODE (XEXP (op, 0)) == POST_INC))
+ 	  return 1;
+ 	break;
+ 
+       case 'E':
+ #ifndef REAL_ARITHMETIC
+ 	/* Match any CONST_DOUBLE, but only if
+ 	   we can examine the bits of it reliably.  */
+ 	if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ 	     || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ 	    && GET_MODE (op) != VOIDmode && ! flag_pretend_float)
+ 	  break;
+ #endif
+ 	if (GET_CODE (op) == CONST_DOUBLE)
+ 	  return 1;
+ 	break;
+ 
+       case 'F':
+ 	if (GET_CODE (op) == CONST_DOUBLE)
+ 	  return 1;
+ 	break;
+ 
+       case 'G':
+       case 'H':
+ 	if (GET_CODE (op) == CONST_DOUBLE
+ 	    && CONST_DOUBLE_OK_FOR_LETTER_P (op, c))
+ 	  return 1;
+ 	break;
+ 
+       case 's':
+ 	if (GET_CODE (op) == CONST_INT
+ 	    || (GET_CODE (op) == CONST_DOUBLE
+ 		&& GET_MODE (op) == VOIDmode))
+ 	  break;
+       case 'i':
+ 	if (CONSTANT_P (op))
+ 	  return 1;
+ 	break;
+ 
+       case 'n':
+ 	if (GET_CODE (op) == CONST_INT
+ 	    || (GET_CODE (op) == CONST_DOUBLE
+ 		&& GET_MODE (op) == VOIDmode))
+ 	  return 1;
+ 	break;
+ 
+       case 'I':
+       case 'J':
+       case 'K':
+       case 'L':
+       case 'M':
+       case 'N':
+       case 'O':
+       case 'P':
+ 	if (GET_CODE (op) == CONST_INT
+ 	    && CONST_OK_FOR_LETTER_P (INTVAL (op), c))
+ 	  return 1;
+ 	break;
+ 
+ #ifdef EXTRA_CONSTRAINT
+       case 'Q':
+       case 'R':
+       case 'S':
+       case 'T':
+       case 'U':
+ 	if (EXTRA_CONSTRAINT (op, c))
+ 	  return 1;
+ 	break;
+ #endif
+ 
+       case 'V':
+ 	if (GET_CODE (op) == MEM
+ 	    && ((strict > 0 && ! offsettable_memref_p (op))
+ 		|| (strict < 0
+ 		    && !(CONSTANT_P (op) || GET_CODE (op) == MEM))
+ 		|| (reload_in_progress
+ 		    && !(GET_CODE (op) == REG
+ 			 && REGNO (op) >= FIRST_PSEUDO_REGISTER))))
+ 	  return 1;
+ 	break;
+ 
+       case 'o':
+ 	if ((strict > 0 && offsettable_memref_p (op))
+ 	    || (strict == 0 && offsettable_nonstrict_memref_p (op))
+ 	    /* Before reload, accept what reload can handle.  */
+ 	    || (strict < 0
+ 		&& (CONSTANT_P (op) || GET_CODE (op) == MEM))
+ 	    /* During reload, accept a pseudo  */
+ 	    || (reload_in_progress && GET_CODE (op) == REG
+ 		&& REGNO (op) >= FIRST_PSEUDO_REGISTER))
+ 	  return 1;
+ 	break;
+ 
+       default:
+ 	if (strict < 0
+ 	    || (strict == 0
+ 		&& GET_CODE (op) == REG
+ 		&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ 	    || (strict == 0 && GET_CODE (op) == SCRATCH)
+ 	    || (GET_CODE (op) == REG
+ 		&& reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c),
+ 				     offset, mode)))
+ 	  return 1;
+       }
+   return 0;
+ }
+ 
+ int
  constrain_operands (insn_code_num, strict)
       int insn_code_num;
       int strict;
*************** constrain_operands (insn_code_num, stric
*** 1656,1697 ****
  
        for (opno = 0; opno < noperands; opno++)
  	{
- 	  register rtx op = recog_operand[opno];
- 	  enum machine_mode mode = GET_MODE (op);
  	  register char *p = constraints[opno];
- 	  int offset = 0;
  	  int win = 0;
- 	  int val;
  
  	  earlyclobber[opno] = 0;
  
! 	  /* A unary operator may be accepted by the predicate, but it
! 	     is irrelevant for matching contraints.  */
! 	  if (GET_RTX_CLASS (GET_CODE (op)) == '1')
! 	    op = XEXP (op, 0);
! 
! 	  if (GET_CODE (op) == SUBREG)
! 	    {
! 	      if (GET_CODE (SUBREG_REG (op)) == REG
! 		  && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER)
! 		offset = SUBREG_WORD (op);
! 	      op = SUBREG_REG (op);
! 	    }
! 
! 	  /* An empty constraint or empty alternative
! 	     allows anything which matched the pattern.  */
! 	  if (*p == 0 || *p == ',')
! 	    win = 1;
  
  	  while (*p && (c = *p++) != ',')
  	    switch (c)
  	      {
- 	      case '?':
- 	      case '!':
- 	      case '*':
- 	      case '%':
- 		break;
- 
  	      case '#':
  		/* Ignore rest of this alternative as far as
  		   constraint checking is concerned.  */
--- 1899,1914 ----
  
        for (opno = 0; opno < noperands; opno++)
  	{
  	  register char *p = constraints[opno];
  	  int win = 0;
  
  	  earlyclobber[opno] = 0;
  
! 	  win = operand_matches_constraint_p (insn_code_num, opno, p, strict);
  
  	  while (*p && (c = *p++) != ',')
  	    switch (c)
  	      {
  	      case '#':
  		/* Ignore rest of this alternative as far as
  		   constraint checking is concerned.  */
*************** constrain_operands (insn_code_num, stric
*** 1711,1919 ****
  		earlyclobber[opno] = 1;
  		break;
  
! 	      case '0':
! 	      case '1':
! 	      case '2':
! 	      case '3':
! 	      case '4':
  		/* This operand must be the same as a previous one.
  		   This kind of constraint is used for instructions such
  		   as add when they take only two operands.
  
! 		   Note that the lower-numbered operand is passed first.
! 
! 		   If we are not testing strictly, assume that this constraint
! 		   will be satisfied.  */
! 		if (strict < 0)
! 		  val = 1;
! 		else
! 		  val = operands_match_p (recog_operand[c - '0'],
! 					  recog_operand[opno]);
  
  		matching_operands[opno] = c - '0';
  		matching_operands[c - '0'] = opno;
  
- 		if (val != 0)
- 		  win = 1;
  		/* If output is *x and input is *--x,
  		   arrange later to change the output to *--x as well,
  		   since the output op is the one that will be printed.  */
! 		if (val == 2 && strict > 0)
  		  {
  		    funny_match[funny_match_index].this = opno;
  		    funny_match[funny_match_index++].other = c - '0';
  		  }
  		break;
  
- 	      case 'p':
- 		/* p is used for address_operands.  When we are called by
- 		   gen_reload, no one will have checked that the address is
- 		   strictly valid, i.e., that all pseudos requiring hard regs
- 		   have gotten them.  */
- 		if (strict <= 0
- 		    || (strict_memory_address_p
- 			(insn_operand_mode[insn_code_num][opno], op)))
- 		  win = 1;
- 		break;
- 
- 		/* No need to check general_operand again;
- 		   it was done in insn-recog.c.  */
- 	      case 'g':
- 		/* Anything goes unless it is a REG and really has a hard reg
- 		   but the hard reg is not in the class GENERAL_REGS.  */
- 		if (strict < 0
- 		    || GENERAL_REGS == ALL_REGS
- 		    || GET_CODE (op) != REG
- 		    || (reload_in_progress
- 			&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
- 		    || reg_fits_class_p (op, GENERAL_REGS, offset, mode))
- 		  win = 1;
- 		break;
- 
- 	      case 'r':
- 		if (strict < 0
- 		    || (strict == 0
- 			&& GET_CODE (op) == REG
- 			&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
- 		    || (strict == 0 && GET_CODE (op) == SCRATCH)
- 		    || (GET_CODE (op) == REG
- 			&& ((GENERAL_REGS == ALL_REGS
- 			     && REGNO (op) < FIRST_PSEUDO_REGISTER)
- 			    || reg_fits_class_p (op, GENERAL_REGS,
- 						 offset, mode))))
- 		  win = 1;
- 		break;
- 
- 	      case 'X':
- 		/* This is used for a MATCH_SCRATCH in the cases when
- 		   we don't actually need anything.  So anything goes
- 		   any time.  */
- 		win = 1;
- 		break;
- 
- 	      case 'm':
- 		if (GET_CODE (op) == MEM
- 		    /* Before reload, accept what reload can turn into mem.  */
- 		    || (strict < 0 && CONSTANT_P (op))
- 		    /* During reload, accept a pseudo  */
- 		    || (reload_in_progress && GET_CODE (op) == REG
- 			&& REGNO (op) >= FIRST_PSEUDO_REGISTER))
- 		  win = 1;
- 		break;
- 
- 	      case '<':
- 		if (GET_CODE (op) == MEM
- 		    && (GET_CODE (XEXP (op, 0)) == PRE_DEC
- 			|| GET_CODE (XEXP (op, 0)) == POST_DEC))
- 		  win = 1;
- 		break;
- 
- 	      case '>':
- 		if (GET_CODE (op) == MEM
- 		    && (GET_CODE (XEXP (op, 0)) == PRE_INC
- 			|| GET_CODE (XEXP (op, 0)) == POST_INC))
- 		  win = 1;
- 		break;
- 
- 	      case 'E':
- #ifndef REAL_ARITHMETIC
- 		/* Match any CONST_DOUBLE, but only if
- 		   we can examine the bits of it reliably.  */
- 		if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
- 		     || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
- 		    && GET_MODE (op) != VOIDmode && ! flag_pretend_float)
- 		  break;
- #endif
- 		if (GET_CODE (op) == CONST_DOUBLE)
- 		  win = 1;
- 		break;
- 
- 	      case 'F':
- 		if (GET_CODE (op) == CONST_DOUBLE)
- 		  win = 1;
- 		break;
- 
- 	      case 'G':
- 	      case 'H':
- 		if (GET_CODE (op) == CONST_DOUBLE
- 		    && CONST_DOUBLE_OK_FOR_LETTER_P (op, c))
- 		  win = 1;
- 		break;
- 
- 	      case 's':
- 		if (GET_CODE (op) == CONST_INT
- 		    || (GET_CODE (op) == CONST_DOUBLE
- 			&& GET_MODE (op) == VOIDmode))
- 		  break;
- 	      case 'i':
- 		if (CONSTANT_P (op))
- 		  win = 1;
- 		break;
- 
- 	      case 'n':
- 		if (GET_CODE (op) == CONST_INT
- 		    || (GET_CODE (op) == CONST_DOUBLE
- 			&& GET_MODE (op) == VOIDmode))
- 		  win = 1;
- 		break;
- 
- 	      case 'I':
- 	      case 'J':
- 	      case 'K':
- 	      case 'L':
- 	      case 'M':
- 	      case 'N':
- 	      case 'O':
- 	      case 'P':
- 		if (GET_CODE (op) == CONST_INT
- 		    && CONST_OK_FOR_LETTER_P (INTVAL (op), c))
- 		  win = 1;
- 		break;
- 
- #ifdef EXTRA_CONSTRAINT
-               case 'Q':
-               case 'R':
-               case 'S':
-               case 'T':
-               case 'U':
- 		if (EXTRA_CONSTRAINT (op, c))
- 		  win = 1;
- 		break;
- #endif
- 
- 	      case 'V':
- 		if (GET_CODE (op) == MEM
- 		    && ((strict > 0 && ! offsettable_memref_p (op))
- 			|| (strict < 0
- 			    && !(CONSTANT_P (op) || GET_CODE (op) == MEM))
- 			|| (reload_in_progress
- 			    && !(GET_CODE (op) == REG
- 				 && REGNO (op) >= FIRST_PSEUDO_REGISTER))))
- 		  win = 1;
- 		break;
- 
- 	      case 'o':
- 		if ((strict > 0 && offsettable_memref_p (op))
- 		    || (strict == 0 && offsettable_nonstrict_memref_p (op))
- 		    /* Before reload, accept what reload can handle.  */
- 		    || (strict < 0
- 			&& (CONSTANT_P (op) || GET_CODE (op) == MEM))
- 		    /* During reload, accept a pseudo  */
- 		    || (reload_in_progress && GET_CODE (op) == REG
- 			&& REGNO (op) >= FIRST_PSEUDO_REGISTER))
- 		  win = 1;
- 		break;
- 
  	      default:
! 		if (strict < 0
! 		    || (strict == 0
! 			&& GET_CODE (op) == REG
! 			&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
! 		    || (strict == 0 && GET_CODE (op) == SCRATCH)
! 		    || (GET_CODE (op) == REG
! 			&& reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c),
! 					     offset, mode)))
! 		  win = 1;
  	      }
  
  	  constraints[opno] = p;
--- 1928,1955 ----
  		earlyclobber[opno] = 1;
  		break;
  
! 	      case '0': case '1': case '2': case '3': case '4':
  		/* This operand must be the same as a previous one.
  		   This kind of constraint is used for instructions such
  		   as add when they take only two operands.
  
! 		   Note that the lower-numbered operand is passed first.  */
  
  		matching_operands[opno] = c - '0';
  		matching_operands[c - '0'] = opno;
  
  		/* If output is *x and input is *--x,
  		   arrange later to change the output to *--x as well,
  		   since the output op is the one that will be printed.  */
! 		if (win == 2 && strict > 0)
  		  {
  		    funny_match[funny_match_index].this = opno;
  		    funny_match[funny_match_index++].other = c - '0';
  		  }
  		break;
  
  	      default:
! 		break;
  	      }
  
  	  constraints[opno] = p;
*** reload1.c.orig	Thu Aug 21 14:08:51 1997
--- reload1.c	Thu Aug 21 21:42:32 1997
*************** static void reload_cse_invalidate_mem	PR
*** 399,407 ****
  static void reload_cse_invalidate_rtx	PROTO((rtx, rtx));
  static int reload_cse_regno_equal_p	PROTO((int, rtx, enum machine_mode));
  static int reload_cse_noop_set_p	PROTO((rtx, rtx));
! static void reload_cse_simplify_set	PROTO((rtx, rtx));
  static void reload_cse_check_clobber	PROTO((rtx, rtx));
  static void reload_cse_record_set	PROTO((rtx, rtx));
  \f
  /* Initialize the reload pass once per compilation.  */
  
--- 399,410 ----
  static void reload_cse_invalidate_rtx	PROTO((rtx, rtx));
  static int reload_cse_regno_equal_p	PROTO((int, rtx, enum machine_mode));
  static int reload_cse_noop_set_p	PROTO((rtx, rtx));
! static int reload_cse_simplify_set	PROTO((rtx, rtx));
! static int reload_cse_simplify_operands	PROTO((rtx));
  static void reload_cse_check_clobber	PROTO((rtx, rtx));
  static void reload_cse_record_set	PROTO((rtx, rtx));
+ static void reload_cse_delete_death_notes	PROTO((rtx));
+ static void reload_cse_no_longer_dead	PROTO((int, enum machine_mode));
  \f
  /* Initialize the reload pass once per compilation.  */
  
*************** static rtx *reg_values;
*** 7176,7181 ****
--- 7179,7191 ----
  
  static rtx invalidate_regno_rtx;
  
+ /* This is a set of registers for which we must remove REG_DEAD notes in
+    previous insns, because our modifications made them invalid.  That can
+    happen if we introduced the register into the current insn, or we deleted
+    the current insn which used to set the register.  */
+ 
+ static HARD_REG_SET no_longer_dead_regs;
+ 
  /* Invalidate any entries in reg_values which depend on REGNO,
     including those for REGNO itself.  This is called if REGNO is
     changing.  If CLOBBER is true, then always forget anything we
*************** reload_cse_invalidate_rtx (dest, ignore)
*** 7375,7380 ****
--- 7385,7436 ----
      reload_cse_invalidate_mem (dest);
  }
  
+ /* Possibly delete death notes on the insns before INSN if modifying INSN
+    extended the lifespan of the registers.  */
+ 
+ static void
+ reload_cse_delete_death_notes (insn)
+      rtx insn;
+ {
+   int dreg;
+ 
+   for (dreg = 0; dreg < FIRST_PSEUDO_REGISTER; dreg++)
+     {
+       rtx trial;
+ 
+       if (! TEST_HARD_REG_BIT (no_longer_dead_regs, dreg))
+ 	continue;
+ 
+       for (trial = prev_nonnote_insn (insn);
+ 	   (trial
+ 	    && GET_CODE (trial) != CODE_LABEL
+ 	    && GET_CODE (trial) != BARRIER);
+ 	   trial = prev_nonnote_insn (trial))
+ 	{
+ 	  if (find_regno_note (trial, REG_DEAD, dreg))
+ 	    {
+ 	      remove_death (dreg, trial);
+ 	      break;
+ 	    }
+ 	}
+     }
+ }
+ 
+ /* Record that the current insn uses hard reg REGNO in mode MODE.  This
+    will be used in reload_cse_delete_death_notes to delete prior REG_DEAD
+    notes for this register.  */
+ 
+ static void
+ reload_cse_no_longer_dead (regno, mode)
+      int regno;
+      enum machine_mode mode;
+ {
+   int nregs = HARD_REGNO_NREGS (regno, mode);
+   while (nregs-- > 0)
+     SET_HARD_REG_BIT (no_longer_dead_regs, regno++);
+ }
+ 
+ 
  /* Do a very simple CSE pass over the hard registers.
  
     This function detects no-op moves where we happened to assign two
*************** reload_cse_invalidate_rtx (dest, ignore)
*** 7385,7391 ****
     This function also detects cases where we load a value from memory
     into two different registers, and (if memory is more expensive than
     registers) changes it to simply copy the first register into the
!    second register.  */
  
  void
  reload_cse_regs (first)
--- 7441,7452 ----
     This function also detects cases where we load a value from memory
     into two different registers, and (if memory is more expensive than
     registers) changes it to simply copy the first register into the
!    second register.  
! 
!    Another optimization is performed that scans the operands of each
!    instruction to see whether the value is already available in a
!    hard register.  It then replaces the operand with the hard register
!    if possible, much like an optional reload would.  */
  
  void
  reload_cse_regs (first)
*************** reload_cse_regs (first)
*** 7443,7448 ****
--- 7504,7511 ----
        if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
  	continue;
  
+       CLEAR_HARD_REG_SET (no_longer_dead_regs);
+ 
        /* If this is a call instruction, forget anything stored in a
  	 call clobbered register, or, if this is not a const call, in
  	 memory.  */
*************** reload_cse_regs (first)
*** 7459,7480 ****
        body = PATTERN (insn);
        if (GET_CODE (body) == SET)
  	{
  	  if (reload_cse_noop_set_p (body, insn))
  	    {
  	      PUT_CODE (insn, NOTE);
  	      NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
  	      NOTE_SOURCE_FILE (insn) = 0;
  
  	      /* We're done with this insn.  */
  	      continue;
  	    }
  
! 	  reload_cse_simplify_set (body, insn);
  	  reload_cse_record_set (body, body);
  	}
        else if (GET_CODE (body) == PARALLEL)
  	{
! 	  int delete;
  
  	  /* If every action in a PARALLEL is a noop, we can delete
               the entire PARALLEL.  */
--- 7522,7553 ----
        body = PATTERN (insn);
        if (GET_CODE (body) == SET)
  	{
+ 	  int count = 0;
  	  if (reload_cse_noop_set_p (body, insn))
  	    {
  	      PUT_CODE (insn, NOTE);
  	      NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
  	      NOTE_SOURCE_FILE (insn) = 0;
+ 	      reload_cse_delete_death_notes (insn);
  
  	      /* We're done with this insn.  */
  	      continue;
  	    }
  
! 	  /* It's not a no-op, but we can try to simplify it.  */
! 	  CLEAR_HARD_REG_SET (no_longer_dead_regs);
! 	  count += reload_cse_simplify_set (body, insn);
! 
! 	  if (count > 0 && apply_change_group ())
! 	    reload_cse_delete_death_notes (insn);
! 	  else if (reload_cse_simplify_operands (insn))
! 	    reload_cse_delete_death_notes (insn);
! 	    
  	  reload_cse_record_set (body, body);
  	}
        else if (GET_CODE (body) == PARALLEL)
  	{
! 	  int count = 0;
  
  	  /* If every action in a PARALLEL is a noop, we can delete
               the entire PARALLEL.  */
*************** reload_cse_regs (first)
*** 7488,7497 ****
--- 7561,7582 ----
  	      PUT_CODE (insn, NOTE);
  	      NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
  	      NOTE_SOURCE_FILE (insn) = 0;
+ 	      reload_cse_delete_death_notes (insn);
  
  	      /* We're done with this insn.  */
  	      continue;
  	    }
+ 	  
+ 	  /* It's not a no-op, but we can try to simplify it.  */
+ 	  CLEAR_HARD_REG_SET (no_longer_dead_regs);
+ 	  for (i = XVECLEN (body, 0) - 1; i >= 0; --i)
+ 	    if (GET_CODE (XVECEXP (body, 0, i)) == SET)
+ 	      count += reload_cse_simplify_set (XVECEXP (body, 0, i), insn);
+ 
+ 	  if (count > 0 && apply_change_group ())
+ 	    reload_cse_delete_death_notes (insn);
+ 	  else if (reload_cse_simplify_operands (insn))
+ 	    reload_cse_delete_death_notes (insn);
  
  	  /* Look through the PARALLEL and record the values being
               set, if possible.  Also handle any CLOBBERs.  */
*************** reload_cse_noop_set_p (set, insn)
*** 7657,7690 ****
  
    /* If we can delete this SET, then we need to look for an earlier
       REG_DEAD note on DREG, and remove it if it exists.  */
!   if (ret)
      {
        if (! find_regno_note (insn, REG_UNUSED, dreg))
! 	{
! 	  rtx trial;
! 
! 	  for (trial = prev_nonnote_insn (insn);
! 	       (trial
! 		&& GET_CODE (trial) != CODE_LABEL
! 		&& GET_CODE (trial) != BARRIER);
! 	       trial = prev_nonnote_insn (trial))
! 	    {
! 	      if (find_regno_note (trial, REG_DEAD, dreg))
! 		{
! 		  remove_death (dreg, trial);
! 		  break;
! 		}
! 	    }
! 	}
      }
  
    return ret;
  }
  
  /* Try to simplify a single SET instruction.  SET is the set pattern.
!    INSN is the instruction it came from. */
  
! static void
  reload_cse_simplify_set (set, insn)
       rtx set;
       rtx insn;
--- 7742,7763 ----
  
    /* If we can delete this SET, then we need to look for an earlier
       REG_DEAD note on DREG, and remove it if it exists.  */
!   if (ret && dreg >= 0)
      {
        if (! find_regno_note (insn, REG_UNUSED, dreg))
! 	reload_cse_no_longer_dead (dreg, dest_mode);
      }
  
    return ret;
  }
  
  /* Try to simplify a single SET instruction.  SET is the set pattern.
!    INSN is the instruction it came from.
!    This function only handles one case: if we set a register to a value
!    which is not a register, we try to find that value in some other register
!    and change the set into a register copy.  */
  
! static int
  reload_cse_simplify_set (set, insn)
       rtx set;
       rtx insn;
*************** reload_cse_simplify_set (set, insn)
*** 7695,7704 ****
    enum reg_class dclass;
    register int i;
  
-   /* We only handle one case: if we set a register to a value which is
-      not a register, we try to find that value in some other register
-      and change the set into a register copy.  */
- 
    dreg = true_regnum (SET_DEST (set));
    if (dreg < 0)
      return;
--- 7768,7773 ----
*************** reload_cse_simplify_set (set, insn)
*** 7726,7763 ****
  	  pop_obstacks ();
  
  	  validated = validate_change (insn, &SET_SRC (set),
! 				       gen_rtx (REG, dest_mode, i), 0);
  
  	  /* Go back to the obstack we are using for temporary
               storage.  */
  	  push_obstacks (&reload_obstack, &reload_obstack);
  
! 	  if (validated)
  	    {
! 	      /* We need to look for an earlier REG_DEAD note on I,
! 		 and remove it if it exists.  */
! 	      if (! find_regno_note (insn, REG_UNUSED, i))
  		{
! 		  rtx trial;
  
! 		  for (trial = prev_nonnote_insn (insn);
! 		       (trial
! 			&& GET_CODE (trial) != CODE_LABEL
! 			&& GET_CODE (trial) != BARRIER);
! 		       trial = prev_nonnote_insn (trial))
  		    {
! 		      if (find_regno_note (trial, REG_DEAD, i))
! 			{
! 			  remove_death (i, trial);
! 			  break;
! 			}
  		    }
  		}
  
! 	      return;
  	    }
  	}
      }
  }
  
  /* These two variables are used to pass information from
--- 7795,8073 ----
  	  pop_obstacks ();
  
  	  validated = validate_change (insn, &SET_SRC (set),
! 				       gen_rtx (REG, dest_mode, i), 1);
  
  	  /* Go back to the obstack we are using for temporary
               storage.  */
  	  push_obstacks (&reload_obstack, &reload_obstack);
  
! 	  if (validated && ! find_regno_note (insn, REG_UNUSED, i))
  	    {
! 	      reload_cse_no_longer_dead (i, dest_mode);
! 	      return 1;
! 	    }
! 	}
!     }
!   return 0;
! }
! 
! /* Try to replace operands in INSN with equivalent values that are already
!    in registers.  This can be viewed as optional reloading.  
!  
!    For each non-register operand in the insn, see if any hard regs are
!    known to be equivalent to that operand.  Record the alternatives which
!    can accept these hard registers.  Among all alternatives, select the
!    ones which are better or equal to the one currently matching, where
!    "better" is in terms of '?' and '!' constraints.  Among the remaining
!    alternatives, select the one which replaces most operands with
!    hard registers.  */
! 
! static int
! reload_cse_simplify_operands (insn)
!      rtx insn;
! {
! #ifdef REGISTER_CONSTRAINTS
!   int insn_code_number, n_operands, n_alternatives;
!   int i,j;
!   int found_equiv = 0;
! 
!   char *constraints[MAX_RECOG_OPERANDS];
! 
!   /* Vector recording how bad an alternative is.  */
!   int *alternative_reject;
!   /* Vector recording how many registers can be introduced by choosing
!      this alternative.  */
!   int *alternative_nregs;
!   /* Array of vectors recording, for each operand and each alternative,
!      which hard register to substitute, or -1 if the operand should be
!      left as it is.  */
!   int *op_alt_regno[MAX_RECOG_OPERANDS];
!   /* Array of alternatives, sorted in order of decreasing desirability.  */
!   int *alternative_order;
! 
!   /* Find out some information about this insn.  */
!   insn_code_number = recog_memoized (insn);
!   /* We don't modify asm instructions.  */
!   if (insn_code_number < 0)
!     return 0;
! 
!   n_operands = insn_n_operands[insn_code_number];
!   n_alternatives = insn_n_alternatives[insn_code_number];
! 
!   if (n_alternatives == 0 || n_operands == 0)
!     return;
!   insn_extract (insn);
! 
!   /* Figure out which alternative currently matches.  */
!   if (! constrain_operands (insn_code_number, 1))
!     abort ();
! 
!   alternative_reject = (int *) alloca (n_alternatives * sizeof (int));
!   alternative_nregs = (int *) alloca (n_alternatives * sizeof (int));
!   alternative_order = (int *) alloca (n_alternatives * sizeof (int));
!   bzero ((char *)alternative_reject, n_alternatives * sizeof (int));
!   bzero ((char *)alternative_nregs, n_alternatives * sizeof (int));
! 
!   for (i = 0; i < n_operands; i++)
!     {
!       enum machine_mode mode;
!       int regno, new_alt;
!       char *p;
! 
!       p = constraints[i] = insn_operand_constraint[insn_code_number][i];
!       mode = insn_operand_mode[insn_code_number][i];
! 
!       /* Add the reject values for each alternative given by the constraints
! 	 for this operand.  Also calculate for which alternatives the
!          operand is valid.  */
!       j = 0;
!       new_alt = 1;
!       op_alt_regno[i] = (int *) alloca (n_alternatives * sizeof (int));
! 
!       for (;;)
! 	{
! 	  char c;
! 	  if (new_alt)
! 	    {
! 	      new_alt = 0;
! 	      if (operand_matches_constraint_p (insn_code_number, i, p, 1))
! 		op_alt_regno[i][j] = -1;
! 	      else
! 		op_alt_regno[i][j] = -2;
! 	    }
! 	  c = *p++;
! 	  if (c == '\0')
! 	    break;
! 	  if (c == ',')
! 	    j++, new_alt = 1;
! 	  else if (c == '?')
! 	    alternative_reject[j] += 3;
! 	  else if (c == '!')
! 	    alternative_reject[j] += 300;
! 	}
! 
!       /* We won't change operands which are already registers.  We
! 	 also don't want to modify output operands.  */
!       regno = true_regnum (recog_operand[i]);
!       if (regno >= 0
! 	  || constraints[i][0] == '='
! 	  || constraints[i][0] == '+')
! 	continue;
! 
!       for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
! 	{
! 	  int class = (int) NO_REGS;
! 
! 	  if (! reload_cse_regno_equal_p (regno, recog_operand[i], mode))
! 	    continue;
! 
! 	  /* We found a register equal to this operand.  Now look for all
! 	     alternatives that can accept this register and have not been
! 	     assigned a register they can use yet.  */
! 	  j = 0;
! 	  p = constraints[i];
! 	  for (;;)
! 	    {
! 	      char c = *p++;
! 
! 	      switch (c)
  		{
! 		case '=':  case '+':  case '?':
! 		case '#':  case '&':  case '!':
! 		case '*':  case '%':  
! 		case '0':  case '1':  case '2':  case '3':  case '4':
! 		case 'm':  case '<':  case '>':  case 'V':  case 'o':
! 		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':
! #ifdef EXTRA_CONSTRAINT
! 		case 'Q':  case 'R':  case 'S':  case 'T':  case 'U':
! #endif
! 		case 'p': case 'X':
! 		  /* These don't say anything we care about.  */
! 		  break;
  
! 		case 'g': case 'r':
! 		  class = reg_class_subunion[(int) class][(int) GENERAL_REGS];
! 		  break;
! 
! 		default:
! 		  class
! 		    = reg_class_subunion[(int) class][(int) REG_CLASS_FROM_LETTER (c)];
! 		  break;
! 
! 		case ',': case '\0':
! 		  /* See if REGNO fits this alternative, and set it up as the
! 		     replacement register if we don't have one for this
! 		     alternative yet.  */
! 		  if (op_alt_regno[i][j] < 0
! 		      && reg_fits_class_p (gen_rtx (REG, mode, regno), class,
! 					   0, mode))
  		    {
! 		      found_equiv = 1;
! 		      alternative_nregs[j]++;
! 		      op_alt_regno[i][j] = regno;
  		    }
+ 		  j++;
+ 		  break;
  		}
  
! 	      if (c == '\0')
! 		break;
  	    }
  	}
      }
+ 
+   /* If we didn't find equivalences, give up now.  */
+   if (!found_equiv)
+     return 0;
+ 
+   /* Record all alternatives which are better or equal to the currently
+      matching one in the alternative_order array.  */
+   for (i = j = 0; i < n_alternatives; i++)
+     {
+       int opno;
+       /* If one of the operands didn't match this alternative and has
+ 	 no replacement register, this alternative can't win.  */
+       for (opno = 0; opno < n_operands; opno++)
+ 	if (op_alt_regno[opno][i] == -2)
+ 	  break;
+       if (alternative_reject[i] <= alternative_reject[which_alternative]
+ 	  && opno == n_operands)
+ 	alternative_order[j++] = i;
+     }
+ 
+   n_alternatives = j;
+ 
+   /* Sort it.  Given a small number of alternatives, a dumb algorithm
+      won't hurt too much.  */
+   for (i = 0; i < n_alternatives - 1; i++)
+     {
+       int best = i;
+       int best_reject = alternative_reject[alternative_order[i]];
+       int best_nregs = alternative_nregs[alternative_order[i]];
+       int tmp;
+ 
+       for (j = i + 1; j < n_alternatives; j++)
+ 	{
+ 	  int this_reject = alternative_reject[alternative_order[j]];
+ 	  int this_nregs = alternative_nregs[alternative_order[j]];
+ 
+ 	  if (this_reject < best_reject
+ 	      || (this_reject == best_reject && this_nregs < best_nregs))
+ 	    {
+ 	      best = j;
+ 	      best_reject = this_reject;
+ 	      best_nregs = this_nregs;
+ 	    }
+ 	}
+ 
+       tmp = alternative_order[best];
+       alternative_order[best] = alternative_order[i];
+       alternative_order[i] = tmp;
+     }
+ 
+   /* Substitute the operands as determined by op_alt_regno for the best
+      alternative.  */
+   j = alternative_order[0];
+   CLEAR_HARD_REG_SET (no_longer_dead_regs);
+ 
+   /* Pop back to the real obstacks while changing the insn.  */
+   pop_obstacks ();
+ 
+   for (i = 0; i < n_operands; i++)
+     {
+       enum machine_mode mode = insn_operand_mode[insn_code_number][i];
+       if (op_alt_regno[i][j] == -1)
+ 	continue;
+ 
+       reload_cse_no_longer_dead (op_alt_regno[i][j], mode);
+       validate_change (insn, recog_operand_loc[i],
+ 		       gen_rtx (REG, mode, op_alt_regno[i][j]), 1);
+     }
+ 
+   for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--)
+     {
+       int op = recog_dup_num[i];
+       enum machine_mode mode = insn_operand_mode[insn_code_number][op];
+ 
+       if (op_alt_regno[op][j] == -1)
+ 	continue;
+ 
+       reload_cse_no_longer_dead (op_alt_regno[op][j], mode);
+       validate_change (insn, recog_operand_loc[op],
+ 		       gen_rtx (REG, mode, op_alt_regno[op][j]), 1);
+     }
+ 
+   /* Go back to the obstack we are using for temporary
+      storage.  */
+   push_obstacks (&reload_obstack, &reload_obstack);
+ 
+   return apply_change_group ();
+ #else
+   return 0;
+ #endif
  }
  
  /* These two variables are used to pass information from

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Probable bug in gcc/reg-stack.c (example given)
  1997-08-22 10:48 Building of generated parser files Niklas Hallqvist
  1997-08-22 13:28 ` Some Haifa scheduler bugs Bernd Schmidt
@ 1997-08-22 13:53 ` Bernd Schmidt
  1997-08-22 13:53 ` reload_cse_regs improvement Bernd Schmidt
  2 siblings, 0 replies; 7+ messages in thread
From: Bernd Schmidt @ 1997-08-22 13:53 UTC (permalink / raw)
  To: egcs

> When compiled by `gcc -O2 t.c', then run by `./a.out', it
> produces a "Floating point exception" on my i486 running GNU/Linux,
> which in this case means it has unexpectedly overflowed the
> floating-point ("387 coprocessor") stack.
> 
> Looking at the RTL and reading up on reg-stack.c a bit -- not enough
> time to figure out the problem and fix it myself right now -- the
> problem seems to be that reg-stack.c doesn't recognize that the
> second assignment to `xmax', `xmax = 2.0', is to a live register,
> which must thus be popped first, then loaded.  Perhaps this is
> because reg-stack's own version of flow analysis, or basic block
> analysis, doesn't see that `lab100' can be reached from the
> `goto *next;' at `lab20', or some such thing.  Anyway, the 1.0 value
> in it is not popped before the new 2.0 value is loaded.  After a
> few iterations, this leads to the stack overflowing -- though if
> that were silent, in this particular case it wouldn't be a problem.

I've looked into it.  The problem is that reg-stack doesn't handle
computed gotos in convert_regs.  Unfortunately, I don't think that it
could easily handle them, it relies on the fact that it can change the
target for every jump insn by simple replacing one LABEL_REF with
another.
I've attached a quick&dirty hack to fix the problem by not allowing any
pseudos that live across a label that is reachable by a nonlocal goto
to be allocated to one of the stack regs.
This patch only fixes optimizing compilation. If it works, we can try to
think of something for stupid.c.

Bernd

--- egcs-ss-970814/flow.c	Fri Aug 15 20:56:02 1997
+++ egcs-new/flow.c	Fri Aug 22 10:49:34 1997
@@ -197,6 +197,12 @@
 
 rtx *basic_block_end;
 
+/* Element N indicates whether basic block N can be reached through a
+   computed jump.
+   This info lasts until we finish compiling the function.  */
+
+int *basic_block_computed_jump_target;
+
 /* Element N is a regset describing the registers live
    at the start of basic block N.
    This info lasts until we finish compiling the function.  */
@@ -341,6 +347,10 @@
   n_basic_blocks = i;
   basic_block_head = (rtx *) oballoc (n_basic_blocks * sizeof (rtx));
   basic_block_end = (rtx *) oballoc (n_basic_blocks * sizeof (rtx));
+  basic_block_computed_jump_target
+    = (int *) oballoc (n_basic_blocks * sizeof (int));
+  bzero ((char *)basic_block_computed_jump_target,
+	 n_basic_blocks * sizeof (int));
   basic_block_drops_in = (char *) alloca (n_basic_blocks);
   basic_block_loop_depth = (short *) alloca (n_basic_blocks * sizeof (short));
   uid_block_number
@@ -540,7 +550,11 @@
 
 		    for (x = label_value_list; x; x = XEXP (x, 1))
 		      if (! LABEL_REF_NONLOCAL_P (x))
-			block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+		        {
+			  int n = BLOCK_NUM (XEXP (x, 0));
+			  basic_block_computed_jump_target[n] = 1;
+			  block_live[n] = 1;
+			}
 		  }
 
 		for (x = label_value_list; x; x = XEXP (x, 1))
--- egcs-ss-970814/basic-block.h	Fri Aug 15 20:55:24 1997
+++ egcs-new/basic-block.h	Fri Aug 22 10:37:45 1997
@@ -102,6 +102,11 @@
 
 extern rtx *basic_block_end;
 
+/* Index by basic block number, contains nonzero if the basic block starts
+   with a label that can be reached through a computed jump.  */
+
+extern int *basic_block_computed_jump_target;
+
 /* Index by basic block number, get address of regset
    describing the registers live at the start of that block.  */
 
--- egcs-ss-970814/global.c	Fri Aug 15 20:56:14 1997
+++ egcs-new/global.c	Fri Aug 22 10:51:49 1997
@@ -663,6 +659,15 @@
 	   allocno now live, and with each hard reg now live.  */
 
 	record_conflicts (block_start_allocnos, ax);
+
+#ifdef STACK_REGS
+	/* Pseudos can't go in stack regs at the start of a basic block
+	   that can be reached through a computed goto, since reg-stack
+	   can't handle computed gotos.  */
+	if (basic_block_computed_jump_target[b])
+	  for (ax = FIRST_STACK_REG; ax <= LAST_STACK_REG; ax++)
+	    record_one_conflict (ax);
+#endif
       }
 
       insn = basic_block_head[b];

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Probable bug in gcc/reg-stack.c (example given)
  1997-08-27  3:29 Here are the g++ test results Mumit Khan
@ 1997-08-27  3:29 ` Jeffrey A Law
  0 siblings, 0 replies; 7+ messages in thread
From: Jeffrey A Law @ 1997-08-27  3:29 UTC (permalink / raw)
  To: egcs

First, when looking for a set of an FP register, it seems to me
that just checking that the pattern is a (set (fp) (something))
won't work for insns like this:

(define_insn ""
  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m")
        (float_truncate:SF
         (match_operand:DF 1 "register_operand" "0,f")))
   (clobber (match_operand:SF 2 "memory_operand" "m,m"))]
  "TARGET_80387"

It seems to me you should use reg_set_p when you're looking
for a set of an FP register and for a set of the PC register.

Or, you need to iterate through each item in a PARALLEL and
do the check for (set (fp) (something))

I also think your patch will pessimize the code when it
runs into a jump table.  I think this can be easily solved
too (look in flow.c I think you can actually crib a lot of
the code from find_basic_blocks).

jeff

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Probable bug in gcc/reg-stack.c (example given)
@ 1997-08-26  2:30 Stan Cox
  0 siblings, 0 replies; 7+ messages in thread
From: Stan Cox @ 1997-08-26  2:30 UTC (permalink / raw)
  To: egcs

Bernd> Unfortunately, I don't think that it could easily handle them

This fixes reg-stack.c, but it is awkward as Bernd mentions.

--- /udd/sac/coxs/fm-fsf/reg-stack.c	Fri Aug  8 17:04:20 1997
+++ /pdd/c/sources/gcc-970803/reg-stack.c	Mon Aug 25 15:22:41 1997
@@ -2728,2 +2728,3 @@ subst_stack_regs (insn, regstack)
   register int i;
+  rtx head, jump, pat, cipat;
   int n_operands;
@@ -2798,2 +2799,32 @@ subst_stack_regs (insn, regstack)
     return;
+
+  /* If we are reached by a computed goto which sets this same stack register,
+     then pop this stack register, but maintain regstack. */
+
+  head = block_begin[BLOCK_NUM(insn)];
+  pat = PATTERN(insn);
+  if (GET_CODE (head) == CODE_LABEL
+      && GET_CODE (pat) == SET && STACK_REG_P (SET_DEST (pat)))
+    for (jump = LABEL_REFS (head);
+	 jump != head;
+	 jump = LABEL_NEXTREF (jump))
+      {
+	register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
+	cipat = PATTERN (CONTAINING_INSN (jump));
+	if (GET_CODE (cipat) == SET
+	    && SET_DEST (cipat) == pc_rtx
+	    && uses_reg_or_mem (SET_SRC (cipat)))
+	  {
+	    if (TEST_HARD_REG_BIT (block_out_reg_set[from_block],
+				   REGNO (SET_DEST (pat))))
+	      {
+		struct stack_def old;
+		bcopy (regstack->reg, old.reg, sizeof (old.reg));
+		emit_pop_insn (insn, regstack, SET_DEST (pat), emit_insn_before);
+		regstack->top += 1;
+		bcopy (old.reg, regstack->reg, sizeof (old.reg));
+		SET_HARD_REG_BIT (regstack->reg_set, REGNO (SET_DEST (pat)));
+	      }
+	  }
+      }
 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Probable bug in gcc/reg-stack.c (example given)
@ 1997-08-19 16:06 Craig Burley
  0 siblings, 0 replies; 7+ messages in thread
From: Craig Burley @ 1997-08-19 16:06 UTC (permalink / raw)
  To: egcs

A longstanding bug in gcc, reported against g77, has been
narrowed down by g77 users enough for me to be able to translate
it into C for purposes of reproduction.  It probably afflicts only
ix86 machines, and shows up on my 486.  The sample program is
enclosed below.

When compiled by `gcc -O2 t.c', then run by `./a.out', it
produces a "Floating point exception" on my i486 running GNU/Linux,
which in this case means it has unexpectedly overflowed the
floating-point ("387 coprocessor") stack.

Looking at the RTL and reading up on reg-stack.c a bit -- not enough
time to figure out the problem and fix it myself right now -- the
problem seems to be that reg-stack.c doesn't recognize that the
second assignment to `xmax', `xmax = 2.0', is to a live register,
which must thus be popped first, then loaded.  Perhaps this is
because reg-stack's own version of flow analysis, or basic block
analysis, doesn't see that `lab100' can be reached from the
`goto *next;' at `lab20', or some such thing.  Anyway, the 1.0 value
in it is not popped before the new 2.0 value is loaded.  After a
few iterations, this leads to the stack overflowing -- though if
that were silent, in this particular case it wouldn't be a problem.

The Fortran equivalent for this code is known as DNRM2, but apparently
a very old version.  In any case, it's Fortran's ugly, but long-ago-
popular, `ASSIGN' and `GOTO variable' features that correspond to
the gcc extensions such as `goto *next;' being used in this example.
They exist in lots of code, so it'd be helpful to have this bug fixed
soon.

If anyone comes up with a fix for this, that'd be great, especially
if it was in the next week or so; it might go into g77-0.5.21, which
is due out September 1.

        tq vm, (burley)


-------- t.c:
#include <stdio.h>

double
dnrm2 (int n, double dx[])
{
  void *next;
  double xmax;
  double rtnval;
  int i;

  next = &&lab100;
  rtnval = 10.0;
  i = 1;
  xmax = 1.0;

lab20:
  goto *next;
lab100:
  next = &&lab110;
  xmax = 2.0;
lab110:
  if (dx[i] > xmax) i = i + 1;
  if (i <= n) goto lab20;
  return rtnval;
}

int
main()
{
  double r[10];
  int n;
  int i;
  double a;

  r[1] = 10.0;
  n = 1;

  for (i = 1; i < 11; ++i)
    {
      a = dnrm2(n, r);
      printf ("a = %g\n", a);
    }
  return 0;
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~1997-08-27  3:29 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1997-08-22 10:48 Building of generated parser files Niklas Hallqvist
1997-08-22 13:28 ` Some Haifa scheduler bugs Bernd Schmidt
1997-08-22 13:53 ` Probable bug in gcc/reg-stack.c (example given) Bernd Schmidt
1997-08-22 13:53 ` reload_cse_regs improvement Bernd Schmidt
  -- strict thread matches above, loose matches on Subject: below --
1997-08-27  3:29 Here are the g++ test results Mumit Khan
1997-08-27  3:29 ` Probable bug in gcc/reg-stack.c (example given) Jeffrey A Law
1997-08-26  2:30 Stan Cox
1997-08-19 16:06 Craig Burley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).