1

# loongarch架构介绍#[二]内存模型和相关指令-开源基础软件社区-51CTO.COM

 1 year ago
source link: https://ost.51cto.com/posts/20395
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

作者:蒋卫峰 李涛

前面一篇文章中介绍了loongarch架构中的基础部分,包括基础的整数运算指令、浮点运算指令、访存指令等,以及loongarch架构中的一些寄存器约定和汇编写法。

这篇文章则主要介绍loongarch架构中内存一致性模型相关信息,以及原子指令和栅障指令的含义和使用方法。本文会先介绍内存一致性模型相关背景知识,然后介绍目前loongarch资料中内存一致性模型相关的信息,再介绍loongarch中的原子指令和栅障指令,最后结合spinlock的实现说明原子指令和栅障指令的使用方法。

1. 内存一致性模型

内存一致性模型(memory consistency model),简称内存模型(memory model),是描述程序的与shared memory相关的访存指令执行顺序行为的模型。对于一段程序,内存模型能够指出哪些访存指令执行顺序是cpu允许存在的,哪些执行顺序是不可能发生的。

对于一般应用程序的开发人员来说,内存一致性模型是透明的,代码看上去是顺序执行的,在多线程中通过调用系统封装的互斥锁等就可实现同步。

但对于系统开发人员,内存一致性模型需要了解,这关系到如底层汇编中自旋锁实现等应用场景。

1.1 SC、TSO和RMO

这里列举出一些常见的内存一致性模型:

  • Sequential Consistency (SC):顺序一致性模型,不打乱访存指令顺序
  • Total Store Order (TSO):一种强序模型。x86的内存一致性模型类似于TSO
  • Relaxed Memory Order (RMO):弱序模型。如arm中支持一种RMO

其中Load表示加载内存操作,Store表示写入内存操作。

除了SC模型外,其他几种都打乱了访存指令的执行顺序,这主要是硬件上性能因素的考虑。

下面对这些模型中定义的访存指令顺序进行介绍。

1.1.1 SC中访存指令顺序

对于Load和Store操作,实际上总共有4种顺序:

  • Load->Load

  • Load->Store

  • Store->Store

  • Store->Load

SC模型保证以上所有的顺序,即访存指令实际的执行顺序与其在程序中的先后顺序相同。

下表为两个cpu核并行运行的例子:

Core C1 Core C2 说明
S1: x = NEW
L1: r1 = y
S2: y = NEW
L2: r2 = x
x, y初始为0

其中,S1和S2为Store操作,L1和L2为Load操作,x和y是cpu核C1和C2中共享的变量,r1和r2是cpu核C1和C2中的局部变量。

SC中所有可能的执行顺序如下:

  1. S1->L1->S2->L2,结果(r1, r2) = (0, NEW)

  2. S2->L2->S1->L1,结果(r1, r2) = (NEW, 0)

  3. S1,S2->L1,L2,结果(r1, r2) = (NEW, NEW)。其中S1和S2之间的顺序,以及L1和L2之间的顺序不确定

结果与一般程序员视角中的模型相同。

1.1.2 TSO中访存指令顺序

TSO中保证的访存指令顺序如下:

  • Load->Load

  • Load->Store

  • Store->Store

其中对于Store->Load可能会被打乱顺序。

下面用与上文SC中相同的例子进行说明:

Core C1 Core C2 说明
S1: x = NEW
L1: r1 = y
S2: y = NEW
L2: r2 = x
x, y初始为0

TSO中所有可能的执行顺序如下:

  1. S1->L1->S2->L2,结果(r1, r2) = (0, NEW)

  2. S2->L2->S1->L1,结果(r1, r2) = (NEW, 0)

  3. S1,S2->L1,L2,结果(r1, r2) = (NEW, NEW)

  4. L1,L2->S1,S2,结果(r1, r2) = (0, 0)

可以看到,TSO中可以出现Load比Store先执行的情况,从而导致结果r1和r2均为0。

1.1.3 RMO中访存指令顺序

在RMO中允许更进一步的乱序执行,如只保证以下顺序:

  • Load->Load,且地址相同

  • Load->Store,且地址相同

  • Store->Store,且地址相同

1.2 同步方法

由于如TSO、RMO等内存模型中访存指令的执行顺序不确定,因此硬件上提供了利用原子指令和栅障指令(称为fence或barrier等)来进行同步。程序中将需要同步的地方手动将其用原子指令和栅障指令进行标记,就能将目标代码同步。

下面用一个例子进行说明:

Core C1 Core C2
S1: data1 = NEW
S2: data2 = NEW
F1: FENCE
S3: flag = SET
L1: r1 = flag
B1: if (r1 != SET) goto L1
F2: FENCE
L2: r2 = data1
L3: r3 = data2

其中FENCE指令保证前后的访存指令顺序一致。最后访存指令的顺序为:S1,S2->F1->S3->L1->F2->L2,L3,结果(r1, r2, r3) = (SET, NEW, NEW)。

如果中间的FENCE指令去掉,那么可能会出现r2和r3为0的情况。

2. loongarch内存模型相关信息

2.1 loongarch内存一致性模型

根据loongarch架构手册中相关信息,其采用弱一致性(Weakly Consistency)的模型。在该模型下,程序员必须通过同步指令将对写共享单元的访问保护起来,以保证多个处理器核对于写共享单元的访问是互斥的。

虽然目前的资料中无法得知loongarch内存一致性模型的具体信息,但可以推测其应该打乱了一些访存指令的顺序,需要借助栅障指令(即fence或barrier指令)进行同步。

2.2 loongarch内存访问类型

loongarch架构下有三种内存访问类型:

  • 一致可缓存(Coherent Cached)

  • 强序非缓存(Strongly-ordered UnCached)

  • 弱序非缓存(Weakly-ordered UnCached)

例如,在页表项中的MAT(Memory Access Type)域中可以设置对应内存区域的访问类型。MAT值与内存访问类型的关系如下:

  • 0对应强序非缓存

  • 1对应一致可缓存

  • 2对应弱序非缓存

通过设置内存访问类型,相当于规定了一片内存区域的内存一致性模型。loongarch手册资料中指出,只有强序非缓存没有副作用,即不打乱访存指令的顺序。

不过loongarch资料中内存一致性模型和内存访问类型并没有详细解释,很多地方存在疑点,只能推测其作用类似于arm上的Normal、Device、Strongly-ordered几种内存模型。

3. loongarch相关指令

3.1 栅障指令

loongarch中栅障指令如下:

  • dbar指令:dbar hint。dbar指令用于完成load/store访存操作之间的栅障功能,其中hint用于指示dbar指令的同步对象和同步程度。

    hint为0在loongarch中是必须实现的,如果没有专门的功能实现,其他所有的hint值都将视为hint=0执行。hint为0指示一个完全功能的同步栅障,只有等到之前所有load/store操作执行完后,dbar 0才会执行;且只有dbar 0执行后,其后所有的load/store操作才能执行。

  • ibar指令:ibar hint。ibar指令用于完成单个处理器核内部store操作和取指操作之间的同步,其中hint用于指示dbar指令的同步对象和同步程度。

    同样的,hint为0在loongarch中是必须实现的。ibar 0确保其后的取指一定能够观察到ibar 0指令之前所有store操作的执行效果。

3.2 原子指令

loongarch中原子指令如下:

  • amswap、amadd、ammax等指令:普通原子指令,能够原子地完成对某个内存单元的“读-修改-写”过程。

    amadd.w rd, rk, rj表示将rj寄存器值作为地址,从该地址读取值写入rd,然后将rd与rk进行加法操作,最后将结果写回该地址,rd中保存该地址中的旧值。整个过程是原子的。

  • amswap_db、amadd_db、ammax_db等指令:带数据栅障功能的原子指令。除了和上面普通原子指令的功能外,还有dbar指令的效果。

  • ll/sc指令:ll/sc一对指令能够完成“读-修改-写”原子操作。

    其作用机制为:ll指令能够完成读操作,并同时记录访问地址和在相关寄存器中置上一个标记(LLbit置为1)。sc指令能够完成写操作,并会先检查寄存器中的LLbit,只有当LLbit为1时才进行写入。而ll和sc之间的指令完成自定义的修改操作。

    在ll/sc指令期间,如果其他处理器核中对该ll/sc指令操作的相同地址进行了写入,那么LLbit就会被清0,这样sc指令也会返回失败。这样的硬件检查机制保证了原子性。

    另外,ll/sc指令也有栅障功能。

    具体使用方法参见下文中spinlock实现。

loongarch原子指令的特点与一些RISC架构的原子指令相似,也类似的用ll/sc这样的指令来代替了CAS(compare-and-swap)类型原子指令。ll/sc指令相比于CAS类指令,CAS指令实现锁时可能会有ABA问题,而使用ll/sc指令可以避免这个问题。

4. 应用场景:spinlock

下面通过spinlock的实现,说明原子指令和栅障指令的应用。

作为对比,首先介绍linux3.2中arm下spinlock的实现。

lock操作函数如下:

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned long tmp;

    __asm__ __volatile__(
// 将lock->lock加载到tmp,ldrex和strex为一对原子指令
// lock是函数参数,lock->lock为整型数据
"1:    ldrex    %0, [%1]\n"
// 判断tmp是否为0
"    teq    %0, #0\n"

// 失败则执行wfe,等待事件将其唤醒
    WFE("ne")
    |   // 省略了一些实现细节
    |-> #define WFE(cond)    ALT_SMP("wfe" cond, "nop")

// 成功则将1写入lock->lock,strexeq的结果保存在tmp
"    strexeq    %0, %2, [%1]\n"
// 判断tmp的值,如果strexeq成功,tmp为0,否则为1
"    teqeq    %0, #0\n"

// 如果strexeq失败,则跳转到前面标签1,ldrex指令处
"    bne    1b"
    : "=&r" (tmp)
    : "r" (&lock->lock), "r" (1)
    : "cc");

// barrier指令
    smp_mb();
}

unlock操作函数如下:

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
    // barrier指令
    smp_mb();

    // 简单的将lock->lock置1
    __asm__ __volatile__(
"    str    %1, [%0]\n"
    :
    : "r" (&lock->lock), "r" (0)
    : "cc");

    // dsb和sev指令
    // dsb为barrier,保证str执行完毕后才执行sev
    // sev发送事件唤醒正在等待事件的cpu核
    dsb_sev();
}

可以看到,arm中也有类似的原子指令和栅障指令。其中ldrex/strex指令类似于loongarch中的ll/sc指令。并且lock和unlock函数之后均有栅障指令进行同步。

类似地,loongarch中spinlock也可以仿照上述代码实现:

spin_lock:
    // 将lock值加载到寄存器t0,假设参数寄存器a0中为传入的参数lock
1:  ll.d    t0, a0, 0
    // 如果lock不为0则跳转
    bnez    t0, 1b

    // 将t0置1并写入lock表示上锁,t0保存sc执行结果
    li.d    t0, 1
    sc.d    t0, a0, 0

    // 如果sc失败则跳转
    bnez    t0, 1b

    // barrier
    dbar    0

spin_unlock:
    dbar    0
    // 将参数lock写入0
    st.d    zero, a0, 0

本文介绍了内存一致性模型、loongarch架构中内存一致性模型相关信息、原子指令和栅障指令的含义和使用方法,并结合自旋锁的案例进行了说明。同时发现,因为目前市场上得到的资料有限,loongarch架构内存一致性模型中还有一些不清楚的地方,需要后面相关厂家完善。

更多原创内容请关注:深开鸿技术团队

入门到精通、技巧到案例,系统化分享OpenHarmony开发技术,欢迎投稿和订阅,让我们一起携手前行共建生态。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK