c++的CAS與內(nèi)存屏障: c/c++的內(nèi)聯(lián)匯編(S0)
- update note
- 20180710 更新m描述
多線程編程中偶爾需要接觸一些底層的東西,如CAS,原子操作,內(nèi)存屏障甚至有時(shí)需要自己包裹系統(tǒng)調(diào)用,這邊從abc開始,即內(nèi)聯(lián)匯編的語(yǔ)法,總結(jié)一下目前工作中遇到的相關(guān)東西。
語(yǔ)法
- 基本語(yǔ)法
asm(
匯編語(yǔ)句
匯編語(yǔ)句
...
[:輸出參數(shù)關(guān)聯(lián)列表]
[:輸入?yún)?shù)關(guān)聯(lián)列表]
[:破壞寄存器列表]
)
例子1:
/// add
int a, b;
/// b = a + b
asm(
"movl %1,%%eax\n\t"
"addl %0,%%eax\n\t"
"movl %%eax,%1\n\t"
:"+r"(b)
:"r"(a)
:"%eax"
)
關(guān)聯(lián)列表語(yǔ)法
"=" : 標(biāo)識(shí)該寄存器是只寫的
"+" : 標(biāo)識(shí)該寄存器既讀又寫
"r" : 由編譯器分配合適的寄存器,在指令中以
%0,%1,...引用.0,1,2...以出現(xiàn)的順序編號(hào).如上面變量b關(guān)聯(lián)的是0,而a關(guān)聯(lián)的是1.-
"m" : 關(guān)聯(lián)時(shí)會(huì)取關(guān)聯(lián)變量的地址,在匯編代碼中對(duì)應(yīng)的
%n會(huì)是1個(gè)地址加上內(nèi)存尋址修飾(AT&T匯編中即為這種形式:ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER).這理解起來(lái)有點(diǎn)繞,但個(gè)人覺得這樣設(shè)計(jì)的原因應(yīng)該是想統(tǒng)一關(guān)聯(lián)變量的傳遞是值傳遞的形式.
可以查看下面的AsmAdd1和AsmAdd2int x, a = 10; asm( "movl %1,%0":"=m"(x):"r"(a):); //編譯成類似 movl %edx,-32(%rbp), `%0`被`-32(%rbp)`替換所以,如果傳入內(nèi)聯(lián)匯編的東西是地址(如函數(shù)需要修改傳入?yún)?shù)的值,受制于值傳遞,需要傳入地址),想在內(nèi)聯(lián)匯編中修改某個(gè)地址,有2種做法:
- 傳入地址值給寄存器并在匯編中自行解引用,以r或abc等方式關(guān)聯(lián),加上().如
"r"(ptr) 匯編中使用(%0) - 傳入地址的解引用值,以m方式關(guān)聯(lián),匯編中對(duì)應(yīng)的操作數(shù)不自行解引用.如
"m"(*ptr) 匯編中使用%0
- 傳入地址值給寄存器并在匯編中自行解引用,以r或abc等方式關(guān)聯(lián),加上().如
其他的注意點(diǎn).asm
- 匯編語(yǔ)句中的寄存器需要2個(gè)百分號(hào),
r關(guān)聯(lián)的操作數(shù)只需1個(gè)百分號(hào).原因是該內(nèi)聯(lián)匯編語(yǔ)句是以字符串的形式被編譯器消費(fèi)的.同理,語(yǔ)句間分隔符需要顯式地給\n\t也是同樣的道理. 注意:不能給;,否則后面的語(yǔ)句在匯編中將被注釋! - gcc默認(rèn)是at&t語(yǔ)法.也就是源操作數(shù)跟目的操作數(shù)位置與上學(xué)時(shí)學(xué)的
masm匯編相反. - AT&T解引用操作符是小括號(hào)而不是中括號(hào).
-
movl不允許兩個(gè)操作數(shù)都為內(nèi)存地址.
-
AT&T語(yǔ)法,常用簡(jiǎn)單匯編指令
- movl s,d
- addl s,d
下面給出幾個(gè)例子實(shí)現(xiàn)asm_add和asm_swap
1 #include <iostream>
2
3
4 // test for inline asm
5 /*
6 * asm(
7 * "匯編語(yǔ)句1;\n"
8 * "匯編語(yǔ)句2;\n"
9 * "匯編語(yǔ)句3;\n"
10 * :"輸出列表"
11 * :"輸入列表"
12 * :"破壞寄存器列表"
13 * )
14 *
15 */
16 int AsmAdd0(int a, int b) {
17 asm(
18 "movl %1,%%eax\n\t"- 練習(xí)
- asm_add
- asm_swap
19 "addl %0,%%eax\n\t"
20 "movl %%eax,%0\n\t"
21 :"+r"(b)
22 :"r"(a)
23 :"%eax"
24 );
25 return b;
26 }
27
28 void AsmAdd1(int a, int b, int* c) {
29 asm (
30 "addl %1, %2\n\t"
31 "movl %2, (%0)\n\t"
32 :"=r"(c)
33 :"r"(a), "r"(b)
34 );
35 }
36
37 void AsmAdd2(int a, int b, int* c) {
38 asm (
39 "addl %1, %2\n\t"
40 "movl %2, %0\n\t"
41 :"=m"(*c)
42 :"r"(a), "r"(b)
43 );
44 }
45
46 /// *b <----- a
47 ///
48 void AsmSet(int a, int *b) {
49 asm(
50 "movl %1, %%eax\n\t"
51 "movl %%eax, %0\n\t"
52 :"=m"(*b)
53 :"r"(a)
54 :"%eax"
55 );
56 }
57
58
59
60 void testM() {
61 int x;
62 int a = 10;
63 asm( "addl %1, %%ebx\n\t"
64 "movl %1,%0\n\t"
65 "addl %%eax, %%ebx"
66 :"=m"(x):"r"(a):"%eax","%ebx"); //%0會(huì)被替換為`(x的偏移量)`
67 std::cout << x << std::endl;
68 }
69
70 template<typename T>
71 void SwapByAsm(T* a, T* b) {
72 asm(
73 "movq (%0), %%rax\n\t"
74 "movq (%1), %%rbx\n\t"
75 "movq %%rbx, (%0)\n\t"
76 "movq %%rax, (%1)\n\t"
77 :"+r"(a), "+r"(b)
78 :
79 :"%rax","%rbx"
80 );
81 }
82 template<>
83 void SwapByAsm(char* a, char* b) {
84 asm(
85 "movb (%0), %%al\n\t"
86 "movb (%1), %%bl\n\t"
87 "movb %%bl, (%0)\n\t"
88 "movb %%al, (%1)\n\t"
89 :"+r"(a), "+r"(b)
90 :
91 :"%al","%bl"
92 );
93 }
94
95 int main() {
96 int a = 20;
97 int b = 10;
98 int c = -1;
99 testM();
100 std::cout << "--------AsmAdd--------" << std::endl;
101
102 c = AsmAdd0(a, b);
103 std::cout << "c:" << c << std::endl;
104
105 c = -1;
106 AsmAdd1(a, b, &c);
107 std::cout << "c:" << c << std::endl;
108
109 c = -1;
110 AsmAdd2(a, b, &c);
111 std::cout << "c:" << c << std::endl;
112
113 std::cout << "-------SwapByAsm---------" << std::endl;
114 double x0 = 0.91, x1 = 10.24;
115 char c0 = 'X', c1 = 'a';
116
117 std::cout << "before: x0:" << x0 << ", x1: " << x1 << std::endl;
118 SwapByAsm(&x0, &x1);
119 std::cout << "after : x0:" << x0 << ", x1: " << x1 << std::endl;
120
121 std::cout << "before: c0:" << c0 << ", c1:" << c1 << std::endl;
122 SwapByAsm(&c0, &c1);
123 std::cout << "after : c0:" << c0 << ", c1:" << c1 << std::endl;
124
125 return 0;
126 }
- 實(shí)際用途
- 自己封裝非posix系統(tǒng)調(diào)用,如gittid(2)
- 自己封裝CAS匯編指令,如nginx中的CAS.
- 內(nèi)存屏障.