扩展装配中cmpxchg16b的不可能约束

扩展装配中cmpxchg16b的不可能约束,c,gcc,x86-64,inline-assembly,lock-free,C,Gcc,X86 64,Inline Assembly,Lock Free,我试图用我的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; typ

我试图用我的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会给出此错误:

函数“cs”中的linkedlist.c:linkedlist.c:45:3:错误:“asm”中不可能存在约束(*(volatile node)

我的代码出了什么问题?如何修复

我正在尝试实现此代码的等效项:

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; 
}

P:那么,让我们来讨论一下这个问题。

在我们开始之前,有几点:

  • 使用内联asm是一个坏主意。它很难编写,很难正确编写,很难维护,不能移植到其他编译器或平台等。除非这是一个分配要求,否则不要这样做
  • 执行cmpxchg操作时,要比较/交换的字段必须是连续的。因此,如果要在单个操作中对
    next
    flag
    mark
    进行操作,它们必须在结构中相邻
  • 执行cmpxchg操作时,字段必须在适当大小的边界上对齐。例如,如果计划在16字节上操作,则数据必须在16字节边界上对齐。gcc提供了从、到_mm_malloc的多种方法
  • 使用_sync_bool_compare_和_swap(比内联asm更好的选择)时,必须将数据类型强制转换为适当大小的整数
  • 我假设你的平台是x64
  • 2和3需要对结构的字段顺序进行一些更改。请注意,我没有尝试更改
    searchfrom
    return\u 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);  
    }
    
    请注意,cas(正确!)在第一次尝试时失败,因为“旧”值与“当前”值不匹配

    虽然使用汇编程序可以为您节省一两条指令,但在可读性、可维护性、可移植性等方面的胜利几乎肯定是值得的


    如果出于某种原因,您必须使用内联asm,那么您仍然需要对结构重新排序,关于对齐的问题仍然存在。您也可以查看。它只使用8个字节,但概念是相同的。

    代码和错误消息应该在问题本身中。具体问题是x86上的“q”约束必须是a/b/c/dx,但您已经在为其他参数使用所有这些参数。也就是说,我希望推动不要使用内联asm。使用uuu sync\u bool\u compare\u和swap如何?或者甚至可能是std::atomic?我认为真正可能发生的是,
    节点lf值
    将值定义为数据结构(而不是指向数据结构的指针)而且不能放入64位寄存器。你真的想用那种方式传递
    值吗?@PeterCordes:是的,它是OCR'ed,比手工做更好;-)。我不得不修复许多问题,并且在错误消息中遗漏了一个小写的
    L
    。但是我相信代码本身是相当准确的。我确实在一条评论中指出了
    value
    的问题,询问OP是否真的打算做他们正在做的事情(显然他们不应该这样做)@MichaelPetch-是的,你是对的:问题是他正在按值返回结构。好吧,这与他试图用
    setz
    设置结构的值相结合?
    value
    将包含一个bool,指示cmpxchg16b是否成功。
    If 0000000000000000:0000000000000003 is 0000000000000000:0000000000000000 change to 0000000000000000:0000000000000000. Result 0
    If 0000000000000000:0000000000000003 is 0000000000000000:0000000000000003 change to 0000000000000000:0000000000000000. Result 1