LLVM intrinsic 介紹

什麼是 LLVM intrinsic

LLVM 支持 “intrinsic function” 的概念。這些函數具有衆所周知的名稱和語義,並且需要遵循某些限制。總的來說,這些 intrinsic 代表 LLVM 語言的擴展機制,在添加到語言 (或者位碼讀取器/寫入器、解析器等) 時不需要更改 LLVM 中的所有轉換。

Intrinsic 函數是編譯器內建的函數,由編譯器提供,類似於內聯函數。但與內聯函數不同的是,因爲 Intrinsic 函數是編譯器提供,而編譯器與硬件架構聯繫緊密,因此編譯器知道如何利用硬件能力以最優的方式實現這些功能。

命名格式

intrinsic 名必須全部以 “ llvm” 開頭前綴。這個前綴在 LLVM 中保留用於 intrinsic 名稱; 因此,函數名稱不能以這個前綴開頭。intrinsic 函數必須始終是外部函數: 你不能定義 intrinsic 函數體。intrinsic 函數只能用於調用或調用指令: 獲取 intrinsic 函數的地址是非法的。此外,由於 intrinsic 函數是 LLVM 語言的一部分,如果添加了 intrinsic 函數,則需要對其更新文檔。

重載

一些 intrinsic 函數可以被重載,例如,intrinsic 函數表示一組在不同數據類型上執行相同操作的函數。由於 LLVM 可以表示超過 800 萬種不同的整數類型,因此通常使用重載來允許 intrinsic 函數對任何整數類型進行操作。可以重載一個或多個參數類型或結果類型以接受任何整數類型。也可以將參數類型定義爲與前一個參數的類型或結果類型完全匹配。這允許一個 intrinsic 函數接受多個參數,但是需要所有參數都是同一類型的,只能對一個參數或結果進行重載

重載 intrinsic 將把它重載的參數類型的名稱編碼到它的函數名中,每個參數類型的前面都有一個.點符號。只有那些重載的類型纔會生成名稱後綴。其類型與另一個類型匹配的參數則不會。例如,llvm.ctpop 函數可以獲取任意寬度的整數,並返回完全相同整數寬度的整數。這導致了一系列函數,如 _@_llvm.ctpop.i8(i8 %val) 和 i29 _@_llvm.ctpop.i29(i29 %val).只有一個類型 (返回類型) 被重載,並且只需要一個類型後綴。因爲參數的類型與返回類型匹配,所以它不需要自己的名稱後綴。

未命名類型被編碼爲 s_s。依賴於其重載參數類型中的未命名類型的重載 intrinsic 將獲得一個額外的 .後綴。這允許將不同的未命名類型作爲參數來區分 intrinsic。(例如: llvm.ssa.copy.p0s_s.2(%42*)), 這個數字在 LLVM 模塊中被跟蹤,並確保模塊中的唯一名稱。在將兩個模塊鏈接在一起時,仍然有可能出現名稱衝突。在這種情況下,其中一個名稱將通過獲得一個新 numver 來區分。

對於爲後端 codegen 定義 intrinsic 的目標開發人員,不應該依賴任何僅基於整數或浮點類型之間區別的內部重載來生成代碼。在這種情況下,開發人員在定義 intrinsic 時, 推薦的方法是創建單獨的整數和 浮點的 intrinsic,而不是依賴於重載

例如,如果 llvm.target.foo(<4 x i32>)) 和 llvm.target.foo(<4 x float>) 需要不同的 codegen,那麼應該將它們分成不同的 intrinsic。

變量參數處理

在 LLVM 中定義了變量參數支持,包括 va_arg 指令和三個內在函數。這些函數與 頭文件中定義的命名類似的宏相關。

所有這些函數都對使用特定於目標的值類型 “ va_list” 的參數進行操作。LLVM 彙編語言參考手冊沒有定義此類型是什麼,因此無論使用何種類型,都應該準備好處理這些函數。

舉個例子

這個例子展示瞭如何使用 va_arg 指令和 intrinsic 函數處理變量參數。

; 定義一個test 函數,第一個i32是返回值, 
; 後面括號裏面的是操作數 i32 %X
define i32 @test(i32 %X, ...) {
  ; 分配一個地址空間給變量,初始化va_list
  %ap = alloca %struct.va_list
  %ap2 = bitcast %struct.va_list* %ap to i8*
  call void @llvm.va_start(i8* %ap2)

  ; va_arg= variable_argument 
  ; 這個指令用於訪問傳遞的參數
  %tmp = va_arg i8* %ap2, i32

  ; 演示如何使用 llvm.va_copy 和 llvm.va_end
  %aq = alloca i8*
  %aq2 = bitcast i8** %aq to i8*
  call void @llvm.va_copy(i8* %aq2, i8* %ap2)
  call void @llvm.va_end(i8* %aq2)

  ; 停止參數的處理
  call void @llvm.va_end(i8* %ap2)
  ret i32 %tmp
}

; 聲明方法,類似cpp裏面的extern 
declare void @llvm.va_start(i8*)
declare void @llvm.va_copy(i8*, i8*)
declare void @llvm.va_end(i8*)

Read more

https://zhuanlan.zhihu.com/p/53659330

https://docs.microsoft.com/en-us/cpp/cpp/extern-cpp?view=msvc-170

https://llvm.org/docs/LangRef.html#intrinsic-functions

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章