arm-coff-gcc function prologue w/ unsigned short parameter

The Chazman
Tue May 9 21:37:00 GMT 2000

We've got both gcc 2.95.2 and egcs 2.91.57 compiling for an arm-coff target,
and both are exhibiting an identical strange behavior in some cases with
unsigned short parameters to functions and unsigned short local variables.
Unfortunately, I can only find one brief code snippet that clearly
exhibits the problem (others are long and complicated), and it uses inline
assembly.  But I'll post it anyway, hoping that the inline assembly is
straightforward enough.  It crops up other places that are pure ANSI C, so
I don't think the use of assembly is causing the problem.

pcx09> cat testus2.c
#include <stdio.h>

static unsigned short
swabw(unsigned short x)
    __asm__("orr %0, %0, %0, lsl #16 ; mov %0, %0, lsr #8" : "+r" (x) );
    return x & 0xffff;

testus2(unsigned short x)
    printf("x = 0x%04x, swabbed = 0x%04x\r\n", x, swabw(x));


The swabw function swaps bytes within a short.  Normally we inline that
function, but when we don't, we get this output from runs through the
testus2() function...

x = 0x1234, swabbed = 0x3412
x = 0x789a, swabbed = 0x9a78
x = 0x89ab, swabbed = 0xff89

In general, the failure mode is that whenever bit 15 is set in the input
to swabw, the output comes out with bits 15-8 all set regardless of what
bits 7-0 of the input were.

Again, when we inline swabw, this does not happen, as the bug appears in
the function prologue.  Here's the assembly generated by this source file...

pcx09> odarm -d testus2.o

testus2.o:     file format coff-arm-big

Disassembly of section .text:

00000000 <LM1>:
   0:   e1a0c00d        mov     ip, sp
   4:   e92dd800        stmdb   sp!, {fp, ip, lr, pc}
   8:   e24cb004        sub     fp, ip, #4
   c:   e1a00800        mov     r0, r0, lsl #16
  10:   e1a00840        mov     r0, r0, asr #16

00000014 <LM2>:
  14:   e1800800        orr     r0, r0, r0, lsl #16
  18:   e1a00420        mov     r0, r0, lsr #8

0000001c <LM3>:
  1c:   e1a00800        mov     r0, r0, lsl #16
  20:   e1a00820        mov     r0, r0, lsr #16
  24:   e91ba800        ldmdb   fp, {fp, sp, pc}

00000028 <_testus2>:
  28:   e1a0c00d        mov     ip, sp
  2c:   e92dd810        stmdb   sp!, {r4, fp, ip, lr, pc}
  30:   e24cb004        sub     fp, ip, #4
  34:   e1a04800        mov     r4, r0, lsl #16
  38:   e1a04824        mov     r4, r4, lsr #16
  3c:   e1a00004        mov     r0, r4
  40:   ebffffee        bl      0 <LM1>
  44:   e1a02800        mov     r2, r0, lsl #16
  48:   e1a01004        mov     r1, r4
  4c:   e59f0008        ldr     r0, 5c <L4>
  50:   e1a02822        mov     r2, r2, lsr #16
  54:   ebfffffe        bl      54 <_testus2+0x2c>

00000058 <LM6>:
  58:   e91ba810        ldmdb   fp, {r4, fp, sp, pc}

0000005c <L4>:
  5c:   00000000        andeq   r0, r0, r0

The bug is at offset 0x10 into the disassembly.  The parameter is
declared unsigned short, which should generate a mov r0, r0, lsr #16
instruction as it does elsewhere in the file.  But an arithmetic shift
is generated instead of a logical one, which, considering the value
signed, fills the top 16 bits with ones if bit 15 is set.  This is
the cause of the errant behavior we see with our printfs.

Has anyone seen this behavior before, erroneously treating unsigned
shorts as signed while zeroing out their top 16 bits before using them?
Is there an explanation for it?  Should I report it as a gcc bug?

Thanks for your help...

Carl Miller               Firmware Engineer              Gordian, Inc.
(714) 850-0205

(posted from my personal e-mail address since that's the one that's
subscribed to the list)

Want more information?  See the CrossGCC FAQ,
Want to unsubscribe? Send a note to

More information about the crossgcc mailing list