如何使用Clang 11进行内联汇编,intel语法和替换变量



我有很多麻烦使它工作:


我尝试了以下方法:

uint32_t reverseBits(volatile uint32_t n) {
uint32_t i = n;
__asm__ (".intel_syntaxn"
"xor eax, eax n" 
"inc eax n"
"myloop: n"
"shr %0, 1 n"
"adc eax, eax n"
"jnc short myloop n"
"mov %1, %0  n"
: [i] "=r"(i),  [n] "=r"(n));;
return n;
}
我将得到:
Line 11: Char 14: error: unknown token in expression
"shr %0, 1 n"
^
<inline asm>:5:5: note: instantiated into assembly here
shr %edx, 1
^

显然编译器将%0替换为%register,但仍然保留'%'


因此我决定用edx代替%0,用ecx代替%1:

uint32_t reverseBits(volatile uint32_t n) {
uint32_t i = n;
__asm__ (".intel_syntaxn"
"xor eax, eax n" 
"inc eax n"
"myloop: n"
"shr edx, 1 n"
"adc eax, eax n"
"jnc short myloop n"
"mov ecx, edx  n"
: [i] "=r"(i),  [n] "=r"(n));;
return n;
}

并获取结果错误:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==31==ERROR: AddressSanitizer: SEGV on unknown address 0x0001405746c8 (pc 0x00000034214d bp 0x7fff1363ed90 sp 0x7fff1363ea20 T0)
==31==The signal is caused by a READ memory access.
#1 0x7f61ff3970b2  (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
AddressSanitizer can not provide additional info.
==31==ABORTING

我怀疑编译器优化了东西并内联了调用的函数(所以不是ret),但仍然不知道我该怎么做。

注意:我不能将编译器从clang更改为gcc,因为它不是我,而是使用clang 11的远程服务器。我也读过这个链接,但它很老了(2013年),如果从那时起事情没有改变,我会感到惊讶。


编辑:在Peter Cordes的精彩回答之后,我能够使它更好地工作:

uint32_t reverseBits(volatile uint32_t n) {
uint32_t i = n;
__asm__ (".intel_syntax noprefixn"
"xor rax,rax n" 
"inc rax n"
"myloop: n"
"shr %V0, 1 n"
"adc eax, eax n"
"jnc short myloop n"
"mov %V0, rax n"

".att_syntax"
: [i] "=r"(i));;

return i;
}

然而有两件事:

1/我不得不将eax改为rax,因为%V0需要64位(r13),这很奇怪,因为i应该只占32位(uint32_t)。

2/我没有得到想要的输出:

input is :             00000010100101000001111010011100
output is:   93330624 (00000101100100000001110011000000)
expected:   964176192 (00111001011110000010100101000000)

NB:我测试了"mov %V0, 1 n",正确地得到1作为输出,这证明了替换在某种程度上是有效的。

我不知道有什么好的方法可以做到这一点,我建议在GNU C内联asm中使用AT&T语法(或方言替代add {%1,%0 | %0,%1},因此它可以在GCC中以两种方式工作)。像-masm=intel这样的选项不会像GCC那样让clang代入裸寄存器名。

(更新:clang 14更改:如何设置gcc或clang永久使用内联asm()语句的英特尔语法?)

如何生成汇编代码与clang在英特尔语法?是关于-S输出使用的语法,与GCC不同的是,它没有连接到编译器的内联asm输入的语法。--x86-asm-syntax=intel的行为没有改变:它仍然以Intel语法输出,并且不能帮助您使用内联asm。


您可以滥用%V0%V[i](而不是%0%[i])来打印"naked"模板中的完整注册名https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#x86Operandmodifiers,但这很糟糕,因为它只打印完整的注册名。即使对于选择EAX的32位整型,它也会打印RAX而不是EAX.

(它也不能为"m"内存操作数获得dword ptr [rsp + 16]或任何编译器的寻址模式选择,但这总比没有好。虽然在我看来,这并不比仅仅使用AT&T语法更好。)


或者您可以选择硬寄存器,如"=a"(var),然后只是明确地使用EAX而不是%0。但这更糟,并且抵消了约束系统的一些优化好处。

在模板中仍然需要".intel_syntax noprefixn",并且应该".att_syntax"结束模板,将汇编器切换回AT&T模式,以汇编以后编译器生成的asm。(如果你想让你的代码与GCC一起工作,这是必需的!)Clang的内置汇编器在汇编之前不会将您的内联asm文本合并到一个大的asm文本文件中,它直接将编译器生成的指令转换为机器码。


显然告诉编译器它可以选择"=r"的任何寄存器,然后实际上使用您自己的硬编码选择,当编译器选择不同时,将创建未定义的行为。您会踩到编译器的脚趾,破坏它以后想要使用的值,并让它从错误的寄存器中获取垃圾作为输出。我不知道你为什么要在你的问题中提到这一点;对于AT&T语法,由于同样相当明显的原因,会以完全相同的方式中断。

相关内容

  • 没有找到相关文章

最新更新