[英]Working of __asm__ __volatile__ (“” : : : “memory”)
什么基本上__asm__ __volatile__ ()
和ARM架構的"memory"
什么意義呢?
asm volatile("" ::: "memory");
創建編譯器級別內存屏障,強制優化器不會跨屏障重新排序內存訪問。
例如,如果您需要按特定順序訪問某個地址(可能是因為該內存區域實際上是由不同的設備而不是內存支持),您需要能夠告訴編譯器,否則它可能只是優化您的步驟效率的緣故。
假設在這種情況下,您必須增加地址中的值,讀取內容並增加相鄰地址中的另一個值。
int c(int *d, int *e) {
int r;
d[0] += 1;
r = e[0];
d[1] += 1;
return r;
}
問題是編譯器(在這種情況下為gcc
)可以重新安排內存訪問,以便在需要時獲得更好的性能( -O
)。 可能會導致如下所示的一系列指令:
00000000 <c>:
0: 4603 mov r3, r0
2: c805 ldmia r0, {r0, r2}
4: 3001 adds r0, #1
6: 3201 adds r2, #1
8: 6018 str r0, [r3, #0]
a: 6808 ldr r0, [r1, #0]
c: 605a str r2, [r3, #4]
e: 4770 bx lr
d[0]
和d[1]
上述值同時加載。 讓我們假設這是你想要避免的事情然后你需要告訴編譯器不要重新排序內存訪問,那就是使用asm volatile("" ::: "memory")
。
int c(int *d, int *e) {
int r;
d[0] += 1;
r = e[0];
asm volatile("" ::: "memory");
d[1] += 1;
return r;
}
所以你會得到你想要的指令序列:
00000000 <c>:
0: 6802 ldr r2, [r0, #0]
2: 4603 mov r3, r0
4: 3201 adds r2, #1
6: 6002 str r2, [r0, #0]
8: 6808 ldr r0, [r1, #0]
a: 685a ldr r2, [r3, #4]
c: 3201 adds r2, #1
e: 605a str r2, [r3, #4]
10: 4770 bx lr
12: bf00 nop
應該注意的是,這只是編譯時內存屏障,以避免編譯器重新排序內存訪問,因為它沒有額外的硬件級指令來刷新內存或等待加載或存儲完成。 如果CPU具有架構功能並且內存地址是normal
類型而不是strongly ordered
或device
( ref ),則CPU仍然可以重新排序內存訪問。
此序列是編譯器內存訪問調度障礙,如Udo引用的文章中所述。 這個是GCC特定的 - 其他編譯器有其他方式來描述它們,其中一些具有更明確(和更少深奧)的語句。
__asm__
是允許嵌套在C代碼中的匯編語言語句的gcc擴展 - 這里使用它的屬性是能夠指定阻止編譯器執行某些類型的優化的副作用(在這種情況下可能最終生成不正確的代碼)。
需要__volatile__
來確保asm語句本身不與任何其他volatile訪問重新排序(C語言中的保證)。
memory
是GCC的一種指令(有點)表示內聯asm序列對全局內存有副作用,因此不僅需要考慮對局部變量的影響。
這里的含義如下:
http://en.wikipedia.org/wiki/Memory_ordering
基本上它意味着匯編代碼將在您期望的位置執行。 它告訴編譯器不要重新排序它周圍的指令。 這是在之前執行這段代碼之前編碼的代碼,之后執行的代碼將在之后執行。
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags;
asm volatile(
" mrs %0, cpsr @ arch_local_irq_save\n"
" cpsid i" //disabled irq
: "=r" (flags) : : "memory", "cc");
return flags;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.