如果使用扩展的汇编器模板(带有输入、输出、破坏等的模板),那么您需要预先添加一个额外的%
在模板内的寄存器名称上。%%rcx
在这种情况下。这将解决与此错误相关的问题:
错误:'asm' 无效:% 字母后缺少操作数
这将带来一个新的问题。您将收到类似以下内容的错误:
“movq”的操作数类型不匹配
问题是"r"(count)
输入约束告诉编译器应该选择一个包含以下值的寄存器count
。由于计数被定义为int
type,它会选择一个32位寄存器。为了论证,假设它选择EAX。替换后,它会尝试生成以下指令:
movq %eax, %rcx
你不能使用movq
将 32 位寄存器的内容移动到 64 位寄存器,从而出现错误。更好的选择是使用ECX作为目标,以便两者属于同一类型。修改后的代码如下所示:
asm("mov %0, %%ecx;"
"startofloop: ; "
"sub $0x1, %%ecx; "
"jne startofloop; ":: "r"(count));
或者,您可以选择使用输入操作数"ri"(count)
。这将允许编译器选择寄存器或立即值。在更高的优化级别上(-O1
, -O2
)在这种情况下可能会确定count
保持不变(100000000)并生成如下代码:
mov $100000000, %ecx
startofloop:
sub $0x1, %ecx
jne startofloop
而不是被迫将 100000000 放入寄存器并将其复制到ECX它可以使用立即数来代替。
您的模板中的一个严重问题是您破坏了ECX but GCC对此一无所知。GCC实际上并没有解析模板内的指令来确定代码的作用。它不知道你已经被打败了ECX。编译器可能依赖ECX模板前后具有相同的值。如果销毁输出操作数中未引用的寄存器,则必须在破坏列表中显式列出它。像这样的事情会起作用:
asm("mov %0, %%ecx;"
"startofloop: ; "
"sub $0x1, %%ecx; "
"jne startofloop; ":: "ri"(count) : "rcx");
Now GCC知道它不能依赖于中的值RCX模板执行前后的值相同。
您可以得到以下结果,而不是使用固定寄存器作为内部计数器:GCC选择可用的东西。这样做意味着我们不再需要破坏者了。您可以创建一个可用于计数的虚拟变量(临时变量)。为了避免这段代码被完全优化,我们可以使用volatile
汇编器模板上的属性。当汇编器模板没有输出操作数时,不需要这样做。像这样的代码可以工作:
int count=100000000
int dummy;
asm volatile("mov %1, %0;"
"startofloop: ; "
"sub $0x1, %0; "
"jne startofloop; ":"=rm"(dummy): "ri"(count));
The =rm
输出约束表示该操作数可以使用内存位置或寄存器。将选择权交给编译器可以生成更好的代码。在优化级别-O1
您可能会发现生成的代码如下所示:
mov $0x5f5e100,%ebx
startofloop:
sub $0x1,%ebx
jne startofloop
在本例中,编译器选择使用立即操作数进行计数 ($0x5f5e100 = $100000000)。这dummy
变量被优化到寄存器EBX.
您还可以使用其他技巧来改进模板。人们可以阅读有关扩展汇编器模板的更多信息GNU 文档 https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Extended-Asm.html#Extended-Asm
您的代码似乎保留了变量中的值count
。如果这不是要求count
要在执行模板之前具有相同的值,您可以使用count
对于输入和输出。该代码可能如下所示:
asm volatile("startofloop: ; "
"sub $0x1, %0; "
"jne startofloop; ":"+rm"(count): );
+rm
意味着输出操作数也被用作输入操作数。在这种情况下count
完成后应始终为零。
如果您使用GCC -S
选项来输出生成的汇编代码,那么您可能希望更改模板以使输出看起来更干净。而不是使用;
(分号)使用\n\t
反而。这会将汇编器模板分解为多行并添加缩进。一个例子:
asm volatile("mov %1, %0\n\t"
"startofloop:\n\t"
"sub $0x1, %0\n\t"
"jne startofloop\n\t":"=rm"(dummy): "ri"(count));
一般来说,除非别无选择,否则不应使用内联汇编器模板。将其编码为C并引导编译器输出您想要的汇编程序,或者在需要时使用编译器内在函数。内联汇编器应该作为最后的手段,或者如果你的家庭作业需要它。大卫·沃尔弗德写了一篇维基文章 https://gcc.gnu.org/wiki/DontUseInlineAsm就此主题而言。