Thursday, September 26, 2013

Moving on, there is a missing typecast

unsigned char Round;
#define VDPWD *((volatile char*)0x8C00)
void modulo()
{
  VDPWD = '0' + Round % 10;
}

This compiles to:

modulo
    movb @Round, r1    * get the variable into R1 MSB
    srl  r1, 8         * move to LSB
    mov  r1, r2        * copy to R2
    clr  r1            * zero R1 (32-bit value is now >000000xx)
    li   r3, >A        * load R3 with 10 (divisor)
    div  r3, r1        * do the division (R1 has dividend, R2 has remainder)
    mov  r2, r1        * copy R2 to R1 - however, remainder was 16 bit! We are about to treat it like 8-bit.
* Missing "swpb r1" here
    ai   r1, >3000     * add '0' as an 8-bit byte value - this is wrong <--- br="">    movb r1, @>8C00    * move the result to the video chip

Looking through the RTL, we find this sequence in 131r.initvals:

(insn 9 8 10 3 tursi5.c:6 (set (reg:QI 21 [ D.1197 ])
        (plus:QI (subreg:QI (reg:HI 25) 1)
            (const_int 48 [0x30]))) -1 (nil))

In assembly, this would be:

swpb r1  * <-- ah="" br="" ha="" instruction="" is="" missing="" our="" this="">ai r1, >3000

Somewhere in step 172, register allocation, the type conversion gets lost

(insn 9 8 11 2 tursi5.c:6 (set (reg:QI 1 r1 [orig:21 D.1197 ] [21])
        (plus:QI (reg:QI 1 r1 [orig:21 D.1197 ] [21])
            (const_int 48 [0x30]))) 59 {addqi3} (nil))

In assembly, this would just be:

ai r1, >3000

Poop, we've found yet another case where GCC assumes that no effort is required to switch between 8-bit and 16-bit values. In order to fix this, we're going to have to dig deep into the guts.

No comments:

Post a Comment