2015-03-24 73 views
0

當編譯該代碼的ARM Cortex M4(teensy 3.1)聯彙編約束

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) 
    ); 
} 

寄存器分配產生的代碼,經過 '編譯'

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 

予指定的SRC是一個讀只進行註冊,但不應該讓'編譯器'被允許覆蓋它,是嗎?快速解決方法是使每個變量可讀/寫(+ r)。

但是是什麼原因造成的,這是一個錯誤,還是有人可以解釋爲什麼會發生這種情況?

編輯:對不起,使用gcc編譯(ARM-NONE-EABI-G ++兩個版本4.7.2和4.8.4)

+0

此外,clobber列表中還需要「內存」和「cc」,否則編譯器會認爲它們不變。 – 2015-03-25 15:38:45

+0

你是對的,這很棘手,因爲這很容易被人遺忘(就像我一樣),大多數情況下,沒有這些線條,它完美地工作,但是,你是對的。如果編譯器太聰明,會導致很難發現錯誤。 – user3310744 2015-03-25 19:17:54

回答

1

這是不是在編譯器中的錯誤忘了提,這個我們在Linux上。該編譯器對ASM操作的輸入/輸出模式是它們都是這樣進行的:

  1. 讀取輸入
  2. 做手術
  3. 寫入輸出

因此,編譯器允許將任何輸出值分配給相同的寄存器作爲輸入值。

如果在asm語句中有一系列指令(例如在您的示例中),則在讀取某些輸入之前會先寫入一些輸出。答案位於https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm的內聯限制文檔中。

你需要解決這個問題的部分是:

使用「&」約束脩飾符(請參見修改)上不能重疊的輸入輸出的所有操作數。否則,GCC可能會將輸出操作數分配到與不相關輸入操作數相同的寄存器中,假設彙編器代碼在生成輸出之前消耗其輸入。如果彙編代碼實際上由多個指令組成,這個假設可能是錯誤的。

如果您改變輸出約束:

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

,那麼你將解決這個問題。

+0

如果我理解正確,在實踐中,您應該始終添加&,因爲您不知道輸入何時會被輸出覆蓋。唯一的例外是單行彙編指令,其中「編譯器」允許重用寄存器。 – user3310744 2015-03-25 18:48:09

+0

在讀取某個輸入之前寫入的指令序列時,應該使用&。你知道什麼時候輸出將被寫入 - 它就在你的asm語句中的彙編指令中。 – 2015-03-26 00:46:20