内核模式 NEON¶
TL;DR 摘要¶
仅使用 NEON 指令,或不依赖支持代码的 VFP 指令
将 NEON 代码隔离在单独的编译单元中,并使用 ‘-march=armv7-a -mfpu=neon -mfloat-abi=softfp’ 进行编译
在调用 NEON 代码的代码前后分别加上
kernel_neon_begin()和kernel_neon_end()调用不要在 NEON 代码中休眠,并注意它在执行时会禁用抢占
简介¶
可以在内核模式下运行的代码中使用 NEON 指令(在某些情况下也可以使用 VFP 指令)。然而,出于性能原因,与普通寄存器文件不同,NEON/VFP 寄存器文件在每次上下文切换或捕获异常时不会被保存和恢复,因此需要进行一些人工干预。此外,对于可能休眠的代码(即可能调用 schedule() 的代码),需要特别注意,因为出于下文概述的原因,NEON 或 VFP 指令将在不可抢占的代码段中执行。
延迟保存与恢复¶
NEON/VFP 寄存器文件使用延迟保存(在 UP 系统上)和延迟恢复(在 SMP 和 UP 系统上)进行管理。这意味着寄存器文件保持“活动”状态,仅在多个任务争夺 NEON/VFP 单元时(或者在 SMP 情况下,当任务迁移到另一个核心时)才进行保存和恢复。延迟恢复是通过在每次上下文切换后禁用 NEON/VFP 单元来实现的,这会导致随后发出 NEON/VFP 指令时产生陷阱,从而允许内核介入并在必要时执行恢复。
内核模式下对 NEON/VFP 单元的任何使用都不应干扰这一点,因此需要对 NEON/VFP 寄存器文件进行“立即”保存,并显式启用 NEON/VFP 单元,以便在随后的首次使用时不会产生异常。这由 kernel_neon_begin() 函数处理,该函数应在发出任何内核模式 NEON 或 VFP 指令之前调用。同样,在使用后应再次禁用 NEON/VFP 单元,以确保用户模式在下次使用时能够触发延迟恢复陷阱。这由 kernel_neon_end() 函数处理。
内核模式中的中断¶
出于性能和简洁性的考虑,决定不为内核模式的 NEON/VFP 寄存器内容设置保存/恢复机制。这意味着,只有在保证中断内核模式 NEON 代码段不会触及 NEON/VFP 寄存器的情况下,才允许发生中断。为此,内核中适用以下规则和限制:* 不允许在中断上下文中使用 NEON/VFP 代码;* 不允许 NEON/VFP 代码休眠;* 执行 NEON/VFP 代码时会禁用抢占。
如果延迟是一个问题,可以在代码中 NEON 寄存器均处于非活动状态的地方,背靠背地调用 kernel_neon_end() 和 kernel_neon_begin()。(如果在此期间没有发生上下文切换,对 kernel_neon_begin() 的额外调用开销应该相当低)
VFP 与支持代码¶
早期版本的 VFP(版本 3 之前)依赖软件支持来处理诸如符合 IEEE-754 标准的下溢处理等事务。当 VFP 单元需要此类软件协助时,它会通过引发未定义指令异常来向内核发出信号。内核通过检查 VFP 控制寄存器、当前指令及参数来响应,并在软件中模拟该指令。
目前,对于在内核模式下执行的 VFP 指令,尚未实现此类软件协助。如果遇到这种情况,内核将失败并生成 OOPS。
将 NEON 代码与普通代码分离¶
编译器并不知道 kernel_neon_begin() 和 kernel_neon_end() 的特殊意义,即它只允许在这些对应函数的调用之间发出 NEON/VFP 指令。此外,如果选择了 -mfpu=neon,GCC 可能会在 -O3 级别下自行生成 NEON 指令;并且即使内核当前是在 -O2 下编译的,如果不加小心的照顾,未来的更改也可能会导致 NEON/VFP 指令出现在意想不到的地方。
因此,在内核中使用 NEON/VFP 推荐且唯一支持的方法是遵守以下规则
将 NEON 代码隔离在单独的编译单元中,并使用 ‘-march=armv7-a -mfpu=neon -mfloat-abi=softfp’ 进行编译;
从未设置 GCC 标志 ‘-mfpu=neon’ 构建的编译单元中,发出对
kernel_neon_begin()、kernel_neon_end()的调用,以及对包含 NEON 代码的单元的调用。
由于内核是用 ‘-msoft-float’ 编译的,以上操作将确保在任何优化级别下,NEON 和 VFP 指令都只会出现在指定的编译单元中。
NEON 汇编器¶
只要遵循上述规则,就支持 NEON 汇编器,且没有其他注意事项。
由 GCC 生成的 NEON 代码¶
GCC 选项 -ftree-vectorize(由 -O3 隐式启用)尝试利用隐式并行性,并从普通的 C 源代码生成 NEON 代码。只要遵循上述规则,这是完全支持的。
NEON 内建函数¶
也支持 NEON 内建函数。但是,由于使用 NEON 内建函数的代码依赖于 GCC 头文件 <arm_neon.h>(其中 #includes <stdint.h>),除了上述规则外,您还应遵守以下规定
使用 ‘-ffreestanding’ 编译包含 NEON 内建函数的单元,以便 GCC 使用其内置版本的 <stdint.h>(这是内核未提供的 C99 头文件);
不要直接包含 <arm_neon.h>:而是包含 <asm/neon-intrinsics.h>,它调整了一些宏定义,以便可以安全地包含系统头文件。