From: Bruce_Martin@next.com (Bruce Martin)
Subject: GCC 2.2.2 bug in register allocation
Organization: GNUs Not Usenet
Distribution: gnu
Date: Sat, 25 Jul 1992 07:45:33 GMT

The following code will cause the GCC 2.2.2 C compiler to generate
incorrect assembler output when targeted for 'i386-mach'.  The
register constraints specified in the asm() statements are not
obeyed during register allocation, even though there is no obvious
reason why they should be ignored.

GCC Target: i386-mach (should fail on any i386 target)
Compile-time flags: -O1
Version: 2.2.2 or ss-920724


Example code:

struct  mumble {
        char                    *is_nic;
        int                      is_fifo_depth;
        char                    *is_card;
        int                      is_board_id;
} mumble[1];

static inline void
outb(unsigned short port, unsigned char data)
{
    /* Must use %eax and %edx registers */
    asm volatile("outb %1,%0"
        :
        : "d" (port), "a" (data));
}

int
doit(unit)
int unit;
{
        struct mumble   *is     = &mumble[unit];
        char *           base   = is->is_nic + 0x10;
        int              temp;
        int              count  = 0;
                                         

        while (foobar() && count < 10000)
                count++;
        if (count == 10000) {
                printf("%s%d: doit(): would not reset.\n",
                        is->is_card, unit);
        }
                                         

        temp = ((is->is_fifo_depth & 0x0c) << 3) | 0x08;    

        if (is->is_board_id & 0x00080000)
                temp |= 0x01;               


        outb (base+0x0E, temp);
/* the following statement fails to compile correctly */
        outb (base+0x0D, 0);

        return 1;
}




When compiled -O1, the last call to the 'outb' inline function is
generated incorrectly.  outb() requires the use of the %eax and %edx
registers explicitly.  Note: When this code is compiled, the
compiler generates warnings about the loss of information due to a
very bogus cast (a char * is cast to a short implicitly). However,
no errors are generated, and I believe that the compiler should
generate reasonable code in this situation (i.e. it should just
truncate the value).


Here is the last bit of assember output generated by the
ss-920724 C compiler:

L6:
        leal 14(%esi),%ecx
        movl %ecx,%edx
        movl %edi,%eax
/APP
        outb %al,%dx		/ next-to-last outb() call
/NO_APP
        leal 13(%esi),%ecx
        xorb %al,%al		/ note lack of %edx load
/APP
        outb %al,%dx		/ last outb() call, which fails because
				/ %dx has not been loaded.
/NO_APP
        movl $1,%eax
        leal -16(%ebp),%esp
        popl %ebx
        popl %esi
        popl %edi
        leave
        ret


A comparison between the 'lreg' and 'greg' stage RTL output for this last
outb() call are interesting.

(note 85 82 88 "" NOTE_INSN_BLOCK_BEG)

(note 88 85 89 "" NOTE_INSN_DELETED)

(insn 89 88 91 (set (subreg:SI (reg:HI 36) 0)
       (plus:SI (reg/v:SI 24)
           (const_int 13))) 73 {addsi3} (nil)
   (expr_list:REG_DEAD (reg/v:SI 24)
       (nil)))

(insn/i 91 89 93 (asm_operands/v ("outb %1,%0") ("") 0[ 

           (reg:HI 36)
           (const_int 0)
       ] 

       [ 

           (asm_input:HI ("d"))
           (asm_input:QI ("a"))
       ]   
("/Net/lono/users/bmartin/Projects/CC_LONO/cc_lono/gcc-2.2.2/bkm/in.c")  
14) -1 (insn_list 89 (nil))
   (expr_list:REG_DEAD (reg:HI 36)
       (nil)))

(note 93 91 97 "" NOTE_INSN_BLOCK_END)



Output from the .greg stage:

(note 85 82 88 "" NOTE_INSN_BLOCK_BEG)

(note 88 85 89 "" NOTE_INSN_DELETED)

(insn 89 88 121 (set (subreg:SI (reg:HI 2) 0)
       (plus:SI (reg/v:SI 4)
           (const_int 13))) 73 {addsi3} (nil)
   (expr_list:REG_DEAD (reg/v:SI 4)
       (nil)))

(insn 121 89 91 (set (reg:QI 0)
       (const_int 0)) 39 {movqi} (nil)
   (nil))

(insn/i:HI 91 121 93 (asm_operands/v ("outb %1,%0") ("") 0[ 

           (reg:HI 1)
           (reg:QI 0)
       ] 

       [ 

           (asm_input:HI ("d"))
           (asm_input:QI ("a"))
       ]   
("/Net/lono/users/bmartin/Projects/CC_LONO/cc_lono/gcc-2.2.2/bkm/in.c"
) 14) -1 (insn_list 89 (nil))
   (expr_list:REG_DEAD (reg:HI 2)
       (nil)))

(note 93 91 97 "" NOTE_INSN_BLOCK_END)


The number 36 psuedo register does not seem to have been assigned
consistently in this pass.  At Insn 89 it is assigned hard register
2, but at insn 91 it is assigned hard reg 1.

Gcc bug or misuse of asm() statements?

				...bruce

Newsgroups: gnu.gcc.bug