内联汇编器的 Stringify 模板类型

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)

有没有一种(简单的)方法可以实现这一目标?

Pet*_*des 1

你不能用预处理器来做到这一点sizeof;模板字符串必须是实际的字符串文字,而不是涉及sizeof和 三元数或类似内容的常量表达式。即使sizeof(foo)没有模板,也不能将其作为整数提供给预处理器用于其条件。

我不知道如何为 ARM 执行此操作,但我认为唯一合理的方法是在模板字符串中使用 GCC 知道如何扩展为后缀的特殊内容。x86%z0不适用于 ARM,我试过了。但我没有检查 GCC 源代码中的修饰符列表。


对于 x86 AT&T 语法,可以使用修饰符z 打印与操作数类型相对应的操作数大小后缀。例如%z0b如果第一个操作数是 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 之后。