This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler generates illegal assembler

Compiling the following code:

void main(void)
{
    volatile int* dly = (void*)0xdead;
    unsigned int i;
    for(i=0;i<32768+4;i++)
    {
        *dly=0;
    }
}

compile using: "cl55 -ml -O2 -k test.c"

Gives the following assembler error

[test.c]
"test.asm", ERROR!   at line 60: [E9999] Syntax Error
         RPT #-32765

"test.asm", ERROR!   at line 60: [E9999] Invalid instructionsyntax, CSR,
                                         Immediate: 8-bit unsigned, Immediate:
                                         16-bit signed, Smem expected
         RPT #-32765

"test.asm", ERROR!   at line 60: [E9999] Trailing operands or input specified
         RPT #-32765

"test.asm", ERROR!   at line 60: [E9999] Failed operand constraints
         RPT #-32765

4 Assembly Errors, No Assembly Warnings

Errors in Source - Assembler Aborted

>> Compilation failure

>> Compilation failure

Note that if we init dly with 0 "magically" solves the problems because a rptblock is created instead of rpt, never the less the compiler should not generate code the assembler does not understand.

Test with version 4.4.0

 

  • The problem seems to be triggered at -o2 and higher (lower levels of optimization work fine). I submitted bug # SDSCM00042470 to track this issue. Feel free to track its status using the SDOWP link in my signature.

  • Note that the problem can be avoided by using the -mg option to select algebraic assembly format for the intermediate assembly file generated by the compiler.  The problem is in the acceptance (or not) of a negative argument to RPT when written in mnemonic assembly format.

    I found various ways to force RPTBLOCK to be used instead of a RPT, but initializing dly with a zero was not one of them.

     

  • Paul Knueven said:

    I found various ways to force RPTBLOCK to be used instead of a RPT, but initializing dly with a zero was not one of them.

    You are right what I test before was a little different, I also changed what gets written to dly. I tried this

     

    void main(void)
    {
    volatile int* dly = (void*)0;
    unsigned int i;
    unsigned int die = 0xDEAD;
    for(i=0;i<32768+4;i++)
    {
    *dly=die;
    }
    }

    And this compiles fine because now the compiler loads BRC0 via AR1. (it cannot load BRC0 directly?)

    This results in a rptblocal because the compiler now decides to use a direct memory reference, and this instruction length is to long for a rpt:

      MOV #-8531, *(#0) ; |8|

    However when using die = 0 it will generate the assembler error since it is turned in to an rpt once more.

    So dly = 0 and die !=0 => Ok assembler

    Dly = 0 and die = 0 -> Wrong assembler.

    So dly = 0 and die = 0 seems to be a special case.

  • Wouter said:

    And this compiles fine because now the compiler loads BRC0 via AR1. (it cannot load BRC0 directly?)

    The instruction to load BRC0 with a constant only accomodates a 12-bit integer (even though BRC0 is a 16-bit register), thus the use of AR1 which can be loaded with a 16-bit constant.

    Wouter said:

    So dly = 0 and die = 0 seems to be a special case.

    For dly = 0 and die !=0 the instruction to be repeated is

      MOV #-8531, *(#0) ; 7 bytes

    For dly = 0 and die = 0 the instruction to be repeated is

      MOV #0, *(#0)     ; 6 bytes

    The Repeat Single Unconditionally (RPT) instruction cannot be used to repeat an instruction longer than 6 bytes (although you would be hard pressed to find that mentioned in the CPU Reference Guide).

    Also, note that the examples we have been playing with depend on the compilation being done in Large or Huge memory model.  In Small model data addresses are limited to 16 bits so the destination of the MOV would use abs16 addressing.  Then even the dly = 0 and die != 0 case would use the RPT since the instruction to be repeated would be

      MOV #-8531, *abs16(#0)  ; 6 bytes

  • And I meant to mention again that the recommended workaround for this problem is to use -mg (--algebraic) to force the compiler to use algebraic assembly format (which can handle all of the above cases) in the generated code passed to the assembler.  The only catch would be if you had asm statements that used mnemonic assembly.

    Also, in a probably mysterious side remark, let me point out that if you do not use mnemonic assembly format in asm statements, compiling C code with -mg is always slightly faster than not using -mg.

  • Paul Knueven said:

     The instruction to load BRC0 with a constant only accomodates a 12-bit integer (even though BRC0 is a 16-bit register), thus the use of AR1 which can be loaded with a 16-bit constant.

     


      Ah never noticed that. Learning everyday.

    Paul Knueven said:

    The Repeat Single Unconditionally (RPT) instruction cannot be used to repeat an instruction longer than 6 bytes (although you would be hard pressed to find that mentioned in the CPU Reference Guide).

     

    I did find that one, but I thought RPT + intruction(s) should not exceed 6. Another thing learned.

    Paul Knueven said:

    Also, note that the examples we have been playing with depend on the compilation being done in Large or Huge memory model. In Small model data addresses are limited to 16 bits so the destination of the MOV would use abs16 addressing. Then even the dly = 0 and die != 0 case would use the RPT since the instruction to be repeated would be
    MOV #-8531, *abs16(#0) ; 6 bytes

     Sure fiddling with memory model flags will change the code all together. Even changing CPU revision might be enough to add some extra NOPS and prevent a RPT

    Paul Knueven said:

    Also, in a probably mysterious side remark, let me point out that if you do not use mnemonic assembly format in asm statements, compiling C code with -mg is always slightly faster than not using -mg.

    Thanks for pointing out the workaround, I knew the workaround. But I opted to solve it by avoiding the long loops. I prefer compiling all my files with the same flags. I will consider using -mg for the complete project as it is faster. Just for satisfying curiosity is just the compiling faster and the assembler slower? Since IIRC algebraic leaves more details for the assembler to solve.

     

     

  • Wouter said:

    ... Just for satisfying curiosity is just the compiling faster and the assembler slower? Since IIRC algebraic leaves more details for the assembler to solve.

    The algebraic assembler is faster than the mnemonic assembler.