Mik*_*yke 5 c++ templates inline-assembly c++11
我正在寻找一种通过模板函数自动执行 gcc 内联汇编调用的方法。
例如,我有以下虚拟函数将值存储到指针中。现在我专门针对不同类型的模板函数。每当代码发生变化时,我都需要针对每个专业化进行更改。
template <typename T>
void store_ptr(T *location, T value);
template <>
void store_ptr<char>(char *location, char value) {
__asm__ __volatile__(
"strb %1, [%0]\n\t"
: "+r" (location)
: "r" (value)
: "memory"
);
}
template <>
void store_ptr<short>(short *location, short value) {
__asm__ __volatile__(
"strh %1, [%0]\n\t"
: "+r" (location)
: "r" (value)
: "memory"
);
}
Run Code Online (Sandbox Code Playgroud)
如果模板可以根据模板类型对指令附录(“b”、“h”...)进行字符串化,那就太好了。
template <typename T>
void store_ptr<T>(T *location, T value) {
__asm__ __volatile__(
"str" stringify_template_type(T) " %1, [%0]\n\t"
: "+r" (location)
: "r" (value)
: "memory"
);
}
Run Code Online (Sandbox Code Playgroud)
有没有一种(简单的)方法可以实现这一目标?
你不能用预处理器来做到这一点sizeof
;模板字符串必须是实际的字符串文字,而不是涉及sizeof
和 三元数或类似内容的常量表达式。即使sizeof(foo)
没有模板,也不能将其作为整数提供给预处理器用于其条件。
我不知道如何为 ARM 执行此操作,但我认为唯一合理的方法是在模板字符串中使用 GCC 知道如何扩展为后缀的特殊内容。x86%z0
不适用于 ARM,我试过了。但我没有检查 GCC 源代码中的修饰符列表。
对于 x86 AT&T 语法,可以使用修饰符z
打印与操作数类型相对应的操作数大小后缀。例如%z0
,b
如果第一个操作数是 a char
。而常规%0
可能会扩展到%al
, 一个字节寄存器。
你会像这样使用它:
template <typename T>
void store_ptr<T>(T *location, T value) {
__asm__ __volatile__(
"mov%z0 %1, %0"
: "=m" (*location) // let the compiler pick an addressing mode
: "re" (value) // register or up-to-imm32 source
: // "memory" // the compiler knows that *location is the only memory written
);
}
Run Code Online (Sandbox Code Playgroud)
例如
void test(void *dst, int x) {
store_ptr((int*)dst, x);
store_ptr((short*)dst, short(x));
store_ptr((char*)dst, char(-123));
store_ptr((long*)dst, long(-123));
store_ptr((long*)dst, long(0x00000000ffffffff)); // doesn't fit in sign-extended imm32
store_ptr((unsigned*)dst, unsigned(0xffffffff)); // does fit, uses an immediate
store_ptr((short*)dst, short(123));
}
Run Code Online (Sandbox Code Playgroud)
这可以在Godbolt上正确编译,使用 x86-64 GCC for AT&T 语法。(但不是 Clang 17;它似乎不理解修饰符%z0
。)我检查了“链接到二进制”或“编译为二进制对象”以验证这个编译器生成的 asm 也可以汇编;GCC 不验证这一点,它只是将字符串替换到汇编器模板中,就像 printf 格式字符串一样。
test(void*, int): # pointer in RDI, int in ESI
movl %esi, (%rdi) # AT&T is op src, dst opposite of ARM insn other than str
movw %si, (%rdi)
movb $-123, (%rdi)
movq $-123, (%rdi) # mov $sign_extended_imm32, m64
movl $4294967295, %eax # put 0x00000000ffffffff into RAX via mov $-1, %eax
movq %rax, (%rdi) # this is the actual inline asm template, the previous instruction was generated to set up the "r" operand
movl $-1, (%rdi) # unsigned(0xffffffff) does match an "e" constraint
movw $123, (%rdi)
ret
Run Code Online (Sandbox Code Playgroud)
我也可以做类似(long*)dst+x
或之类的事情dst+8
来获得其他寻址模式,例如(%rdi,%rsi,8)
在将 ESI 符号扩展为 RSI 之后。