fuz*_*fuz 7 assembly gcc clang inline-assembly arm64
受到最近一个问题的启发。
gcc 式内联汇编的一种用例是对编译器和汇编器都不知道的指令进行编码。例如,我给出了如何在太旧而无法支持的工具链上使用指令的示例:rdrand
/* "rdrand %%rax ; setc %b1" */
asm volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %b1"
: "=a"(result), "=qm"(success) :: "cc");
Run Code Online (Sandbox Code Playgroud)
不幸的是,对指令进行硬编码意味着您还需要对其使用的寄存器进行硬编码,从而大大减少了编译器执行寄存器分配的自由度。
在某些架构上(例如带有.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");
Run Code Online (Sandbox Code Playgroud)
同样,如果有一种方法可以让 gcc 打印12
而不是v12
ARM64 上的 SIMD 寄存器 12,则可以执行如下操作:
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;
}
Run Code Online (Sandbox Code Playgroud)
有没有办法获取注册号?如果不是,还有哪些其他选项可以对编译器和汇编器都不知道的指令进行编码,而无需对寄存器号进行硬编码?
我实际上遇到了同样的问题并提出了以下解决方案。
#define REG_CONST(n) asm(".equ .L__reg_const__v" #n ", " #n);
REG_CONST(0)
REG_CONST(1)
REG_CONST(2)
REG_CONST(3)
// ... repeat this for all register numbers ...
REG_CONST(27)
REG_CONST(28)
REG_CONST(29)
REG_CONST(30)
float32x4_t add3(float32x4_t a, float32x4_t b) {
float32x4_t c;
// fadd %0, %1, %2
asm(".inst 0x4e20d40 | .L__reg_const__%0 | (.L__reg_const__%1 << 5) + (.L__reg_const__%2 << 16)" : "=w"(c) : "w"(a), "w"(b));
return c;
}
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?
%0
, , ... 之类的占位符。%1
.equ
指令来定义表示整数的符号。(以 开头的符号.L
在生成的目标文件中将不可见,因此我们不会不必要地弄乱符号表)REG_CONST
都会定义一个(本地)符号:.L__reg_const__v0
该符号等于 0、.L__reg_const__v1
等于 1、.L__reg_const__v2
等于 2,依此类推。asm(".equ .L__reg_const__v0 0")
表达式应该位于汇编文件的顶部。asm(".inst ...")
内的模板中,, ,将被替换为编译器为,和所选的任何寄存器。add3
%0
%1
%2
a
b
c
.L__reg_const__
,因此替换会将其变成类似 的表达式.L__reg_const__v7
。 归档时间: |
|
查看次数: |
524 次 |
最近记录: |