c - GCC 扩展程序集 pin 局部变量到除 r12 之外的任何寄存器
问题描述
基本上,我正在寻找一种将临时固定到任何寄存器的方法,除了r12
.
我知道我可以“提示”编译器将其固定到单个寄存器:
// Toy example. Obviously an unbalanced `pop` in
// extended assembly will cause serious problems.
register long tmp asm("rdi"); // or just clober rdi and use it directly.
asm volatile("pop %[tmp]\n" // using pop hence don't want r12
: [tmp] "=&r" (tmp)
:
:);
这通常可以避免r12
但可能会弄乱编译器在其他地方的寄存器分配。
是否可以在不强制编译器使用单个寄存器的情况下做到这一点?
解决方案
请注意,这register asm
并没有真正将变量“固定”到寄存器,它只确保使用该变量作为内联 asm 中的操作数将使用该寄存器。原则上,变量可以存储在两者之间的其他地方。请参阅https://gcc.gnu.org/onlinedocs/gcc-11.1.0/gcc/Local-Register-Variables.html#Local-Register-Variables。但听起来您真正需要的只是确保您的pop
指令不用r12
作其操作数,可能是因为使用寄存器 R12 时为什么 POP 很慢?. 我不知道有什么方法可以做到这一点,但这里有一些可能会有所帮助的选项。
每个寄存器rax, rbx, rcx, rdx, rsi, rdi
都有自己的约束字母,a,b,c,d,S,D
分别(其他寄存器没有)。所以你可以通过做
long tmp;
asm volatile("pop %[tmp]\n"
: [tmp] "=&abcdSD" (tmp)
:
:);
这样,编译器可以选择这六个寄存器中的任何一个,这应该给寄存器分配器更多的灵活性。
另一种选择是声明您的 asm clobbers r12
,这将阻止编译器在那里分配操作数:
long tmp;
asm volatile("pop %[tmp]\n"
: [tmp] "=&r" (tmp)
:
: "r12");
权衡是它也不会用于r12
缓存 中的局部变量asm
,因为它假定它可以被修改。希望它足够聪明,可以完全避免r12
在代码的那部分使用,但如果不能,它可能会发出额外的寄存器移动或溢出到 asm 周围的堆栈中。尽管如此,它仍然没有-ffixed-r12
阻止编译器使用r12
整个源文件中的任何地方那么残酷。
未来的读者应该注意,通常在 x86-64 上修改内联 asm 中的堆栈指针是不安全的。编译器假定内联 asm 不会更改它,并且它可以随时rsp
通过具有相对于 的恒定偏移量的有效地址访问堆栈变量。rsp
而且x86-64使用了红色区域,所以即使是push/pop
一对也不安全,因为下面可能存储了重要的数据rsp
。(而意外pop
可能意味着其他重要数据不再在红色区域,因此会被信号处理程序覆盖。)因此,除非您愿意在每次重新编译后仔细阅读生成的程序集以确保编译器没有决定执行任何这些操作,否则您不应该这样做事物。(在你问之前,你不能通过声明一个rsp
; 的破坏来解决这个问题,这是不受支持的。)
推荐阅读
- colors - QML Material Dark 不会更改为深色调色板
- r - Alteryx RTool 不提供输出
- javascript - 使用选择选项值 JS 更改图像
- java - 是否可以将带有嵌套选择语句的查询映射到 DTO?
- visual-studio-code - 在 VS Code 中,即使在调整了更漂亮的配置设置后,Prettier 也不会以逗号结尾
- ansible - 如何使用 ansible 创建和编辑文件
- google-apps-script - 邮件未通过 Google 表格应用脚本发送
- mysql - 如何为单个列中存在的值添加多个条件并将结果显示在单独的列中?
- reactjs - Kendo React - X 轴共享 Y 轴的图表
- bash - 使用别名在一行中运行多个 git 命令