This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Linker placing code beyond space limit. 21 bytes of code, 2KB space

int main()
{
  for(;;)
  {
    P1_6 = 0;
    P1_6 = 1;
  }
}

Tried LX51 link with: (SMALL / SMALL)

TO "Clock"
REGFILE (.\Clock.ORC)
RESERVE (C:0x0800-C:0x7FFF)

Result:

Build target 'Target 1'
compiling main.c...
linking...
Program Size: data=9.0 xdata=0 const=0 code=21
creating hex file from "Clock"...
"Clock" - 0 Error(s), 0 Warning(s).
HEX output:
:030000000280007B
:10800000787FE4F6D8FD75810702800CC296D2967F
:0280100080FAF4
:00000001FF

Target device: AT89LP214. Has 2KB of code space, which ends at 0x07FF.
Everything I try sticks the code at 0x0800 or higher.

I'm going to be sad if I have to link code manually every time I compile.

I've tried not reserving any code space at all; linker sticks it at 0x0800.
I've tried setting the target device to AT89LP214 and letting keil figure things out. Sticks it at 0x0800 with the LX51 linker, doesn't compile with BL51.
I've tried changing memory models, code rom size models, same result. 0x0800 with LX51, doesn't compile with BL51. Tried checking and unchecking "Use On-Chip ROM (0x0-0x7FF)".

Using uVision V4.60.6.10, Toolchain PK51 9.51, C51 v9.51, A51 V8.02b, BL51 6.22, LX51 V4.64.0.0

Parents
  • I won't say much about the Keil IDE - the editor is definitely a bit lacking. And their testing have been lacking recently - too many easy-to-catch bugs slipping out. Mostly related to the IDE, but also some compiler goofs.

    But any compiler will always produce suboptimal code compared to what the best assembly programmers can manage. But the Keil compilers are normally producing less suboptimal code that most of the competition.

    So what exactly are you complaining about, regarding the Keil code generation?

Reply
  • I won't say much about the Keil IDE - the editor is definitely a bit lacking. And their testing have been lacking recently - too many easy-to-catch bugs slipping out. Mostly related to the IDE, but also some compiler goofs.

    But any compiler will always produce suboptimal code compared to what the best assembly programmers can manage. But the Keil compilers are normally producing less suboptimal code that most of the competition.

    So what exactly are you complaining about, regarding the Keil code generation?

Children
  • For example, on max "optimizations":

    void transmit(char bar)
    {
    }
    
    void calls_transmit(long arg)
    {
            transmit((arg & 0xFF000000) >> 24);
            transmit((arg & 0xFF0000) >> 16);
            transmit((arg & 0xFF00) >> 8);
            transmit((arg & 0xFF));
    }
    
    int main()
    {
            long arg = 0xAABBCCDD;
    
            calls_transmit(arg);
    
            return 0;
    }
    

    compiles to:

                 ; FUNCTION _transmit (BEGIN)
    8F00        R     MOV     bar,R7
    22                RET
                 ; FUNCTION _transmit (END)
    
                 ; FUNCTION _calls_transmit (BEGIN)
    8F00        R     MOV     arg+03H,R7
    8E00        R     MOV     arg+02H,R6
    8D00        R     MOV     arg+01H,R5
    8C00        R     MOV     arg,R4
    
    E4                CLR     A
    FF                MOV     R7,A
    FE                MOV     R6,A
    FD                MOV     R5,A
    E500        R     MOV     A,arg
    7818              MOV     R0,#018H
    1100        E     ACALL   ?C?ULSHR
    1100        R     ACALL   _transmit
    
    E4                CLR     A
    FF                MOV     R7,A
    FE                MOV     R6,A
    E500        R     MOV     A,arg+01H
    FD                MOV     R5,A
    E4                CLR     A
    FC                MOV     R4,A
    7810              MOV     R0,#010H
    1100        E     ACALL   ?C?SLSHR
    1100        R     ACALL   _transmit
    
    E4                CLR     A
    FF                MOV     R7,A
    E500        R     MOV     A,arg+02H
    FE                MOV     R6,A
    E4                CLR     A
    FD                MOV     R5,A
    FC                MOV     R4,A
    7808              MOV     R0,#08H
    1100        E     ACALL   ?C?SLSHR
    1100        R     ACALL   _transmit
    
    E500        R     MOV     A,arg+03H
    FF                MOV     R7,A
    0100        R     AJMP    _transmit
                 ; FUNCTION _calls_transmit (END)
    
                 ; FUNCTION main (BEGIN)
    ;---- Variable 'arg' assigned to Register 'R4/R5/R6/R7' ----
    7FDD              MOV     R7,#0DDH
    7ECC              MOV     R6,#0CCH
    7DBB              MOV     R5,#0BBH
    7CAA              MOV     R4,#0AAH
    1100        R     ACALL   _calls_transmit
    
    E4                CLR     A
    FE                MOV     R6,A
    FF                MOV     R7,A
    
    ?C0003:
    22                RET
                 ; FUNCTION main (END)
    
    Code size: 70 bytes
    

    It **Could be**, assuming we're naïve and still do the things the compiler likes to do:

    ASSEMBLY LISTING OF GENERATED OBJECT CODE
    
                 ; FUNCTION _transmit (BEGIN)
    
    ; ** this MOV is actually unnecessary.
    8F00        R     MOV     bar,R7
    
    ; ** With function inlining, this RET is also unnecessary.
    ; Because this function does nothing.
    22                RET
                 ; FUNCTION _transmit (END)
    
                 ; FUNCTION _calls_transmit (BEGIN)
    8F00        R     MOV     arg+03H,R7
    8E00        R     MOV     arg+02H,R6
    8D00        R     MOV     arg+01H,R5
    8C00        R     MOV     arg,R4
    
    MOV R7, arg
    ACALL   _transmit
    
    MOV R7, arg + 01H
    ACALL   _transmit
    
    MOV R7, arg + 02H
    ACALL   _transmit
    
    MOV R7, arg + 03H
    AJMP    _transmit
                 ; FUNCTION _calls_transmit (END)
    
                 ; FUNCTION main (BEGIN)
    
    ;---- Variable 'arg' assigned to Register 'R4/R5/R6/R7' ----
    7FDD              MOV     R7,#0DDH
    7ECC              MOV     R6,#0CCH
    7DBB              MOV     R5,#0BBH
    7CAA              MOV     R4,#0AAH
    
    0008 1100        R     ACALL   _calls_transmit
    
    ** Removes 3 unnecessary assigns to A, R6, and R7.
    
    ?C0003:
    22                RET
                 ; FUNCTION main (END)
    
    38 bytes total.
    

    Or simply:

    RET
    

    Another example:

    line level    source
    
    char getRand()
    {
      return 5;
    }
    
    
    char callsGetRand()
    {
      return getRand();
    }
    
    int main()
    {
      return callsGetRand();
    }
    
    ASSEMBLY LISTING OF GENERATED OBJECT CODE
    
                 ; FUNCTION getRand (BEGIN)
    
    7F05              MOV     R7,#05H0002         ?C0001:
    22                RET
                 ; FUNCTION getRand (END)
    
                 ; FUNCTION callsGetRand (BEGIN)
    
    1100        R     ACALL   getRand0002         ?C0002:
    22                RET
                 ; FUNCTION callsGetRand (END)
    
                 ; FUNCTION main (BEGIN)
    1100        R     ACALL   callsGetRand
    EF                MOV     A,R7
    33                RLC     A
    95E0              SUBB    A,ACC
    FE                MOV     R6,A
    ?C0003:
    22                RET
                 ; FUNCTION main (END)
    

    Lack of inlining here produces an extra few bytes as well. And I have no idea what the compiler's doing inside of main.

    line level    source
    
       3          void foo()
       4          {
    22                RET
       5   1      }
    
       7          int main()
       8          {
       9   1        char a;
      10   1
      11   1        for(a = 0; a != 10; a++)
      12   1          foo();
    
    E4                CLR     A
    FF                MOV     R7,A
    ?C0002:
    1100        R     ACALL   foo
    0F                INC     R7
    BF0AFA            CJNE    R7,#0AH,?C0002
    
      14   1        for(a = 10; a; a--)       ; This is optimal!
      15   1          foo();
    ?C0003:
    7F0A              MOV     R7,#0AH
    ?C0005:
    1100        R     ACALL   foo
    DFFC              DJNZ    R7,?C0005
    
    
      16   1
      17   1        a = 10;
      18   1        while(a != 0)
      19   1        {
      20   2          a--;
      21   2          foo();
      22   2        }
    
    ; This is *not* optimal. Unless you switch source lines 20 and 21 around, then it is.
    ?C0006:
    7F0A              MOV     R7,#0AH
    ?C0008:
    EF                MOV     A,R7
    6005              JZ      ?C0010
    1F                DEC     R7
    1100        R     ACALL   foo
    80F8              SJMP    ?C0008
    
    
    22                RET
    
      23   1      }
    

    Here, a lack of any sort of source code level analysis yields less than optimal code. The first case, for(a = 0; a != 10; a++) can be rewritten as for(a = 10; a; a--) because a is not used in the body of the loop.

    The second case is optimal, thanks to 8051's DJNZ instruction. But, the other 2 cases should be optimized to that, if possible.

    The third case is a bit harder for the compiler, but if it had tried switchin a--; and foo() around, it would have found the optimal code.

    As far as compile times go, we're not doing #include <windows.h>, which adds 10 million
    lines of code to the translation unit... There's plenty of power available.

    P.S: The biggest thing that makes me sad though, isn't the suboptimal code generation (I can live with that.). It's the number of clicks you have to go through to add another file to your project. Right now its, "New file", "Save as", type name, right click folder, "add file(s) to folder", navigate to where I saved file, repeat 2x for .c and .h file. It *should* be, right click -> add new file. rename it. repeat for .c file.