有没有办法以编程方式生成内联汇编?

时间:2018-05-20 22:18:10

标签: c gcc assembly x86 inline-assembly

在我的程序中,我需要将NOP作为内联汇编插入到循环中,并且NOP的数量可以通过参数控制。像这样:

char nop[] = "nop\nnop";

for(offset = 0; offset < CACHE_SIZE; offset += BLOCK_SIZE) {

    asm volatile (nop
            :
            : "c" (buffer + offset)
            : "rax");

}

有没有办法告诉编译器将上面的内联汇编转换成以下内容?

   asm volatile ("nop\n"
                 "nop"
            :
            : "c" (buffer + offset)
            : "rax");

2 个答案:

答案 0 :(得分:3)

嗯,你可以做到这一点:

#define NOPS(n) asm volatile (".fill %c0, 1, 0x90" :: "i"(n))

此宏将所需数量的nop指令插入到指令流中。请注意,n必须是编译时常量。您可以使用switch语句选择不同的长度:

switch (len) {
case 1: NOPS(1); break;
case 2: NOPS(2); break;
...
}

您还可以执行此操作以获得更多代码规模经济:

if (len & 040) NOPS(040);
if (len & 020) NOPS(020);
if (len & 010) NOPS(010);
if (len & 004) NOPS(004);
if (len & 002) NOPS(002);
if (len & 001) NOPS(001);

请注意,您应该考虑使用pause指令代替nop指令,因为pause是一个语义提示,您只是想要消磨时间。这会将宏的定义更改为:

#define NOPS(n) asm volatile (".fill %c0, 2, 0x90f3" :: "i"(n))

答案 1 :(得分:2)

不,内联asm模板需要编译时常量,因此汇编程序可以将它组装成机器代码。

如果您想要一个在运行时修改的灵活模板,那就称为JIT编译或代码生成。您通常直接生成机器代码,而不是您提供给汇编程序的汇编源文本。

例如,请参阅此完整示例,该示例生成由可变数量的dec eax指令组成的函数,然后执行它。 Code golf: The repetitive byte counter

BTW,dec eax在所有现代x86 CPU上每时钟运行1次,不像NOP,每个时钟运行4次,或者Ryzen运行5次。请参阅http://agner.org/optimize/

微小延迟的更好选择可能是pause指令,或某些可变数量的imul指令的依赖关系链,或者sqrtps,以{{1}结尾阻止无序执行(至少在Intel CPU上)。我没有查看AMD的手册,看看lfence是否被记录为执行障碍,但是Agner Fog报告说它可以在Ryzen每时钟运行4次。

但实际上,你可能根本不需要JIT任何代码。对于只需要在一个或几个系统上工作的一次性实验,可以使用

之类的东西来破解延迟循环
lfence

这会强制编译器在每次迭代时将循环计数器放在寄存器中,因此它不能优化循环,或将其转换为乘法。您应该像for (int i=0 ; i<delay_count ; i++) { asm volatile("" : "r" (i)); // defeat optimization } 那样获得编译器生成的asm。您可能希望在循环之后放置delayloop: dec eax; jnz delayloop