static inline void __set_64bit (unsigned long long * ptr,
unsigned int low, unsigned int high)
{
__asm__ __volatile__ (
"\n1:\t"
"movl (%0), %%eax\n\t"
"movl 4(%0), %%edx\n\t"
"lock cmpxchg8b (%0)\n\t"
"jnz 1b"
: /* no outputs */
: "D"(ptr),
"b"(low),
"c"(high)
: "ax","dx","memory");
}
static inline void __set_64bit_constant (unsigned long long *ptr,
unsigned long long value)
{
__set_64bit(ptr,(unsigned int)(value), (unsigned int)((value)>>32ULL));
}
#define ll_low(x) *(((unsigned int*)&(x))+0)
#define ll_high(x) *(((unsigned int*)&(x))+1)
static inline void __set_64bit_var (unsigned long long *ptr,
unsigned long long value)
{
__set_64bit(ptr,ll_low(value), ll_high(value));
}
#define set_64bit(ptr,value) \
(__builtin_constant_p(value) ? \
__set_64bit_constant(ptr, value) : \
__set_64bit_var(ptr, value) )
__builtin_constant_p
是 GCC(GNU 编译器集合)中的一个内建函数,用于在编译时确定一个表达式是否为常量表达式。这个函数对于代码优化非常有用,因为编译器可以基于表达式是否为常量做出优化决策。
语法
int __builtin_constant_p(exp)
exp
是要被评估的表达式。
描述
- 如果参数
exp
在编译时被认为是一个常量表达式,则该函数返回非零值。 - 否则,返回零。
用法
该函数通常在宏中使用,根据表达式是否为常量来优化代码。例如,可以用来选择函数的不同实现,一个用于常量表达式,另一个用于变量表达式。
示例
#define my_max(a, b) (__builtin_constant_p(a) && __builtin_constant_p(b) ? ((a) > (b) ? (a) : (b)) : runtime_max(a, b))
int runtime_max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
int x = 5;
int y = 10;
int z = my_max(x, y); // 使用 runtime_max
int w = my_max(20, 15); // 使用编译时最大值计算
}
在这个例子中,宏 my_max
检查 a
和 b
是否都是编译时常量。如果是,它使用编译时的 ? :
运算符来确定最大值。如果不是常量,则调用 runtime_max
函数在运行时确定最大值。
注意事项
- 该函数是 GCC 特有的,其他编译器可能不支持。
- 它通常用于低级编程,如系统库和嵌入式系统中,在这些场合性能优化非常关键。
在这段代码中,使用 __builtin_constant_p
来区分值是否为常量的目的是为了优化代码性能。具体原因如下:
优化常量表达式
如果 value
是一个常量表达式,编译器在编译时就可以确定它的值。因此,可以直接将 value
拆分成低32位和高32位,并传递给 __set_64bit
函数。这避免了在运行时进行拆分操作,从而提高了效率。
__set_64bit(ptr, (unsigned int)(value), (unsigned int)((value)>>32ULL) )
在这种情况下,编译器会在编译时计算出低32位和高32位,并直接生成优化后的代码。
处理非常量表达式
如果 value
不是常量表达式,则需要在运行时进行拆分。这时,使用 ll_low
和 ll_high
宏来获取 value
的低32位和高32位。
__set_64bit(ptr, ll_low(value), ll_high(value))
这段代码会在运行时通过指针操作从 value
中提取低32位和高32位。
内联汇编函数使用 cmpxchg8b
指令来原子性地设置64位值。它首先将指针 ptr
指向的内存内容加载到 eax
和 edx
寄存器,然后尝试用 low
和 high
的组合值来更新它。如果失败(即 cmpxchg8b
不成功),则循环重试。