内核模式 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/VFP 寄存器的情况下,才允许中断内核模式 NEON 部分。因此,以下规则和限制在内核中适用: * 不允许在中断上下文中使用 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>(它包含 <stdint.h>),因此除了上述规则外,还应注意以下事项
使用 '-ffreestanding' 编译包含 NEON 内联函数的单元,以便 GCC 使用其内置版本的 <stdint.h>(这是一个内核不提供的 C99 头文件);
最后包含 <arm_neon.h>,或者至少在 <linux/types.h> 之后包含