X86 xchgl和cmpxchgl指令替换案例分享
1 问题背景
客户迁移过程中,编译自研代码时,有如果两个编译报错:
Ø 不识别xchgl汇编指令
{standard input}: Assembler messages:
{standard input}:1222: Error: unknown mnemonic `xchgl' -- `xchgl x1,[x19,112]'
{standard input}:1225: Error: unknown mnemonic `xchgl' -- `xchgl x0,[x19,88]'
Ø 不识别cmpxchgl汇编指令
{standard input}: Assembler messages:
{standard input}:1222: Error: unknown mnemonic `cmpxchgl'
2 原因分析
xchgl和 cmpxchgl都是X86上的指令集,ARM64上不识别,需要进行替换。xchgl指令的作用是交换 (寄存器/内存变量)和 (寄存器) 的值。如果交换的两个变量中有内存变量,会对内存变量增加原子锁操作。详细见以下释义:
cmpxchgl指令的作用是比较并交换两数。详细见官网释义:
3 解决方案
3.1 内存屏障选择
xchgl和 cmpxchgl这两个指令在ARM64上可以用GCC的原子操作接口进行替换。在使用GCC内置的原子操作函数__atomic_xxxx_n时,输出参数包含memory order(即通常我们所说的内存屏障)。GCC4.7前,__sync 同步原语中默认的内存模型为full barrier模型,__sync原语前后的读写操作均不可做指令重排。为提高流水线执行效率,GCC 4.7合入C++11的内存模型,通过__atomic 同步原语,由使用者控制需要的屏障级别。对多线程访问临界区的逻辑不清晰时,建议仍使用__ATOMIC_SEQ_CST屏障,避免由屏障使用不当带来一致性问题。下面是几种可选的屏障类型及其简要介绍:
Memory order | Instroduction |
__ATOMIC_RELAXED | __ATOMIC_RELAXED |
__ATOMIC_CONSUME | load操作,当前线程依赖该原子变量的访存操作不能reorder到该指令之前,对其他线程store操作(release)可见 |
__ATOMIC_ACQUIRE | load操作,当前线程所有访存操作不能reorder到该指令之前,对其他线程store操作(release)可见 |
__ATOMIC_RELEASE | store操作,当前线程所有访存操作不能reorder到该指令之后,对其他线程load操作(consume)可见 |
__ATOMIC_ACQ_REL | load/store操作,memory_order_acquire + memory_order_release |
__ATOMIC_SEQ_CST | memory_order_acq_rel + 顺序一致性(sequential consisten) |
3.2 xchgl替换方法
xchgl在交换两值时,其中有一个是内存变量,替换时要对内存变量加原子锁。arm上没有可以完全替换的汇编指令,可以使用GCC的原子操作接口__atomic_exchange_n进行替换。
X86实现样例:
inline int nBasicAtomicInt::fetchAndStoreOrdered(int newValue)
{
/* 原子操作, 把_value的值和newValue
* 交换, 且返回_value原来的值
*/
asm volatile("xchgl %0,%1"
: "=r" (newValue), "+m" (m_value)
: "0" (newValue)
: "memory");
return newValue;
}
TaiShan上可替换成:
inline int nBasicAtomicInt::fetchAndStoreOrdered(int newValue)
{
/* 原子操作, 把_value的值和newValue
* 交换, 且返回_value原来的值
*/
return __atomic_exchange_n(&_q_value, newValue, __ATOMIC_SEQ_CST);
}
3.3 cmpxchgl替换方法
cmpxchgl可用GCC的原子操作接口__atomic_compare_exchange_n进行替换。与xchgl类似,GCC原子操作接口的参数, 使用者可以根据自身代码逻辑选择合适的参数。
X86实现样例:
inline bool nBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue)
{
unsigned char ret;
/* 原子操作, 原来m_value的值如果等于expectedValue,则把newValue
* 载入m_value, 且返回ret=true; 如果不等于,则m_value的值不变,且返回ret=false
*/
asm volatile("lock\n"
"cmpxchgl %3,%2\n"
"sete %1\n"
: "=a" (newValue), "=qm" (ret), "+m" (m_value)
: "r" (newValue), "0" (expectedValue)
: "memory");
return ret != 0;
}
TaiShan上可替换成:
inline bool nBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue)
{
unsigned char ret;
/* 原子操作, 原来m_value的值如果等于expectedValue,则把newValue载入_value, 且返回ret=true; 如果不等于,则m_value的值不变,且返回ret=false */
return __atomic_compare_exchange_n(&m_value, &expectedValue, newValue, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
}
4 总结
客户自研代码迁移时,如果遇到汇编指令报错(编译日志中有“Assembler messages”关键字),而ARM64上没有功能完全匹配的汇编指令时,除了可以用汇编指令的组合替换之外,还可以尝试使用GCC的内置函数替换。
- 点赞
- 收藏
- 关注作者
评论(0)