RISC-VにはいくつかのAtomic演算命令が定義されている。
Load-Reserved/Store-Conditional 命令
図. Load-Reserved/Store Conditional命令
LR命令はアドレスrs1からのデータをレジスタrdにロードし、その時のアドレスをRegister Reservationに記憶する。 SC命令はレジスタrs2のデータをアドレスrs1にストアするのだが、Register Reservationに格納されているアドレスがrs1と一致していない場合、そのストア処理は無効となる。
- ストアが無効の場合 : rdに非ゼロを格納される。
- ストアが有効な場合 : rdにゼロを格納される。
使いどころなのだが、以下のようなサンプルプログラムが掲載されている。
lr.w
で値をロードし、sc.w
でアップデートする。
# a0 holds address of memory location # a1 holds expected value # a2 holds desired value # a0 holds return value, 0 if successful, !0 otherwise cas: lr.w t0, (a0) # Load original value. bne t0, a1, fail # Doesn’t match, so fail. sc.w a0, a2, (a0) # Try to update. jr ra # Return. fail: li a0, 1 # Set return to failure. jr ra # Return.
Load-Reserved / Store-Conditional の Memory Orderingビット
上記の図のように、LR/SC命令ではaq
ビットとrl
ビットが付加されている。
aq
はacquire、rl
はreleaseと考えるとよい。
aq
: Acquire操作命令の後続のメモリアクセス命令はその命令を追い越すことができない。rl
: Release操作命令はその命令よりも前のメモリアクセス命令よりも前に実行することができない。aq
とrl
が両方1 : 完全に前後のメモリアクセス命令を追い越したり追い越されたりすることはできない。
図. RISC-V Memory Consistency Model Tutorial (https://content.riscv.org/wp-content/uploads/2018/05/14.25-15.00-RISCVMemoryModelTutorial.pdf) より抜粋。
Atomicメモリ操作命令
図. Atomicメモリ操作命令
Atomicメモリ操作は、rs1レジスタで指定されるアドレスからのデータをメモリから読み込み、rs2レジスタとの操作を行ったうえで、rs1レジスタで指定されるアドレスにデータをストアする。
Atomicメモリ操作の例外
Atomicメモリ操作命令は、2種類の例外が発生する可能性がある。
- メモリアドレスMisaligned例外 : メモリアドレスが操作するデータ型のサイズに合っていない。
- メモリページ例外 : 参照するメモリアドレスのページ変換処理に失敗する。
AMO命令にはロード処理・ストア処理が入るのだが、どちらもページ変換処理に失敗したときにはStore Page Faultが発生するらしい。 何故だ?
riscv-isa-sim/riscv/mmu.h
// template for functions that perform an atomic memory operation #define amo_func(type) \ template<typename op> \ type##_t amo_##type(reg_t addr, op f) { \ if (addr & (sizeof(type##_t)-1)) \ throw trap_store_address_misaligned(addr); \ try { \ auto lhs = load_##type(addr); \ store_##type(addr, f(lhs)); \ return lhs; \ } catch (trap_load_page_fault& t) { \ /* AMO faults should be reported as store faults */ \ throw trap_store_page_fault(t.get_tval()); \ } catch (trap_load_access_fault& t) { \ /* AMO faults should be reported as store faults */ \ throw trap_store_access_fault(t.get_tval()); \ } \ }