灵感来自a recent question。
gcc风格的内联汇编的一个用例是对编译器和汇编器都不知道的指令进行编码。例如,我给出了this example如何在旧的工具链上使用rdrand
指令:
/* "rdrand %%rax ; setc %b1" */
asm volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %b1"
: "=a"(result), "=qm"(success) :: "cc");
字符串
不幸的是,硬编码指令意味着您还需要硬编码与它一起使用的寄存器,这大大减少了编译器执行寄存器分配的自由度。
在某些架构上(如带有.insn
指令的RISC-V),汇编程序提供了一种系统地构建原始指令的方法,但这似乎是个例外。
一个简单的解决方案是有一种方法来获得寄存器的未修饰数,以手动将其编码到指令中。例如,假设存在一个模板修饰符X
来打印所选寄存器的编号。然后,上面的示例可以变得更加灵活:
/* "rdrand %0 ; setc %b1" */
asm volatile (".byte 0x48 | (%X0 >> 3), 0x0f, 0xc7, 0xf0 | (%X0 & 7); setc %b1"
: "=r"(result), "=qm"(success) :: "cc");
型
类似地,如果有一种方法可以让gcc在ARM 64上为SIMD寄存器12打印12
而不是v12
,那么就可以这样做:
float32x4_t add3(float32x4_t a, float32x4_t b)
{
float32x4_t c;
/* fadd %0, %1, %2 */
asm (".inst 0x4e20d40 + %X0 + (%X1<<5) + (%X2<<16)" : "=w"(c) : "w"(a), "w"(b));
return c;
}
型
有没有办法获得注册号?如果没有,还有什么其他的选择可以编码编译器和汇编器都不知道的指令,而不必硬编码寄存器号?
1条答案
按热度按时间oprakyz71#
我实际上也遇到过同样的问题,并提出了以下解决方案。
字符串
这是如何工作的?
1.请记住,占位符如
%0
、%1
、...将由编译器 * 在 * 将结果传递给汇编程序之前,* 通过简单的字符串替换来填充寄存器名称。1.在汇编文件中,我们可以使用
.equ
指令来定义表示整数的符号。(以.L
开头的符号在生成的目标文件中是不可见的,因此我们不会不必要地使符号表混乱)1.对
REG_CONST
宏的每个调用将定义一个(本地)符号:.L__reg_const__v0
等于0,.L__reg_const__v1
等于1,.L__reg_const__v2
等于2,依此类推。1.这些宏被有意地放在文件的顶部,在任何函数之外,因为生成的
asm(".equ .L__reg_const__v0 0")
表达式应该放在程序集文件的顶部。1.在
add3
函数内的asm(".inst ...")
模板中,%0
、%1
、%2
随后将被替换为编译器为a
、b
和c
选择的任何寄存器。1.由于我们偷偷地在
.L__reg_const__
表达式后面直接写入了没有任何空格的占位符,因此替换将把它转换为类似于.L__reg_const__v7
的表达式。1.但这与我们在顶部定义的整数符号的名称完全对应!所以汇编程序实际上会将其作为一个符号,并将其替换为我们定义的整数值。
1.在对符号求值之后,结果是纯数字表达式,并且汇编器将很高兴地将整数值一起“或”,从而产生所需的操作码。