[gnu.gcc.bug] switch statement with unsigned casting bug

satz@CISCO.COM (01/04/89)

We have a single piece of code which has given compilers from many
different types of systems fits. Apparently the case table generation code
was enhanced since gcc 1.30, the last release we used. gcc 1.32 on a Sun
3/280 (68020) running SunOS 3.5 tries to structure the case table into a
tree of conditionals so the cost of any item in the table was o(log n).
gcc 1.30 just tested for each value which for the end of list was o(n).
Unfortunately it seems that the unsignedness of the value was overlooked or
broken.

long test (unsigned short arg)
{
    switch ((unsigned int)arg) {    /* removing this cast makes it work too */
	case 0x0800: return(1);
	case 0x0806: return(2);
	case 0x0200: return(3);
	case 0x0201: return(4);
	case 0x8035: return(5);
	case 0x0804: return(6);
	case 0x6003: return(7);
	case 0x9000: return(8);
	case 0x0600: return(9);
	case 0x8038: return(10);
	default: return(-1);
	case 0:	     return(-2);
    }
}

main()
{
	printf("result = %d\n",test(0x9000));
}

Will return -1 when it should return 8. The following assembler is
generated with -O:

#NO_APP
gcc_compiled.:
.text
	.even
.globl _test
_test:
	link a6,#0
	movew a6@(10),d0
	cmpw #2052,d0
	jeq L8
	jgt L16
	cmpw #513,d0
	jeq L6
	jgt L17
	tstw d0
	jeq L14
	cmpw #512,d0
	jeq L5
	jra L13
L17:
	cmpw #1536,d0
	jeq L11
	cmpw #2048,d0
	jeq L3
	jra L13
L16:
	cmpw #32821,d0
	jeq L7
	jgt L18
	cmpw #2054,d0
	jeq L4
	cmpw #24579,d0
	jeq L9
	jra L13
L18:
	cmpw #32824,d0
	jeq L12
	cmpw #36864,d0
	jeq L10
	jra L13
L3:
	moveq #1,d0
	jra L1
L4:
	moveq #2,d0
	jra L1
L5:
	moveq #3,d0
	jra L1
L6:
	moveq #4,d0
	jra L1
L7:
	moveq #5,d0
	jra L1
L8:
	moveq #6,d0
	jra L1
L9:
	moveq #7,d0
	jra L1
L10:
	moveq #8,d0
	jra L1
L11:
	moveq #9,d0
	jra L1
L12:
	moveq #10,d0
	jra L1
L13:
	moveq #-1,d0
	jra L1
L14:
	moveq #-2,d0
L1:
	unlk a6
	rts
LC0:
	.ascii "result = %d\12\0"
	.even
.globl _main
_main:
	link a6,#0
	movel #36864,sp@-
	jbsr _test
	movel d0,sp@-
	pea LC0
	jbsr _printf
	unlk a6
	rts

Note that 0x9000 has the sign bit set for unsigned short values and that
the jump/test instructions aren't ignoring the sign bit. Modifying the
variable used in the switch statement to be unsigned long is the
work-around we used.


PS. gcc-1.30 generated the following code with -O:

#NO_APP
.text
	.even
.globl _test
_test:
	link a6,#0
	movew a6@(10),d0
	jeq L14
	cmpw #32824,d0
	jeq L12
	cmpw #1536,d0
	jeq L11
	cmpw #36864,d0
	jeq L10
	cmpw #24579,d0
	jeq L9
	cmpw #2052,d0
	jeq L8
	cmpw #32821,d0
	jeq L7
	cmpw #513,d0
	jeq L6
	cmpw #512,d0
	jeq L5
	cmpw #2054,d0
	jeq L4
	cmpw #2048,d0
	jne L13
	moveq #1,d0
	jra L1
L4:
	moveq #2,d0
	jra L1
L5:
	moveq #3,d0
	jra L1
L6:
	moveq #4,d0
	jra L1
L7:
	moveq #5,d0
	jra L1
L8:
	moveq #6,d0
	jra L1
L9:
	moveq #7,d0
	jra L1
L10:
	moveq #8,d0
	jra L1
L11:
	moveq #9,d0
	jra L1
L12:
	moveq #10,d0
	jra L1
L13:
	moveq #-1,d0
	jra L1
L14:
	moveq #-2,d0
L1:
	unlk a6
	rts
LC0:
	.ascii "result = %d\12\0"
	.even
.globl _main
_main:
	link a6,#0
	movel #36864,sp@-
	jbsr _test
	movel d0,sp@-
	pea LC0
	jbsr _printf
	unlk a6
	rts