在我的程序中,我需要将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");
答案 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
。