[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avr-gcc-list] "Mixed" Assembly Listings (and Degenerate I/O Code Exampl

From: Bruce D. Lightner
Subject: [avr-gcc-list] "Mixed" Assembly Listings (and Degenerate I/O Code Examples)
Date: Fri, 06 Oct 2006 12:23:37 -0700
User-agent: Thunderbird (Windows/20060909)

Tom Deutschman wrote:
For your reference, here are links to PDFs of assembler listing and map file comparisons from your example source when compiled with avr-gcc 3.4.6 and 4.1.1 using your specified compiler options (plus those required to generate the assembly listing and map files.)


Does anyone know how to include high level source in assembly listings? I would have thought that the -Wa,-ahls compiler option to the assembler would do this, but it does not.

This has been reported as a "bug" in the GNU assembler in the past, but in fact it is caused by "pilot error".  It seems that the GNU assembler needs "stabs" debug information in order to extract source code information for the purposes of inserting it "in-line" with the machine code in the "avr-gcc" assembly listing.

Here's what I use in my "Makefiles"...

  %.o : %.c
        $(CC) $(CFLAGS) $(D) -c -gstabs -Wa,-ahlmsd=$*.lst -o $@ $<

Note the "-gstabs".

The sample "Makfile" shipped with WinAVR was altered at some point to re-define "DEBUG" to "dwarf-2" instead of "stabs".  This "broke" the in-line source code feature in the resultant assembly listing files.  This is the likely the source of the confusion that followed.

The ability to see the AVR machine code "in context" with the source code is an *essential* feature of any compiler "tool kit" used for embedded programming.  Sometimes compilers (and especially "avr-gcc") do some pretty stupid things!

An example of why more people should be "watching" the "avr-gcc" code generator are these three simple C routines which test a bit in one of the AVR microcontroller I/O ports.  Because of the AVR instruction set's clever "sbic" and "sbis" instructions, the machine code for tests like these should be short...and very fast.  However, with modern versions of "avr-gcc" (i.e., 3.x.x and 4.x.x) "your mileage may vary from that shown on the window sticker"!

#include <avr/io.h>

#define J1708_IN_PORT  PORTD
#define J1708_IN_DDR   DDRD
#define J1708_IN_PIN   PIND
#define J1708_IN_BIT   3

  int test1(void)
    unsigned char bit = (J1708_IN_PIN & (1 << J1708_IN_BIT)) ? 1 : 0;
    return bit;

  int test2(void) {
   if (!(J1708_IN_PIN & (1 << J1708_IN_BIT))) return 0;
   return 1;

  int test3(void) {
   if (!(J1708_IN_PIN & (1 << J1708_IN_BIT))) return 1;
   return 0;

...compiled with avr-gcc 3.4.6:

  C:/WinAVR_20060421/bin/avr-gcc -mmcu=atmega16 -Os \
          -mcall-prologues -Wall -c \
          -gstabs -Wa,-ahlmsd=x.lst -o test.o test.c

...gives us horrible code for all except "test3()":

  **** int test1(void)
  **** {
  ****     unsigned char bit = (J1708_IN_PIN & (1 << J1708_IN_BIT)) ? 1 : 0;
                in r24,48-0x20
                clr r25
                ldi r18,3
        1:      lsr r25
                ror r24
                dec r18
                brne 1b
                andi r24,lo8(1)
  ****     return bit;
  **** }
                clr r25

  **** int test2(void) {
  ****    if (!(J1708_IN_PIN & (1 << J1708_IN_BIT))) return 0;
                in r24,48-0x20
                clr r25
                movw r18,r24
                andi r18,lo8(8)
                andi r19,hi8(8)
                sbrc r24,3
                rjmp .L3
                movw r24,r18
  ****    return 1;
                ldi r24,lo8(1)
                ldi r25,hi8(1)
  **** }

  **** int test3(void) {
  ****    if (!(J1708_IN_PIN & (1 << J1708_IN_BIT))) return 1;
                sbic 48-0x20,3
                rjmp .L5
                ldi r24,lo8(1)
                ldi r25,hi8(1)
  ****    return 0;
                ldi r24,lo8(0)
                ldi r25,hi8(0)
  **** }

The bit-shift "loop" code generated for "test1()" is really "special". :-)    (Yes, that's 5 clocks per bit shift!)

I was lucky that my application only referenced bit 3 in the I/O port.  If the hardware had used bit 6, the routine would take twice as long to execute...over 33 clocks for an I/O bit test that should have taken just a couple of clocks!

Note that the only difference between "test2()" and "test3()" is the return value (i.e., 0 vs. 1).  Experiments show that returning "0" is what triggers the "bad behavior".  This bad example takes 6 AVR instructions to do the work of one "sbic" instruction.

This kind of "optimized" machine code makes me miss the code generator logic in "avr-gcc" v2.95...and the v2.95 I/O macros like "sbi()", "cbi()", "bit_is_set()" and "bit_is_clear()" which always did the right thing!  (Eric/Joerg, please don't "flame" me...again!) :-)

So, if you are doing any time-critical checking of I/O bits using "avr-gcc", beware!  Regular inspection of a "mixed listings" from the GNU assembler is essential!  In many real-time embedded applications, code this bad means that your application is "broken".

BTW: Here's my "cheap fix" for the "test2()" example:

  #define _BYTE(ch) ({                    \
        uint8_t t;                        \
        asm volatile (                    \
                "ldi %0, %1"              \
                : "=d" (t)                \
                : "M" ((uint8_t)(ch))     \
        );                                \
        t;                                \

  int test2(void) {
   if (!(J1708_IN_PIN & (1 << J1708_IN_BIT))) return _BYTE(0);
   return 1;

...which eliminate the degenerative behavior when trying to "return 0" as part of an I/O port bit check.

So, does anyone know if "avr-gcc 4.x.x" just as "special"?

Maybe an automatic code-generation "regression test" for this is needed, assuming that will get the problem fixed in a future "avr-gcc" release?   This particular code generation bug has been in "avr-gcc" for literally years now!  I'd be happy to provide some test code, if the idea of "regression testing" of the "avr-gcc" code generator is even a "concept".  (Yes, I know it is a "hard problem", but look what "slips through the cracks"!)

Or,maybe it's time to bring back *real* in-line assembly macros for "sbi()", "cbi()", "bit_is_set()" and "bit_is_clear()"!  The compiler clearly doesn't get it right all the time when it comes to critical I/O code, and when it's wrong, it is *really* bad!

Best regards,

 Bruce D. Lightner
 Lightner Engineering
 La Jolla, California
 Voice: +1-858-551-4011
 FAX: +1-858-551-0777
 Email: address@hidden
 URL: http://www.lightner.net/lightner/bruce/

reply via email to

[Prev in Thread] Current Thread [Next in Thread]