Bug 28574 - [4.2 regression] switch statement points to unreferenced label at -O2
Summary: [4.2 regression] switch statement points to unreferenced label at -O2
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 4.2.0
: P2 normal
Target Milestone: 4.2.0
Assignee: Not yet assigned to anyone
URL:
Keywords: link-failure
Depends on:
Blocks:
 
Reported: 2006-08-02 17:16 UTC by Martin Michlmayr
Modified: 2006-09-20 16:49 UTC (History)
6 users (show)

See Also:
Host: ia64-linux-gnu
Target: ia64-linux-gnu
Build:
Known to work: 4.0.3 4.1.1
Known to fail: 4.2.0
Last reconfirmed: 2006-08-02 19:24:30


Attachments
test case (579 bytes, text/plain)
2006-08-02 17:17 UTC, Martin Michlmayr
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Michlmayr 2006-08-02 17:16:16 UTC
Compiling an application on IA-64, I got errors during linking about undefined references to a label.  This reference is produced by the compiler as part of a switch statement but the label itself is not actually generated.  Note that this looks so much like PR27531 but the patch that fixed that problem on sparc doesn't help on ia64.  I hope though that previous investigation for PR27531 will help solve this problem.  It certainly looks related, although I cannot tell for sure.

tbm@coconut0:~/bugs$ /usr/lib/gcc-snapshot/bin/gcc -O2 yasm-module.c
/usr/lib/crt1.o: In function `_start':
(.text+0x41): undefined reference to `main'
/tmp/ccOFSz00.o: In function `yasm_list_modules':
yasm-module.c:(.text+0x70): undefined reference to `.L11'
yasm-module.c:(.text+0x80): undefined reference to `.L11'
collect2: ld returned 1 exit status
tbm@coconut0:~/bugs$ /usr/lib/gcc-snapshot/bin/gcc -O1 yasm-module.c
/usr/lib/crt1.o: In function `_start':
(.text+0x41): undefined reference to `main'
collect2: ld returned 1 exit status
tbm@coconut0:~/bugs$ gcc-4.1 -O2 yasm-module.c
/usr/lib/gcc/ia64-linux-gnu/4.1.2/../../../crt1.o: In function `_start':
(.text+0x41): undefined reference to `main'
collect2: ld returned 1 exit status
tbm@coconut0:~/bugs$ gcc-4.0 -O2 yasm-module.c
/usr/lib/gcc/ia64-linux-gnu/4.0.4/../../../crt1.o: In function `_start':
(.text+0x41): undefined reference to `main'
collect2: ld returned 1 exit status
tbm@coconut0:~/bugs$
Comment 1 Martin Michlmayr 2006-08-02 17:17:18 UTC
Created attachment 11999 [details]
test case
Comment 2 Steven Bosscher 2006-08-02 19:03:11 UTC
There are a million reasons why labels can disappear in GCC. This happens because GCC deletes or keeps labels based on ref counting (LABEL_NUSES and friends) and this is just too fragile.

The way for you to narrow down the bug is this:

1. Compile with -daAP
2. Look in the .s file which instruction references the missing label. There should be a LABEL_REF with a number.
3. Grep for "code_label.*<number>" in the RTL dumps.  The label should at least appear in the .expand dump.

This will help you find the pass during/after which the label disappears.
Comment 3 Martin Michlmayr 2006-08-02 19:13:22 UTC
Thanks for the explanation!  I see the following two references in the assembler file but I don't see any code_label references to those number in the dumps.

//(insn/c 185 231 30 2 (set (reg/f:DI 14 r14 [386])
//        (plus:DI (high:DI (label_ref:DI 48))
//            (reg:DI 1 r1))) 76 {*load_symptr_high} (nil)
//    (nil))
        addl r14 = @ltoffx(.L11), r1    // 185  *load_symptr_high

and:

//(insn:TI 186 230 229 3 (set (reg/f:DI 14 r14 [386])
//        (lo_sum:DI (reg/f:DI 14 r14 [386])
//            (label_ref:DI 48))) 77 {*load_symptr_low} (nil)
//    (nil))
        ld8.mov r14 = [r14], .L11       // 186  *load_symptr_low
Comment 4 Martin Michlmayr 2006-08-02 19:15:00 UTC
Ah, sorry, looked for the wrong number.  The last pass which has the case_label is:

yasm-module.c.144r.peephole2:(code_label 48 47 49 11 "" [3 uses])
Comment 5 Martin Michlmayr 2006-08-02 19:17:53 UTC
So from 144r (peephole2) to 145r (ce3) it goes from:

;; Insn is not within a basic block
(code_label 48 47 49 11 "" [3 uses])

;; Insn is not within a basic block
(jump_insn 49 48 50 (addr_diff_vec:DI (label_ref:DI 48)
         [
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
        ]
        (const_int 0 [0x0])
        (const_int 0 [0x0])) -1 (nil)
    (nil))

(barrier 50 49 103)


to:

...(insn 185 35 186 3 (set (reg/f:DI 14 r14 [386])
        (plus:DI (high:DI (label_ref:DI 48))
            (reg:DI 1 r1))) 76 {*load_symptr_high} (nil)
    (nil))

(insn 186 185 44 3 (set (reg/f:DI 14 r14 [386])
        (lo_sum:DI (reg/f:DI 14 r14 [386])
            (label_ref:DI 48))) 77 {*load_symptr_low} (nil)
    (nil))

...

and here's where the case_label is lost.
Comment 6 Martin Michlmayr 2006-08-02 19:43:54 UTC
Slightly more reduced testcase:

typedef enum yasm_module_type {
    YASM_MODULE_ARCH = 0,
    YASM_MODULE_DBGFMT,
    YASM_MODULE_OBJFMT,
    YASM_MODULE_LISTFMT,
    YASM_MODULE_OPTIMIZER
} yasm_module_type;

typedef struct yasm_module {
    const char *name;
};

typedef struct yasm_module yasm_arch_module;
typedef struct yasm_module yasm_dbgfmt_module;
typedef struct yasm_module yasm_objfmt_module;
typedef struct yasm_module yasm_listfmt_module;
typedef struct yasm_module yasm_optimizer_module;

typedef struct module {
    void *data;
} module;

static struct {
    module *m;
    int n;
} module_types[] = {
 {},
};

void
yasm_list_modules(yasm_module_type type,
                  void (*printfunc) (const char *name))
{
    int i;
    module *modules = module_types[type].m;
    yasm_arch_module *arch;
    yasm_dbgfmt_module *dbgfmt;
    yasm_objfmt_module *objfmt;
    yasm_listfmt_module *listfmt;
    yasm_optimizer_module *optimizer;

    for (i=0; i<2; i++) {
        switch (type) {
            case YASM_MODULE_ARCH:
                arch = modules[i].data;
                printfunc(arch->name);
                break;
            case YASM_MODULE_DBGFMT:
                dbgfmt = modules[i].data;
                printfunc(dbgfmt->name);
                break;
            case YASM_MODULE_OBJFMT:
                objfmt = modules[i].data;
                printfunc(objfmt->name);
                break;
            case YASM_MODULE_LISTFMT:
                listfmt = modules[i].data;
                printfunc(listfmt->name);
                break;
            case YASM_MODULE_OPTIMIZER:
                optimizer = modules[i].data;
                printfunc(optimizer->name);
        }
    }
}
Comment 7 Steven Bosscher 2006-08-02 20:52:50 UTC
;; Insn is not within a basic block
(code_label 48 47 49 11 "" [3 uses])

;; Insn is not within a basic block
(jump_insn 49 48 50 (addr_diff_vec:DI (label_ref:DI 48)
         [
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
        ]
        (const_int 0 [0x0])
        (const_int 0 [0x0])) -1 (nil)
    (nil))


So we lose a jump table.
Comment 8 Steven Bosscher 2006-08-02 21:10:40 UTC
Happens when we are in find_if_case_1, where we call:

  delete_basic_block (then_bb);

The basic block we try to remove is this one:

;; basic block 5, loop depth 1, count 0
;; prev block 9, next block 6
;; pred:
;; succ:       6 [100.0%]
;; Registers live at start:  12 [r12] 32 [r34] 33 [r35] 34 [r36] 35 [r37] 36 [r38] 37 [r39] 38 [r40] 112 [r32] 113 [r33] 272 [p16] 273 [p17]
(note 129 214 47 5 [bb 5] NOTE_INSN_BASIC_BLOCK)
(jump_insn 47 129 48 5 (parallel [
            (set (pc)
                (reg:DI 326 b6))
            (use (label_ref 48))
        ]) 331 {*tablejump_internal} (nil)
    (expr_list:REG_DEAD (reg:DI 326 b6)
        (nil)))
;; Registers live at end:  12 [r12] 32 [r34] 33 [r35] 34 [r36] 35 [r37] 36 [r38] 37 [r39] 38 [r40] 112 [r32] 113 [r33] 272 [p16] 273 [p17]
$72 = void


But the jump table follows right after this block:
;; Start of basic block 5, registers live: 12 [r12] 32 [r34] 33 [r35] 34 [r36] 35 [r37] 36 [r38] 37 [r39] 38 [r40] 112 [r32] 113 [r33] 272 [p16] 273 [p17]
(note 129 214 47 5 [bb 5] NOTE_INSN_BASIC_BLOCK)

(jump_insn 47 129 48 5 (parallel [
            (set (pc)
                (reg:DI 326 b6))
            (use (label_ref 48))
        ]) 331 {*tablejump_internal} (nil)
    (expr_list:REG_DEAD (reg:DI 326 b6)
        (nil)))
;; End of basic block 5, registers live:
 12 [r12] 32 [r34] 33 [r35] 34 [r36] 35 [r37] 36 [r38] 37 [r39] 38 [r40] 112 [r32] 113 [r33] 272 [p16] 273 [p17]

;; Insn is not within a basic block
(code_label 48 47 49 11 "" [3 uses])

;; Insn is not within a basic block
(jump_insn 49 48 50 (addr_diff_vec:DI (label_ref:DI 48)
         [
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
            (label_ref:DI 103)
        ]
        (const_int 0 [0x0])
        (const_int 0 [0x0])) -1 (nil)
    (nil))

(barrier 50 49 103)


And rtl_delete_block (the cfgrtl hook for delete_basic_block) says:

  /* Include any jump table following the basic block.  */
  end = BB_END (b);
  if (tablejump_p (end, NULL, &tmp))
    end = tmp;

So there we lose.

Part of the problem is (should I say now "as always"?) missing REG_LABEL notes:

(insn 185 35 186 3 (set (reg/f:DI 14 r14 [386])
        (plus:DI (high:DI (label_ref:DI 48))
            (reg:DI 1 r1))) 76 {*load_symptr_high} (nil)
    (nil))

But even with a REG_LABEL note, this label is going to disappear AFAICT because 
can_delete_label_p will return true for it.
Comment 9 Steve Ellcey 2006-09-14 22:37:10 UTC
I don't see any way to delete a block without deleting the attached jumptable so the only fix I can see is to not delete the block in the first place.  The block is being deleted on IA64 because it is a 'then block' on an if statment and the code is being predicated and moved up to the previous block (where the if is). After the instructions are moved/copied we don't need the then block and we delete it, along with the attached jumptable.  I am currently testing a fix/hack that fixes the problem by not allowing us to do the if conversion.  This means we wind up with an explicit jump instead of predicated instructions for this test case.  My proposed patch (still being tested) is:

Index: ifcvt.c
===================================================================
--- ifcvt.c     (revision 116938)
+++ ifcvt.c     (working copy)
@@ -3560,6 +3560,13 @@ dead_or_predicable (basic_block test_bb,
   head = BB_HEAD (merge_bb);
   end = BB_END (merge_bb);

+  /* If merge_bb ends with a tablejump, predicating/moving insn's
+     into test_bb and then deleting merge_bb will result in the jumptable
+     that follows merge_bb being removed along with merge_bb and then we
+     get an unresolved reference to the jumptable.  */
+  if (tablejump_p (end, NULL, NULL))
+    return FALSE;
+
   if (LABEL_P (head))
     head = NEXT_INSN (head);
   if (NOTE_P (head))
Comment 10 Steve Ellcey 2006-09-20 16:41:21 UTC
Subject: Bug 28574

Author: sje
Date: Wed Sep 20 16:41:12 2006
New Revision: 117084

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=117084
Log:
	PR target/28574
	* ifcvt.c (dead_or_predicable): Don't predicate then blocks
	with tablejumps in them.

Added:
    trunk/gcc/testsuite/gcc.dg/pr28574.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/ifcvt.c

Comment 11 Steve Ellcey 2006-09-20 16:49:26 UTC
Fix is now checked in.