ARM技术william hill官网
直播中

张玲

7年用户 1288经验值
私信 关注
[经验]

使用GCC 10充分利用Arm架构

GCC 10.1 版本

GNU Compiler Collection 用于对各种系统进行编程:从最快的超级计算机到最小的微控制器。Arm 热爱生态系统。最近发布的 GCC 10.1 是 GCC 社区一年辛勤工作的结晶。Arm 合作伙伴关系也发挥了作用。本博客让您深入了解一些我们对新 CPU 支持、架构支持、便携式软件部署辅助和性能优化最感兴趣的新 Arm 相关功能。

可扩展向量扩展

去年添加了超过 25000 行代码来实现 SVE ACLE。自从 GCC 项目在 2020 年开始使用 git 进行版本控制以来,这些统计数据现在很容易收集。我们很自豪地宣布,**GCC ****10.1 **完全支持SVE 的 Arm C 语言扩展。

这使您可以访问 4000 多个内在函数,以便在您的内核中使用 SVE 的许多高级功能。例如,让我们尝试编译从 Arm Scalable Vector Extensions 和应用程序到机器学习白皮书的稍微修改的示例:

#ifdef __ARM_FEATURE_SVE

#include <arm_sve.h>

#else

#error "Must use SVE for this example!"

#endif / __ARM_FEATURE_SVE /

void

vla_add_arrays **(**double *dst, double *src, double c, long N)

{

for **(**long i = 0; i < N; i += svcntd ())

**{**

svbool_t Pg = svwhilelt_b64 (i, N);

svfloat64_t vsrc = svld1 (Pg, &src[i]);

svfloat64_t vdst = svadd_x (Pg, vsrc, c);

svst1 (Pg, &dst[i], vdst);

**}**

}

使用 AArch64 GCC ****10.1编译它:

$ aarch64-none-linux-gnu-gcc -O2 -march=armv8.2-a+sve example.c

编译没有问题,并为我们提供了 SVE 程序集:

vla_add_arrays:

**cmp**     **x2**, **0**

**ble**     **.**L1

**mov**     **x3**, **0**

**mov**     **z1**.**d**, **d0**

**.**p2align **3**,,**7**

.L3:

**whilelt** **p0**.**d**, **x3**, **x2**

**ld1d**    **z0**.**d**, **p0**/**z**, **[**x1**,** **x3**, **lsl** **3**]

**fadd**    **z0**.**d**, **p0**/**m**, **z0**.**d**, **z1**.**d**

**st1d**    **z0**.**d**, **p0**, **[**x0**,** **x3**, **lsl** **3**]

**incd**    **x3**

**cmp**     **x2**, **x3**

**bgt**     **.**L3

.L1:

**ret**

您可以在此处看到使用 SVE 每车道预测功能对循环进行矢量化。这避免了传统 SIMD 指令集所必需的标量尾声和回退。

为部署未来架构技术做准备,GCC ****10.1还提供了对SVE2 ACLE内部函数的支持。可以使用和选项的+sve2 扩展来启用 SVE2 的编译。

例如:-march``<span> </span>-mcpu

$ aarch64-none-linux-gnu -march=armv8.5-a+sve2

作为 SIMD ISA,SVE 是编译器自动矢量化的一个很好的目标。尽管优化编译器的工作从未完成,但GCC 10.1在针对 SVE 时改进了自动矢量化功能。请留意即将推出的更多详细信息。

用于 LSE 部署的非线原子

Armv8.1 -A 架构引入了大型系统扩展 (LSE)。其中包括执行常用操作的指令,如比较和交换 (CAS) 和原子加载和增量 (LDADD)。它们可用于有效地将 __atomic_compare_exchange 和 __atomic_fetch_add 等高级语言结构映射到符合 Arm 内存模型的指令序列。这些说明对于在大核心数系统中获得最佳性能扩展至关重要。

事实上,GCC将在编译 for 或更高版本时自动使用 LSE 指令,-march=armv8.1-a而不是像编译 for 时那样使用 load-exclusive、operation、store-exclusive 循环-march=armv8-a

GNU/Linux 发行版针对基线-march=armv8-a 架构进行编译,以确保它可以在每个 AArch64 实现上正确运行。但他们仍然希望在可用时利用 LSE 指令。

为此,Linaro 的 Richard Henderson 为GCC 10.1贡献了 该-moutline-atomics选项,该选项在 GCC 10.1中默认启用 . 当使用此选项为 Armv8-A 基线编译时,编译器将生成一个调用运行时辅助函数的存根,而不是发出一个加载独占存储独占循环。辅助函数通过 HWCAP 机制对 LSE 指令的可用性执行运行时检查(缓存结果以便更快地进行后续检查)。

然后,如果可用,它会分派到 LSE 指令序列,或者分派到 load-exclusive-store-exclusive 循环。如果这听起来很复杂,这里有一个使用语言级原子结构的 C 语言简单示例:

整数

test_cas_atomic_int ( int * val , int * foo , int * bar )

{

返回 __atomic_compare_exchange_n ( val , foo , bar , 0 , 0 , 0 ) ;

}

-march=armv8-a -O2 使用GCC 9编译会生成一个 load-exclusive-store-exclusive 循环:

test_cas_atomic_int:

ldr w3,[x1]

.L4:

ldxr w4, [x0]

CMP w4, w3

bne .L5

stxr w5, w2, [x0]

cbnz w5, .L4

.L5:

cset w0, eq

贝克.L2

str w4, [x1]

.L2:

ret

您可以在其中看到重试独占存储的循环,STXR直到它以原子方式成功。编译-O2 -march=armv8.1-a(它有一个隐含的+lse)它生成:

test_cas_atomic_int:

ldr w4, [x1]

mov w3, w4

cas w3, w2, [x0]

CMP w3, w4

cset w0, eq

贝克.L2

str w3, [x1]

.L2:

ret

您可以在其中看到使用CASLSE 指令的更简单的序列。现在,使用 GCC 10.1 ,我们获得了以下选项 -O2 -march=armv8-a<span> </span>

test_cas_atomic_int:

stp x29x30[sp,-32]mov x29, sp

stp x19, x20, [sp, 16]

移动 x19, x1

移动 w1, w2

移动 x2, x0

ldr w20[x19]

移动 w0, w20

bl __aarch64_cas4_relax

cmp w0, w20

移动 w1, w0

cset w0, eq

贝克.L2

str w1[x19]

.L2:

ldp x19, x20, [sp, 16]

ldp x29, x30, [sp], 32

ret

这里有一些复杂性,与为函数调用准备参数有关,该函数调用 是运行时库__aarch64_cas4_relax提供的辅助函数。

libgcc<span> </span>在那里,运行时可以测试是否存在 LSE 指令并分派到前两个序列中的任何一个。这种间接允许此函数在所有 AArch64 系统上正确运行,甚至是 Armv8-A 系统,同时仍尽可能使用来自 Armv8.1-A 的 LSE 指令。

Arm 生态系统中的许多成员已经测量了这种间接寻址对不同系统集的性能影响,我们很高兴地发现,与使用 LSE 指令在大核心数量下实现更好可扩展性的好处相比,它是最小的。

Arm 架构的年度更新:Armv8.6-A

Armv8.6 -A架构更新引入了许多创新来加速机器学习工作负载。其中包括用于通用矩阵乘法 (GEMM) 的指令以及用于训练和推理的bfloat16数据类型。

为了强调这些工作负载的重要性,我们将这些扩展引入了 AArch32 和 AArch64 状态,后者也获得了 SVE 变体。

您可以在GCC 10.1中通过 ACLE 内部函数以及-march=armv8.6-a选项和关联的扩展来使用这些扩展。

Arm 自定义指令和自定义数据路径扩展

Simon Segars 在 Arm TechCon 2019 上发布的Arm Custom Instructions 引起了轰动。

在幕后,工程团队一直在努力实现这一目标。随着初始架构规范到位,我们定义了许多ACLE 内在函数,以提供对通过自定义数据路径扩展 ( CDE ) 提供的新指令的访问 )。

这些指令的性质允许 M-profile 处理器供应商为他们的特定应用定制这些指令的行为。编译器只需要知道所使用的输入和输出寄存器以及一些关于这些指令不存在不可预测的副作用的基本保证,就可以正确地对程序的数据流进行建模。

您现在可以使用 GCC 10.1编译器为 Armv8.1-M 生成这些自定义指令,方法是指定要使用的协处理器,如下所示:

$ arm-none-eabi-gcc -march=armv8.1-m.main+cdecp0

此编译命令启用CP0CDE 协处理器和相关指令。使用新标头中定义的内在函数的玩具示例,arm_cde.h例如:

#include "arm_cde.h"

uint32_t

test_cde_cx1 ( uint32_t a )

{

返回 __arm_cx2 ( 0 , a , 33 ) ;

}

然后编译成程序集:

test_cde_cx1:

cx2 p0, r0, r0, #33

bx lr

我们很想听听您对如何最好地为您最喜欢的用例公开 Arm 自定义说明的反馈。

Armv8.1-M

GCC 10.1-march=armv8.1-m.mainline通过选项及其扩展 支持 Armv8.1-M Mainline 架构。这包括为更新的CMSE规范生成代码,并带有对MVE SIMD 架构的-mcmse选项和初始支持。这包括对包含新头文件时可用的ACLE 内部函数的支持。

除了架构支持之外, Cortex-M55处理器还提供了一个全新的-mcpu=cortex-m55选项。

原作者:凯里洛·特卡乔夫

更多回帖

发帖
×
20
完善资料,
赚取积分