繁体   English   中英

AArch64 程序集中的原子链表 LIFO,在 ldxr / stxr 之间使用加载或存储

[英]atomic linked-list LIFO in AArch64 assembly, using load or store between ldxr / stxr

我使用 ARMv8 64 位的程序集为共享的 memory 上下文实现了 LIFO。

LIFO 在开头插入一个节点,每个节点结构的第一个属性必须是下一个指针。

这是为 LIFO 实现原子插入和删除的正确程序集吗?

它使用LL/SC在 LDXR 和 STXR 之间进行额外的加载或存储来读取 head->next 或将指针存储到新节点中。

typedef union {
   void * head[1];
}lifo;
int atomic_lifo_init(lifo * h) {
if (h) {
   h->head[0]=NULL;
  }
}
inline void *
 atomic_lifo_delete (lifo *h)
 {
         void    *ret = NULL;
         /*sa_ignore UNUSED_VAR*/
         void *  tmp = NULL;
 
         asm volatile ("\n"
                 "2:  ldxr      %0,[%2] \n" //load the head from h, which points the 1st node of list to local ret pointer.
                 "    cbz     %0, 3f \n" //check if the lifo is empty.
                 "    ldr      %1,[%0] \n" //store in tmp the 2nd node by derefencing the ret (h->head points 1st node. value of each node is next node as 1st attribute of node structure is pointing next.)
                 "    stxr     %w1, %1,[%2] \n" //update h->head with tmp.
                 "    cbnz     %w1, 2b \n" //continue if failed
                 "3:              \n"
                 : "=&r" (ret), "=&r" (tmp)
                 : "r" (h)
                 : "memory"
                 );
         return(ret);
 }
 
 /*
  * atomic_lifo_insert()
  *      Put an element on a list, protected against SMP
  */
 void
 atomic_lifo_insert (lifo *h, void *__new)
 {
         /*sa_ignore UNUSED_VAR*/
         void * next = NULL;
         void * flag = NULL;
         asm volatile (" \n"
                 "1: ldxr      %1,[%2] \n" //load head[0] from h,which points 1st node to local next pointer
                 "   str      %1,[%3] \n" //store the local next pointer to value of __new, as 1st attribute of the any node is next (convention used here). so __new's next is pointing current 1st node.
                 "   stxr    %w0, %3,[%2] \n" //update the h->head with 
   __next.
                 "   cbnz    %w0, 1b \n" //if stxr is failure try again.
                 : "=&r" (flag), "=&r" (next)
                 : "r" (h), "r" (__new)
                 : "memory"
                 );
 }

我对 ARM 组件真的很陌生,所以非常感谢任何帮助。

您的内联 asm 约束看起来正确,这应该按照您的预期方式编译。 您可能可以使用"+m" (*h)让编译器选择寻址模式,而不是使用"r"(h)[%2]对其进行硬编码。

就排序而言, ldr在 ldxr 之后是依赖排序的(如 C11 memory_order_consume ),因此有效。

但是在stxr将地址发布到其他线程之后insert中的 LL/SC 之间的str可能不会变得可见。 所以我认为你需要stlxr (一个发布商店)在insert


在 LDXR/STXR 之间进行额外的加载或存储是不安全的。 Wikipedia 的LL/SC 文章提到,如果您在 LL 和 SC 之间进行任何加载或存储,某些实现可能会虚假失败。 Wiki 说 PowerPC 确实允许这样做。 但 AArch64 通常明确不:根据 ARM 参考手册( 请参阅@James Greenhalgh 的评论):

只有在 [...] Load-Exclusive 和 Store-Exclusive 之间没有明确的 memory 访问时,LoadExcl/StoreExcl 循环才能保证向前推进

可能有一些 AArch64 CPU 会在其中创建stxr故障的无限循环,但可能还有其他 CPU 可以正常工作。 如果它在您测试的 CPU 上实际工作,那么查看是否有任何文档支持它可能是个好主意。

如果new_节点恰好与头节点位于同一高速缓存行(或 LL/SC 独占块)中,则这很可能是一个问题。 如果这在您关心的微架构上完全有效,请确保您测试该案例,或者以某种方式使其成为不可能。


除此之外,我认为您的整体算法看起来是正确的,所以如果您已经对其进行了测试并发现它有效,那么这可能很好。

但是,我并没有真正仔细考虑过您的算法。 我也没有任何设计或使用原始 LL/SC 的经验。 我原则上知道它们是如何工作的,但我不准备说这绝对是正确的。 从我所做的一点点思考来看,我没有看到任何问题,但这并不意味着没有任何问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM