RFC -- More problems with disappearing labels
Jeffrey A Law
law@cygnus.com
Mon May 14 09:45:00 GMT 2001
Compile the following on the PPC with -O2:
static void
foo ()
{
long maplength;
int type;
{
const long nibbles = 8;
char buf1[nibbles + 1];
char buf2[nibbles + 1];
char buf3[nibbles + 1];
((nibbles) <= 16
? (({
void *__s = (buf2);
union
{
unsigned int __ui;
unsigned short int __usi;
unsigned char __uc;
} *__u;
unsigned char __c = (unsigned char)('0');
switch ((unsigned int) (nibbles))
{
case 16:
__u->__ui = __c * 0x01010101;
__u = __extension__ ((void *) __u + 4);
case 12:
__u->__ui = __c * 0x01010101;
__u = __extension__ ((void *) __u + 4);
case 8:
__u->__ui = __c * 0x01010101;
__u = __extension__ ((void *) __u + 4);
case 4:
__u->__ui = __c * 0x01010101;
case 0:
break;
}
__s;
}))
: 0);
}
}
>From the .loop dump we have:
(insn 122 139 124 (set (reg/f:SI 79)
(label_ref:SI 127)) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 127 (expr_list:REG_EQUAL (label_ref:SI 127)
(nil))))
(insn 124 122 125 (set (reg:SI 80)
(mem/u:SI (plus:SI (mult:SI (reg/v/u:SI 44)
(const_int 4 [0x4]))
(reg/f:SI 79)) 0)) 35 {*movsi_1} (nil)
(expr_list:REG_EQUAL (mem/u:SI (plus:SI (mult:SI (const_int 4 [0x4])
(const_int 8 [0x8]))
(reg/f:SI 79)) 0)
(nil)))
(jump_insn 125 124 126 (parallel[
(set (pc)
(reg:SI 80))
(use (label_ref 127))
] ) 328 {tablejump} (nil)
(nil))
(barrier 126 125 127)
(code_label 127 126 128 8 "" "" [2 uses])
(jump_insn 128 127 129 (addr_vec:SI[
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 97)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 75)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 53)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 31)
] ) -1 (nil)
(nil))
Note carefully the REG_EQUAL note on insn 124. It is what is going to enable
cse2 to realize that this sequence will always jump to (label_ref:SI 75).
After CSE completes (but before we re-run the jump optimizer) we get the
following:
(insn 122 139 124 (set (reg/f:SI 79)
(label_ref:SI 127)) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 127 (expr_list:REG_EQUAL (label_ref:SI 127)
(nil))))
(insn 124 122 152 (set (reg:SI 80)
(label_ref:SI 75)) 35 {*movsi_1} (nil)
(expr_list:REG_EQUAL (label_ref:SI 75)
(nil)))
(jump_insn 152 124 153 (set (pc)
(label_ref 75)) -1 (nil)
(nil))
(barrier 153 152 125)
(jump_insn 125 153 126 (parallel[
(set (pc)
(label_ref:SI 75))
(use (label_ref 127))
] ) 328 {tablejump} (nil)
(nil))
(barrier 126 125 127)
(code_label 127 126 128 8 "" "" [2 uses])
(jump_insn 128 127 129 (addr_vec:SI[
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 97)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 75)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 53)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 130)
(label_ref:SI 31)
] ) -1 (nil)
(nil))
(barrier 129 128 31)
The sequence above is not ideal, but it's still correct and valid. After
jump.c cleans things up a little we get the following:
(insn 122 139 124 (set (reg/f:SI 79)
(label_ref:SI 127)) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 127 (expr_list:REG_EQUAL (label_ref:SI 127)
(nil))))
(insn 124 122 152 (set (reg:SI 80)
(label_ref:SI 75)) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 75 (expr_list:REG_EQUAL (label_ref:SI 75)
(nil))))
(jump_insn 152 124 153 (set (pc)
(label_ref 75)) -1 (nil)
(nil))
(barrier 153 152 127)
(code_label 127 153 140 8 "" "" [1 uses])
It's still not ideal since we would have liked to have zapped insn 122, since
it's only purpose is setup for the tablejump. But regardless, this sequence
is still valid.
A short while later we build the cfg. Before simplification the CFG looks
like this:
5 basic blocks, 7 edges.
Basic block 0: first insn 138, last 120, loop_depth 0, count 0.
Predecessors: ENTRY (fallthru)
Successors: 1 (fallthru) 4 (crit)
Registers live at start: (nil)
Registers live at end: (nil)
Basic block 1: first insn 139, last 152, loop_depth 0, count 0.
Predecessors: 0 (fallthru)
Successors: 3
Registers live at start: (nil)
Registers live at end: (nil)
Basic block 2: first insn 127, last 72, loop_depth 0, count 0.
Predecessors:
Successors: 3 (fallthru)
Registers live at start: (nil)
Registers live at end: (nil)
Basic block 3: first insn 75, last 113, loop_depth 0, count 0.
Predecessors: 2 (fallthru) 1
Successors: 4 (fallthru)
Registers live at start: (nil)
Registers live at end: (nil)
Basic block 4: first insn 130, last 144, loop_depth 0, count 0.
Predecessors: 3 (fallthru) 0 (crit)
Successors: EXIT (fallthru)
Registers live at start: (nil)
Registers live at end: (nil)
The first thing to note is that block 2 is unreachable, it will be removed.
Doing so leaves us with:
(insn 122 139 124 (set (reg/f:SI 79)
(label_ref:SI [127 deleted])) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 127 (expr_list:REG_EQUAL (label_ref:SI [127 deleted])
(nil))))
(insn 124 122 127 (set (reg/f:SI 80)
(label_ref:SI 75)) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 75 (expr_list:REG_EQUAL (label_ref:SI 75)
(nil))))
(note 127 124 32 "" NOTE_INSN_DELETED_LABEL 8)
(note 32 127 48 ("j.c") 24 -1347440721)
(note 48 32 54 ("j.c") 25 -1347440721)
(note 54 48 70 ("j.c") 27 -1347440721)
(note 70 54 75 ("j.c") 28 -1347440721)
(code_label 75 70 142 5 "" "" [2 uses])
(note 142 75 76 [bb 2] NOTE_INSN_BASIC_BLOCK -1347440721)
Note how we have a reference to a deleted label at insn 122. I think this
is the first point at which something is really wrong.
It almost seems to me that when delete_unreachable_blocks removes a label
that we should ensure no dangling REG_LABEL fields are hanging around.
Anyway, let's pretend that's not a problem right now and move on to
After block merging we have the following:
(insn 122 139 124 (set (reg/f:SI 79)
(label_ref:SI [127 deleted])) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 127 (expr_list:REG_EQUAL (label_ref:SI [127 deleted])
(nil))))
(insn 124 122 127 (set (reg/f:SI 80)
(label_ref:SI [75 deleted])) 35 {*movsi_1} (nil)
(insn_list:REG_LABEL 75 (expr_list:REG_EQUAL (label_ref:SI [75 deleted])
(nil))))
(note 127 124 32 "" NOTE_INSN_DELETED_LABEL 8)
(note 32 127 48 ("j.c") 24 -1347440721)
(note 48 32 54 ("j.c") 25 -1347440721)
(note 54 48 70 ("j.c") 27 -1347440721)
(note 70 54 75 ("j.c") 28 -1347440721)
(note 75 70 76 "" NOTE_INSN_DELETED_LABEL 5)
Notice how block merging has created a dangling REG_LABEL reference. Ugh.
The process is similar, we tie together two blocks because the CFG says it
is useful to do so, in the process we zap a label that happens to have a
reference via a REG_LABEL note.
That made me think that maybe we should hook in the code which actually
deletes the CODE_LABELs. However, it seems rather expensive to have to
search through the insn stream for a REG_LABEL note every time we delete
a CODE_LABEL.
Another approach would be to defer the cleanup a little so that we can
do it all in a single pass over the insn stream.
If we deferred cleaup until life analysis, then we could actually verify
that when we find a dangling REG_LABEL note that the destination of set in
the insn with the REG_LABEL note is never used.
I'm open to all three suggestions.
jeff
More information about the Gcc
mailing list