0

When compiling this code

void FastRead()
{
    register uint32_t cnt = 1000, sample;
    register uint8_t *dst = data;
    asm volatile(
    "loop1:"
        "ldr     %[sample], [%[src]]\n\t"
        "strb    %[sample], [%[dst], #1]!\n\t"
        "subs    %[cnt],1 \n\t"                
        "bne     loop1\n\t"
        : [cnt] "+r" (cnt), [dst] "+r" (dst), [sample] "=r" (sample)
        : [src] "r" (&CORE_PIN16_PINREG)
    );
}

The register assignment produces this code, after 'compiling'

000004dc <_Z8FastReadv>:
     4dc:   f44f 737a   mov.w   r3, #1000   ; 0x3e8
     4e0:   4a03        ldr r2, [pc, #12]   ; (4f0 <loop1+0xc>)
     4e2:   4904        ldr r1, [pc, #16]   ; (4f4 <loop1+0x10>)

000004e4 <loop1>:
     4e4:   6809        ldr r1, [r1, #0]  ;         <--  ?!?!?!?!?!?
     4e6:   f802 1f01   strb.w  r1, [r2, #1]!
     4ea:   3b01        subs    r3, #1
     4ec:   d1fa        bne.n   4e4 <loop1>
     4ee:   4770        bx  lr
     4f0:   1fff8820    .word   0x1fff8820
     4f4:   400ff050    .word   0x400ff050

I specified the src to be a read-only register, but should not mean the 'compiler' is allowed to overwrite it, does it? quick workaround is making every variable read/writable (+r).

But what causes this, is this a bug, or can anybody explain me why this is happening?

Edit: sorry forgot to mention, this us on linux using the gcc compiler (arm-none-eabi-g++ both versions 4.7.2 and 4.8.4)

  • Additionally "memory" and "cc" are required in clobber list otherwise the complier will assume they are unchanged. – Timothy Baldwin Mar 25 '15 at 15:38
  • You are right, this is tricky, since this can easily be forgotten (as i did), most of the time it works perfectly without these lines, but yes you are right. It can lead to very hard to find bugs if the compiler gets too smart. – user3310744 Mar 25 '15 at 19:17

1 Answers1

1

This is not a bug in the compiler. The compiler's model for inputs/outputs of an asm operation is that they are performed like this:

  1. read inputs
  2. do operation
  3. write outputs

Therefore, the compiler is allowed to assign any output value to the same registers as an input value.

When you have a sequence of instructions in an asm statement, such as in your example, some outputs are written before some inputs are read. The answer is found in the documentation for inline asm constraints at https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm.

The part you need to solve this problem is:

Use the ‘&’ constraint modifier (see Modifiers) on all output operands that must not overlap an input. Otherwise, GCC may allocate the output operand in the same register as an unrelated input operand, on the assumption that the assembler code consumes its inputs before producing outputs. This assumption may be false if the assembler code actually consists of more than one instruction.

If you change your output constraints to:

: [cnt] "+&r" (cnt), [dst] "+&r" (dst), [sample] "=&r" (sample)

then you will fix the problem.

Charles Baylis
  • 851
  • 7
  • 8
  • If I understand correctly, in practice, you should always add the &, since you don't know when the input will be overwritten with an output. The only exception is for single line assembly instructions, where is gives the 'compiler' the freedom to reuse registers. – user3310744 Mar 25 '15 at 18:48
  • You should use the & when your instruction sequence which is written before some input is read. You know when the output will be written - it's right there in the assembly instructions in your asm statement. – Charles Baylis Mar 26 '15 at 00:46