------- Comment #4 from wilson at gcc dot gnu dot org 2006-06-27 03:02 ------- This only fails for --enable-checking builds, which is the default for mainline, but not release branches. I was able to reproduce this with gcc-4.0, but not gcc-3.4. The difference between the two is that gcc-4.0 has the __builtin_copysign support, which emits the RTL that triggers the error. The error has probably been latent for a while.
The insn that causes the problem is (jump_insn 99 98 113 15 (set (pc) (if_then_else (ge:SI (subreg:SI (reg:DF 32 $f0) 4) (const_int 0 [0x0])) (label_ref 101) (pc))) 261 {*branch_zerosi} (insn_list:REG_DEP_TRUE 94 (nil)) (expr_list:REG_DEAD (reg:SI 33 $f1) (expr_list:REG_BR_PROB (const_int 5000 [0x1388]) (nil)))) On a 32-bit mips target, DFmode $f0 is a register pair $f0/$f1, so technically this is correct. There is a corresponding REG_UNUSED for $f0 on the insn that sets (reg:DF 32 $f0). However, flow doesn't track individual regs in subregs, it only tracks the whole subreg. Note that mark_used_reg ignores subregs. So when we execute the second life pass, we end up with (jump_insn 98 97 112 17 (set (pc) (if_then_else (ge (subreg:SI (reg:DF 32 $f0) 4) (const_int 0 [0x0])) (label_ref 100) (pc))) 271 {*branch_ordersi} (insn_list:REG_DEP_TRUE 93 (nil)) (expr_list:REG_DEAD (reg:DF 32 $f0) (expr_list:REG_DEAD (reg:SI 33 $f1) (expr_list:REG_BR_PROB (const_int 5000 [0x1388]) (nil))))) Note that we now have two overlapping REG_DEAD notes plus an overlapping REG_UNUSED note on a previous insn. When sched runs, it deletes both REG_DEAD notes, but only readds one, resulting in the abort for a REG_DEAD note consistency problem. A subreg of a hard register is normally not allowed, but it is created in this case because CANNOT_CHANGE_CLASS_MODE is defined, and HARD_REGNO_MODE_OK says that an SImode $f1 is not OK. The result is that simplify_subreg doesn't simplify this. The other part is that register_operand says it is OK. Eventually, this gets fixed by reload. Fixing combine to get this right looks complicated. Combine has to know that the register was used inside a subreg, and then figure out that the subreg wasn't simplified because of CANNOT_CHANGE_CLASS_MODE, etc. I think a simpler solution here is to note that the life2 pass would have worked correctly if it deleted all prior REG_UNUSED/REG_DEAD notes before it started. Incidentally, the comments for the life2 pass say it was explicitly added to fix REG_UNUSED/REG_DEAD problems with distribute_notes in combine, so it was apparently added to fix a related problem. It just isn't working the way it was originally intended. It is curious that life2 is running immediately before sched, instead of immediately after combine. If we ran it immediately after combine, we could get rid of the REG_DEAD/REG_UNUSED support in distribute_notes, which is probably the most complicated, and most buggy, part of combine. This would also speed up the compiler a little bit. -- wilson at gcc dot gnu dot org changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |wilson at gcc dot gnu dot | |org Status|UNCONFIRMED |NEW Ever Confirmed|0 |1 Last reconfirmed|0000-00-00 00:00:00 |2006-06-27 03:02:40 date| | http://gcc.gnu.org/bugzilla/show_bug.cgi?id=27883