除了對今年架構的標準支持外,我們還完成了對可擴展矩陣擴展(SME和SME2)的匯編級支持。在CPU方面,此版本擴展了Armv9-A內核系列,支持我們的Cortex-A715和Cortex-X3 CPU。
A-profile 2022更新:Armv8.9-A和Armv9.4-A
現在,除了將在下一個LLVM版本中支持的保護調用堆棧(GCS)之外,所有擴展都可以進行匯編和反匯編。Arm C語言擴展(ACLE)也用兩個新的內部函數__rsr128和__wsr128進行了擴展;這些使得新的128位系統寄存器更容易訪問。LLVM現在支持這些內部函數。
轉換加固擴展(THE)是Armv9.4-A的主要安全改進之一,也是虛擬內存系統體系結構(VMSA)的一部分。其目的是防止在攻擊者獲得內核權限的情況下對虛擬內存的轉換表進行任意更改。新的讀取-檢查-寫入(RCW)指令已添加到體系結構中,以允許在禁用普通寫入的同時對此類表進行受控修改。
盡管這些指令是針對內核而非用戶空間開發人員的,但RCW指令可以很好地映射到C++中128位數據類型上的各種原子操作。更具體地說,fetch_and、fetch_or和exchange可以直接用這些指令來實現。
這個功能對任何使用原子操作的人都很有用,所以我們在LLVM 16中添加了代碼生成支持。在LRCPC3和LSE2擴展也可用的目標中,這些專用指令直接從C++代碼生成,而不需要匯編或內部函數。
以下是std::atomic::fetch_and的示例:
#includestd::atomic<__uint128_t> global; void sink(__uint128_t); void ldclrpal_example(__uint128_t x) { __uint128_t res = global.fetch_and(x); sink(res); } void ldclrp_example(__uint128_t x) { __uint128_t res = global.fetch_and(x, std::memory_order_relaxed); sink(res); }
使用-march=armv9.4a+lse128+rcpc3-O3編譯,生成的程序集顯示正在生成的新指令:
ldclrpal_example(unsigned __int128): mvn x1, x1 mvn x0, x0 adrp x8, global add x8, x8, global ldclrpal x0, x1, [x8] b sink(unsigned __int128) ldclrp_example(unsigned __int128): mvn x1, x1 mvn x0, x0 adrp x8, global add x8, x8, global ldclrp x0, x1, [x8] b sink(unsigned __int128)
多版本控制功能
如今,許多平臺都有一個單一的二進制部署模型:每個應用程序都是通過一個二進制文件分發的。這使得開發人員很難針對多個體系結構功能。為了解決這個問題,LLVM 16提供了一種針對特定體系結構特征的方便方式,而不需要處理特征檢測和其他細節。這個新功能被稱為函數多版本控制。
提供了一個新的宏__HAVE_FUNCTION_MULTI_VERSIONING來檢測功能的可用性。如果存在,我們可以要求編譯器通過標記__attribute__((target_clones())來生成給定函數的多個版本。函數的最合適版本將在運行時調用。
在下面的示例中,一個函數被標記為要為Advanced SIMD(又名NEON)和SVE構建。如果SVE在目標上可用,則將使用SVE版本。
#ifdef __HAVE_FUNCTION_MULTI_VERSIONING __attribute__((target_clones("sve", "simd"))) #endif float foo(float *a, float *b) { // }
在某些情況下,開發人員希望為每個功能提供不同的代碼。這也可以通過使用__attribute__((target_version()))來實現。在下面的例子中,我們為同一個函數提供了兩個版本。同樣,如果SVE可用,將調用SVE版本。宏__HAVE_FUNCTION_MULTI_VERSIONING允許編寫與具有和不具有函數多版本控制的編譯器兼容的代碼。
#ifdef __HAVE_FUNCTION_MULTI_VERSIONING __attribute__((target_version("sve"))) static void foo(void) { printf("FMV uses SVE "); } #endif // this attribute is optional // __attribute__((target_version("default"))) static void foo(void) { printf("FMV default "); return; }
此功能依賴于編譯器rt(-rtlib=編譯器rt),并且在默認情況下啟用,但可以使用標志-mno fmv禁用它。請注意,函數多版本控制仍處于測試狀態。ACLE規范非常歡迎通過打開新問題或創建pull請求來提供反饋。
性能改進
復數自動矢量化
LLVM 16包括對復數上的公共運算的自動矢量化的支持。這些分別利用了Armv8-A和Armv8-M體系結構的高級SIMD(Neon)和MVE指令集中可用的指令。例如,代碼:
#include#define N 512 void fma (_Complex float a[restrict N], _Complex float b[restrict N], _Complex float c[restrict N]) { for (int i=0; i < N; i++) c[i] = a[i] * b[i]; }
輸出以下匯編代碼:
fma: // @fma mov x8, xzr .LBB0_1: // =>This Inner Loop Header: Depth=1 add x9, x0, x8 add x10, x1, x8 movi v2.2d, #0000000000000000 movi v3.2d, #0000000000000000 ldp q1, q0, [x9] add x9, x2, x8 add x8, x8, #32 cmp x8, #1, lsl #12 // =4096 ldp q5, q4, [x10] fcmla v3.4s, v1.4s, v5.4s, #0 fcmla v2.4s, v0.4s, v4.4s, #0 fcmla v3.4s, v1.4s, v5.4s, #90 fcmla v2.4s, v0.4s, v4.4s, #90 stp q3, q2, [x9] b.ne .LBB0_1 ret
請注意FCMLA指令的使用,該指令對復數向量執行融合乘加向量運算和可選的復數旋轉。
默認啟用功能專業化和SPEC2017內部改進
在為速度進行優化時,默認情況下在所有優化級別都啟用了功能的專業化。通行證的優化啟發式和編譯時屬性已經得到了改進,并且被認為通常足夠有益,可以默認啟用。
這種優化在各種AArch64平臺上特別將SPEC2017 intrate中的505.mcf_r基準提高了約10%。這有助于將SPEC2017年intrate C/C++基準在AArch64提高3%。
請注意,SPEC2017性能提升還得益于SelectOpt通道和其他高級模式識別的默認調整和啟用。
SVE和自動矢量化的改進
SVE的自動矢量化一直是一個非常活躍的發展領域。例如,到目前為止,在條件的不同分支中訪問的指針的矢量化是非常基本的:大多數時候,它會被計算為成本太高。現在,指針上的基本運算包含在矢量器的成本模型中。這意味著現在可以在更好的情況下對以下代碼進行矢量化:
void foo(float *dst, float *src, int *cond, long disp) { for (long i=0; i<1024; i++) { if (cond[i] != 0) { dst[i] = src[i]; } else { dst[i] = src[i+disp]; } } }
也就是說,在合成示例中,找到合適的環境以使矢量化有利可圖是很棘手的,并且生成的代碼非常長。如果你想看看矢量化的代碼是什么樣子的,你可以調整成本模型。使用-march=v9a-O3-Rpass=loop vectorize-mllvm-force target instruction cost=1編譯前面的示例。
通過減少對顯式合并操作的需求,尾部折疊循環的矢量化也得到了改進。例如,以下代碼:
float foo(float *a, float *b) { float sum = 0.0; for (int i = 0; i < 1024; ++i) sum += a[i] * b[i]; return sum; }
用-march=armv9-a-Ofast-mllvm-sve tail folding=all編譯,這表明現在發出了預測的FMLA:
.LLVM_15_LOOP: ld1w { z2.s }, p1/z, [x0, x8, lsl #2] ld1w { z3.s }, p1/z, [x1, x8, lsl #2] add x8, x8, x10 fmul z2.s, z3.s, z2.s sel z2.s, p1, z2.s, z0.s whilelo p1.s, x8, x9 fadd z1.s, z1.s, z2.s b.mi .LLVM_15_LOOP .LLVM_16_LOOP: ld1w { z1.s }, p1/z, [x0, x8, lsl #2] ld1w { z2.s }, p1/z, [x1, x8, lsl #2] add x8, x8, x10 fmla z0.s, p1/m, z2.s, z1.s whilelo p1.s, x8, x9 b.mi .LLVM_16_LOOP
此外,通過減少對顯式反向運算的需要,改進了具有反向迭代計數的循環的矢量化。以這個循環為例:
void foo(int *a, int *b, int* c) { for (int i = 1024; i >= 0; --i) { if (c[i] > 10) a[i] = b[i] + 5; } }
使用-march=armv9-a-O3編譯后,LLVM 16輸出不再反轉加載的數據,也不再反轉用于條件的謂詞:
.LLVM_15_LOOP: ld1w { z0.s }, p0/z, [x16, x9, lsl #2] ld1w { z1.s }, p0/z, [x17, x9, lsl #2] rev z0.s, z0.s rev z1.s, z1.s cmpgt p1.s, p0/z, z0.s, #10 cmpgt p2.s, p0/z, z1.s, #10 rev p1.s, p1.s rev p2.s, p2.s ld1w { z0.s }, p1/z, [x14, x9, lsl #2] ld1w { z1.s }, p2/z, [x15, x9, lsl #2] add z0.s, z0.s, #5 // =0x5 add z1.s, z1.s, #5 // =0x5 st1w { z0.s }, p1, [x12, x9, lsl #2] st1w { z1.s }, p2, [x13, x9, lsl #2] sub x9, x9, x10 cmp x18, x9 b.ne .LLVM_15_LOOP .LLVM_16_LOOP: ld1w { z0.s }, p0/z, [x13, x9, lsl #2] ld1w { z1.s }, p0/z, [x14, x9, lsl #2] cmpgt p1.s, p0/z, z0.s, #10 cmpgt p2.s, p0/z, z1.s, #10 ld1w { z0.s }, p1/z, [x15, x9, lsl #2] ld1w { z1.s }, p2/z, [x16, x9, lsl #2] add z0.s, z0.s, #5 // =0x5 add z1.s, z1.s, #5 // =0x5 st1w { z0.s }, p1, [x17, x9, lsl #2] st1w { z1.s }, p2, [x18, x9, lsl #2] sub x9, x9, x10 cmp x12, x9 b.ne .LLVM_16_LOOP
LLVM 16上SVE的其他性能改進包括:
。DUP的使用在各種場景中都得到了極大的改進,尤其是對于128位LD1RQ變體。
。乘法-加法和乘法子指令可以更廣泛地使用。
。對PTEST指令的需求已經大大減少。
。擴展循環負載消除現在是類型不可知的,因此可以檢測更多的情況。
。SLP成本模型得到了改進。
Spec2017與Flang一起構建
去年12月,我們通過LLVM/Frang在O3上實現了所有Fortran速率基準測試的里程碑。主要關注點是啟用四個失敗的基準測試(521.wrf_r、527.cam4_r、549.fotonik3d_r、554.roms_r)。主要改進之一是通過使用復雜方言消除了對外部復雜數學庫的依賴。
此外,通過改進前端和LLVM之間的信息共享,以及改進對快速數學的支持,還獲得了一些性能。
您可以通過將-DLLVM_ENABLE_PROJECTS=“Flang;clang;mlir”傳遞給CMake來構建Flang。flang可執行文件稱為flang-new;確保通過選項-flang實驗exec來生成可執行文件。
Target-gated ACLE 內聯
最初是由Highway庫引發的,目標(“
現在支持的格式是:
。arch=
。cpu=
。tune=
。+<feature>,+no<feature>啟用或禁用特定功能,以與GCC目標屬性兼容。
。<feature>,no-<feature>啟用或禁用特定功能,以便與以前的clang版本向后兼容。
隨著上述變化,ACLE內部函數的實現也進行了修改,使其不再基于預處理器宏。相反,它們是基于當前目標啟用的。這允許在單個函數中提供內部函數,而不需要為同一目標編譯整個文件。以下示例說明了函數sve2_log上屬性的使用:
#include#include void base_log(float *src, int *dst, int n) { for(int i = 0; i < n; i++) dst[i] = log2f(src[i]); } void __attribute__((target("sve2"))) sve2_log(float *src, int *dst, int n) { int i = 0; svbool_t p = svwhilelt_b32(i, n); while(svptest_any(svptrue_b32(), p)) { svfloat32_t d = svld1_f32(p, src+i); svint32_t l = svlogb_f32_z(p, d); svst1_s32(p, dst+i, l); i += svcntb(); p = svwhilelt_b32(i, n); } }
llvm objdump的改進
在LLVM 16中,Arm目標的LLVM objdump的輸出在可讀性和正確性方面得到了改進,使其成為基于LLVM的工具鏈上GNU objdump的更合適的替代品。
big-endian對象文件的反匯編現在可以正常工作。以前,每個指令字都被意外地進行了字節交換,并被分解為完全不同的東西。
此外,在反匯編中遇到的無法識別的指令會以更有用的方式進行處理。以前,反匯編程序只前進一個字節,然后從奇數地址重試。此策略在具有可變長度指令的體系結構上是有意義的,但在Arm上則不然。新的行為是推進整個指令,以便文件的其余部分可能會被正確地反匯編。
LLVM 16包括Arm架構的其他質量改進,包括Thumb與Arm反匯編的錯誤修復,以及現在包含正確字節的.byte指令。對指令編碼進行了一些可讀性改進,使Arm和32位Thumb更容易區分:現在您可以看到Arm指令有一個8位數字,Thumb有兩個4位數字,中間有一個空格。
支持AArch64上的嚴格浮點
AArch64已經實現了嚴格的浮點語義。clang命令行選項-ffp model=strict現在在AArch64目標上被接受,而不是被忽略并發出警告。舉個例子,只有在安全的情況下才執行FP除法:
float fn(int n, float x, float y) { if (n == 0) { x += 1; } else { x += y/n; } return x; }
在LLVM 15上,使用-O2進行編譯會生成以下代碼:
fn(int, float, float): // @fn(int, float, float) scvtf s3, w0 fmov s2, #1.00000000 cmp w0, #0 fdiv s1, s1, s3 fadd s1, s1, s0 fadd s0, s0, s2 fcsel s0, s1, s0, ne ret
它將執行兩個分支,包括除法,然后在fcsel中選擇正確的結果。盡管保留了代碼的功能,但當n=0時,它會導致偽FE_DIVBYZERO浮點異常。在LLVM 16上,使用-O2-ffp模型=嚴格編譯會產生以下代碼:
fn(int, float, float): // @fn(int, float, float) cbz w0, .LBB0_2 scvtf s2, w0 fdiv s1, s1, s2 fadd s0, s0, s1 ret .LBB0_2: mov w8, #1 scvtf s1, w8 fadd s0, s0, s1 ret
其中兩個不同的執行分支保持分離,從而防止FP異常的發生。
由于支持嚴格的FP,現在也接受了選項-frapping math和-frounding math。一方面,-ftrapping數學確保代碼不會引入或刪除任何類型的FP異常可能導致的副作用。其中包括軟件可以通過檢查FPSR異步檢測到的異常。類似地,-founding數學避免應用假設特定FP舍入行為的優化。
在編譯器rt和LLD中支持早期的Arm體系結構
LLD現在可以用作ARMv4和ARMv4T的鏈接器:它現在發出與ARMv4和ARMv4T兼容的thunk,而不是ARMv4的不兼容BX指令或ARMv4或ARMv4T的BLX指令。
與此相關的是,為ARMv4T、ARMv5TE和ARMv6添加了對編譯器rt內置程序的支持,從而解鎖了對這些體系結構的運行時支持。
由于這項啟用工作,現在可以為這些32位Arm架構提供一個完整的基于LLVM的工具鏈。因此,Linux內核現在增加了對使用LLD構建Clang的支持,Rust程序不再需要依賴GNU鏈接器。
審核編輯:劉清
-
寄存器
+關注
關注
31文章
5359瀏覽量
120814 -
ARM處理器
+關注
關注
6文章
361瀏覽量
41834 -
編譯器
+關注
關注
1文章
1640瀏覽量
49198 -
SIMD
+關注
關注
0文章
35瀏覽量
10311 -
GNU
+關注
關注
0文章
143瀏覽量
17517
原文標題:LLVM16的新增功能
文章出處:【微信號:Arm軟件開發者,微信公眾號:Arm軟件開發者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論