扩展程序集中 cmpxchg16b 的不可能约束
Impossible constraint with cmpxchg16b in extended assembly
我正在尝试用我的 C 代码编写内联汇编来执行比较和交换操作。我的代码是:
typedef struct node {
int data;
struct node * next;
struct node * backlink;
int flag;
int mark;
} node_lf;
typedef struct searchfrom {
node_lf * current;
node_lf * next;
} return_sf;
typedef struct csArg {
node_lf * node;
int mark;
int flag;
} cs_arg;
typedef struct return_tryFlag {
node_lf * node;
int result;
} return_tf;
static inline node_lf cs(node_lf * address, cs_arg *old_val, cs_arg *new_val)
{
node_lf value = *address;
__asm__ __volatile__("lock; cmpxchg16b %0; setz %1;"
:"=m"(*(volatile node_lf *)address),
"=q"(value)
:"m"(*(volatile node_lf *)address),
"a"(old_val->mark), "d"(old_val->flag),
"b"(new_val->mark), "c"(new_val->flag)
:"memory");
return value;
}
GCC 在编译代码时给出这个错误:
linkedlist.c: In function 'cs': linkedlist.c:45:3: error: impossible constraint in 'asm' __asm__ __volatile__
("lock; cmpxchg16b %0; setz %1;":"=m"(*(volatile node_lf
我的代码有什么问题?我该如何解决这个问题?
我正在尝试实现与此代码等效的代码:
node_lf cs (node_lf * address, cs_arg *old_val, cs_arg *new_val ) {
node_lf value = *address;
if (value.next == old_val->node && value.mark == old_val->mark &&
value.flag == old_val->flag) {
address->next = new_val->node;
address->mark = new_val->mark;
address->flag = new_val->flag;
}
return value;
}
所以,让我们来尝试一下。
开始之前的几点说明:
- 使用内联 asm 是个坏主意。它很难编写,很难正确编写,很难维护,不能移植到其他编译器或平台等。除非这是分配要求,否则不要这样做。
- 执行cmpxchg 操作时,要compared/exchanged 的字段必须是连续的。因此,如果要在单个操作中对
next
、flag
和 mark
进行操作,它们必须在结构中彼此相邻。
- 执行 cmpxchg 操作时,字段必须在适当大小的边界上对齐。例如,如果您计划对 16 字节进行操作,则数据必须在 16 字节边界上对齐。 gcc 提供了从 aligned attribute 到 _mm_malloc.
的多种方法
- 当使用 __sync_bool_compare_and_swap(比内联 asm 更好的选择)时,您必须将数据类型转换为适当大小的整数。
- 我假设您的平台是 x64。
2 和 3 需要对结构的字段顺序进行一些更改。请注意,我没有尝试更改 searchfrom
或 return_tryFlag
,因为我不确定它们的用途。
所以,考虑到这些,我得出以下结论:
#include <stdio.h>
#include <memory.h>
typedef struct node {
struct node * next;
int mark;
int flag;
struct node * backlink;
int data;
} node_lf;
typedef struct csArg {
node_lf * node;
int mark;
int flag;
} cs_arg;
bool cs3(node_lf * address, cs_arg *old_val, cs_arg *new_val) {
return __sync_bool_compare_and_swap((unsigned __int128 *)address,
*(unsigned __int128 *)old_val,
*(unsigned __int128 *)new_val);
}
void ShowIt(void *v)
{
unsigned long long *ull = (unsigned long long *)v;
printf("%p:%p", *ull, *(ull + 1));
}
int main()
{
cs_arg oldval, newval;
node n;
memset(&oldval, 0, sizeof(oldval));
memset(&newval, 0, sizeof(newval));
memset(&n, 0, sizeof(node));
n.mark = 3;
newval.mark = 4;
bool b;
do {
printf("If "); ShowIt(&n); printf(" is "); ShowIt(&oldval); printf(" change to "); ShowIt(&newval);
b = cs3(&n, &oldval, &newval);
printf(". Result %d\n", b);
if (b)
break;
memcpy(&oldval, &n, sizeof(cs_arg));
} while (1);
}
当你退出循环时,oldval 将是之前的内容(必须是,否则 cas 会失败,我们会再次循环),newval 将是实际写入的内容。请注意,如果这确实是多线程的,则无法保证 newval 与 n 的当前内容相同,因为另一个线程可能已经出现并再次更改它。
对于输出我们得到:
If 0000000000000000:0000000000000003 is 0000000000000000:0000000000000000 change to 0000000000000000:0000000000000000. Result 0
If 0000000000000000:0000000000000003 is 0000000000000000:0000000000000003 change to 0000000000000000:0000000000000000. Result 1
请注意,cas(正确!)在第一次尝试时失败,因为 'old' 值与 'current' 值不匹配。
虽然使用汇编程序可能会为您节省一两条指令,但在可读性、可维护性、可移植性等方面的胜利几乎肯定是值得的。
如果出于某种原因你必须使用内联汇编,你仍然需要重新排序你的结构,关于对齐的要点仍然存在。你也可以看看。它只使用8个字节,但概念是一样的。
我正在尝试用我的 C 代码编写内联汇编来执行比较和交换操作。我的代码是:
typedef struct node {
int data;
struct node * next;
struct node * backlink;
int flag;
int mark;
} node_lf;
typedef struct searchfrom {
node_lf * current;
node_lf * next;
} return_sf;
typedef struct csArg {
node_lf * node;
int mark;
int flag;
} cs_arg;
typedef struct return_tryFlag {
node_lf * node;
int result;
} return_tf;
static inline node_lf cs(node_lf * address, cs_arg *old_val, cs_arg *new_val)
{
node_lf value = *address;
__asm__ __volatile__("lock; cmpxchg16b %0; setz %1;"
:"=m"(*(volatile node_lf *)address),
"=q"(value)
:"m"(*(volatile node_lf *)address),
"a"(old_val->mark), "d"(old_val->flag),
"b"(new_val->mark), "c"(new_val->flag)
:"memory");
return value;
}
GCC 在编译代码时给出这个错误:
linkedlist.c: In function 'cs': linkedlist.c:45:3: error: impossible constraint in 'asm'
__asm__ __volatile__
("lock; cmpxchg16b %0; setz %1;":"=m"(*(volatile node_lf
我的代码有什么问题?我该如何解决这个问题?
我正在尝试实现与此代码等效的代码:
node_lf cs (node_lf * address, cs_arg *old_val, cs_arg *new_val ) {
node_lf value = *address;
if (value.next == old_val->node && value.mark == old_val->mark &&
value.flag == old_val->flag) {
address->next = new_val->node;
address->mark = new_val->mark;
address->flag = new_val->flag;
}
return value;
}
所以,让我们来尝试一下。
开始之前的几点说明:
- 使用内联 asm 是个坏主意。它很难编写,很难正确编写,很难维护,不能移植到其他编译器或平台等。除非这是分配要求,否则不要这样做。
- 执行cmpxchg 操作时,要compared/exchanged 的字段必须是连续的。因此,如果要在单个操作中对
next
、flag
和mark
进行操作,它们必须在结构中彼此相邻。 - 执行 cmpxchg 操作时,字段必须在适当大小的边界上对齐。例如,如果您计划对 16 字节进行操作,则数据必须在 16 字节边界上对齐。 gcc 提供了从 aligned attribute 到 _mm_malloc. 的多种方法
- 当使用 __sync_bool_compare_and_swap(比内联 asm 更好的选择)时,您必须将数据类型转换为适当大小的整数。
- 我假设您的平台是 x64。
2 和 3 需要对结构的字段顺序进行一些更改。请注意,我没有尝试更改 searchfrom
或 return_tryFlag
,因为我不确定它们的用途。
所以,考虑到这些,我得出以下结论:
#include <stdio.h>
#include <memory.h>
typedef struct node {
struct node * next;
int mark;
int flag;
struct node * backlink;
int data;
} node_lf;
typedef struct csArg {
node_lf * node;
int mark;
int flag;
} cs_arg;
bool cs3(node_lf * address, cs_arg *old_val, cs_arg *new_val) {
return __sync_bool_compare_and_swap((unsigned __int128 *)address,
*(unsigned __int128 *)old_val,
*(unsigned __int128 *)new_val);
}
void ShowIt(void *v)
{
unsigned long long *ull = (unsigned long long *)v;
printf("%p:%p", *ull, *(ull + 1));
}
int main()
{
cs_arg oldval, newval;
node n;
memset(&oldval, 0, sizeof(oldval));
memset(&newval, 0, sizeof(newval));
memset(&n, 0, sizeof(node));
n.mark = 3;
newval.mark = 4;
bool b;
do {
printf("If "); ShowIt(&n); printf(" is "); ShowIt(&oldval); printf(" change to "); ShowIt(&newval);
b = cs3(&n, &oldval, &newval);
printf(". Result %d\n", b);
if (b)
break;
memcpy(&oldval, &n, sizeof(cs_arg));
} while (1);
}
当你退出循环时,oldval 将是之前的内容(必须是,否则 cas 会失败,我们会再次循环),newval 将是实际写入的内容。请注意,如果这确实是多线程的,则无法保证 newval 与 n 的当前内容相同,因为另一个线程可能已经出现并再次更改它。
对于输出我们得到:
If 0000000000000000:0000000000000003 is 0000000000000000:0000000000000000 change to 0000000000000000:0000000000000000. Result 0
If 0000000000000000:0000000000000003 is 0000000000000000:0000000000000003 change to 0000000000000000:0000000000000000. Result 1
请注意,cas(正确!)在第一次尝试时失败,因为 'old' 值与 'current' 值不匹配。
虽然使用汇编程序可能会为您节省一两条指令,但在可读性、可维护性、可移植性等方面的胜利几乎肯定是值得的。
如果出于某种原因你必须使用内联汇编,你仍然需要重新排序你的结构,关于对齐的要点仍然存在。你也可以看看