CAS與內(nèi)存屏障: c/c++的內(nèi)聯(lián)匯編(S0)

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和AsmAdd2

    int 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種做法:

    1. 傳入地址值給寄存器并在匯編中自行解引用,以r或abc等方式關(guān)聯(lián),加上().如"r"(ptr) 匯編中使用(%0)
    2. 傳入地址的解引用值,以m方式關(guān)聯(lián),匯編中對(duì)應(yīng)的操作數(shù)不自行解引用.如"m"(*ptr) 匯編中使用%0
  • 其他的注意點(diǎn).asm

  1. 匯編語(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ǔ)句在匯編中將被注釋!
  2. gcc默認(rèn)是at&t語(yǔ)法.也就是源操作數(shù)跟目的操作數(shù)位置與上學(xué)時(shí)學(xué)的masm匯編相反.
  3. AT&T解引用操作符是小括號(hào)而不是中括號(hào).
  4. 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)存屏障.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容