__asm__ __volatile__在C中做了什么?

时间:2014-10-19 23:25:30

标签: c gcc inline-assembly

我从

中查看了一些C代码

http://www.mcs.anl.gov/~kazutomo/rdtsc.html

他们使用" 内联"," asm "等如下:

代码1:

static __inline__ tick gettick (void) {
    unsigned a, d;
    __asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) );
    return (((tick)a) | (((tick)d) << 32));
}

码2:

volatile int  __attribute__((noinline)) foo2 (int a0, int a1) {
    __asm__ __volatile__ ("");
}

我想知道code1和code2做了什么?

3 个答案:

答案 0 :(得分:54)

__volatile__块上的__asm__修饰符强制编译器的优化器按原样执行代码。没有它,优化器可能会认为它可以直接删除,也可以从循环中取出并缓存。

这对rdtsc指令很有用,如下所示:

__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )

这不依赖,因此编译器可能会认为该值可以被缓存。易失性用于强制它读取新的时间戳。

单独使用时,如下:

__asm__ __volatile__ ("")

它实际上不会执行任何操作。但是,您可以对此进行扩展,以获得编译时内存屏障,该屏障不允许重新排序任何内存访问指令:

__asm__ __volatile__ ("":::"memory")

rdtsc指令是volatile的一个很好的例子。当您需要计算一些指令执行的时间时,通常会使用rdtsc。想象一下像这样的代码,你想要在r1r2执行时间:

__asm__ ("rdtsc": "=a" (a0), "=d" (d0) )
r1 = x1 + y1;
__asm__ ("rdtsc": "=a" (a1), "=d" (d1) )
r2 = x2 + y2;
__asm__ ("rdtsc": "=a" (a2), "=d" (d2) )

这里实际上允许编译器缓存时间戳,并且有效输出可能显示每行完全执行0个时钟。显然这不是你想要的,所以你引入__volatile__来防止缓存:

__asm__ __volatile__("rdtsc": "=a" (a0), "=d" (d0))
r1 = x1 + y1;
__asm__ __volatile__("rdtsc": "=a" (a1), "=d" (d1))
r2 = x2 + y2;
__asm__ __volatile__("rdtsc": "=a" (a2), "=d" (d2))

现在您每次都会获得一个新的时间戳,但它仍然存在允许编译器和CPU重新排序所有这些语句的问题。在已经计算出r1和r2之后,它可能最终执行asm块。要解决此问题,您需要添加一些强制序列化的障碍:

__asm__ __volatile__("mfence;rdtsc": "=a" (a0), "=d" (d0) :: "memory")
r1 = x1 + y1;
__asm__ __volatile__("mfence;rdtsc": "=a" (a1), "=d" (d1) :: "memory")
r2 = x2 + y2;
__asm__ __volatile__("mfence;rdtsc": "=a" (a2), "=d" (d2) :: "memory")

请注意此处的mfence指令,它强制执行CPU端屏障,以及&#34;内存&#34; volatile块中的说明符,它强制执行编译时屏障。在现代CPU上,您可以将mfence:rdtsc替换为rdtscp以获得更高效的效果。

答案 1 :(得分:4)

asm用于将本机汇编代码包含在C源代码中。 E.g。

int a = 2;
asm("mov a, 3");
printf("%i", a); // will print 3

编译器有不同的变体。 __asm__应该是同义词,可能有一些特定于编译器的差异。

volatile表示可以从外部修改变量(也就是说不是由C程序修改)。例如,当编程微控制器时,存储器地址0x0000x1234被映射到某些特定于设备的接口(即,当编码GameBoy时,按钮/屏幕/等以这种方式访问​​。)

volatile std::uint8_t* const button1 = 0x00001111;

这种禁用的编译器优化依赖于*button1,除非被代码更改,否则不会更改。

它还用于多线程编程(今天不再需要?),其中一个变量可能被另一个线程修改。

inline提示编译器“内联”对函数的调用。

inline int f(int a) {
    return a + 1
}

int a;
int b = f(a);

不应将此编译为f的函数调用,而应编译为int b = a + 1。好像f所在的宏。编译器主要根据功能使用/内容自动执行此优化。此示例中的__inline__可能具有更具体的含义。

类似__attribute__((noinline))(GCC特定语法)阻止函数内联。

答案 2 :(得分:-1)

__asm__属性指定要在函数或变量的汇编代码中使用的名称。

__volatile__限定符,通常用于嵌入式系统的实时计算,解决了status registerERRORREADY的编译器测试问题在优化期间导致问题。引入__volatile__是为了告诉编译器该对象受到快速变化的影响,并强制该对象的每个引用都是真正的引用。