ARC64工具链中FPU寄存器分配问题的分析与解决
问题背景
在构建ARC64架构的嵌入式工具链时,开发团队遇到了一个关于浮点运算单元(FPU)寄存器分配的关键问题。当使用arc64-snps-elf工具链编译newlib数学库中的多个函数时,编译器报告了"unable to find a register to spill"的错误,导致编译失败。这个问题主要出现在包含复杂浮点运算的函数中,如k_rem_pio2.c、e_log.c等数学函数实现。
问题现象
编译过程中出现的错误信息表明,编译器在处理64位浮点常数时遇到了寄存器分配困难。具体表现为:
- 编译器无法为特定的指令找到可用的寄存器进行溢出(spill)
- 错误发生在将64位常量(如0x3fc0000000000000)移动到FPU寄存器的过程中
- 问题在启用多库支持(--enable-multilib)的配置下出现
技术分析
经过深入分析,开发团队发现问题的根源在于ARC64架构中处理64位浮点常量的方式存在缺陷。当编译器需要将64位浮点常量加载到FPU寄存器时,原有的实现存在以下问题:
- 直接使用整数模式(DImode)处理双精度浮点(DFmode)常量
- 缺少从整数到浮点的显式转换指令
- 寄存器分配器在处理这种特殊情况时无法正确工作
解决方案
开发团队针对此问题提出了以下修复方案:
- 在arc64_prepare_move_operands函数中增加显式的浮点转换
- 使用gen_floatdidf2指令生成从整数到双精度浮点的转换
- 添加断言确保只在DImode和DFmode组合下执行此操作
- 考虑到当前ARC32+FPUDF配置不受支持,暂时不做兼容处理
修复的核心代码修改如下:
gcc_assert (imode == DImode && mode == DFmode);
...
emit_insn (gen_floatdidf2 (op0, tmp));
验证测试
为了验证修复效果,开发团队创建了一个精简测试用例,模拟了原始问题中的关键操作:
int *a;
int b, c, d;
double e, g;
double f[0];
double *h;
int i[0];
void l() {
int j;
k:
for (; g; b++, g--)
i[b] = 1.67772160000000000000e07 * e;
if (b) {
for (; b <= d; b++) {
f[b] = a[b];
c = 0.0;
for (; c <= j; c++)
e = h[c] * f[c];
}
goto k;
}
}
该测试用例成功验证了修复方案的有效性,确认了编译器现在能够正确处理64位浮点常量的加载和寄存器分配。
影响范围
此修复影响以下方面:
- 所有使用ARC64架构FPU双精度浮点运算的代码
- 特别是数学库中涉及复杂浮点计算的函数
- 使用-mfpu=fpud选项编译的代码
总结
这个问题的解决确保了ARC64工具链能够正确编译包含复杂浮点运算的代码,特别是标准数学库的实现。通过显式处理64位浮点常量的加载和转换,编译器现在能够更可靠地分配FPU寄存器,为ARC64架构的高性能浮点运算提供了坚实基础。这一修复已合并到arc-2024.06和arc64分支中,成为工具链稳定性的重要改进。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



