mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
125 lines
3.3 KiB
Zig
125 lines
3.3 KiB
Zig
//! Atomic barrier implementations on various architectures.
|
|
//! This is needed since Zig removed @fence.
|
|
|
|
const builtin = @import("builtin");
|
|
|
|
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
|
|
pub inline fn fence(comptime ordering: std.builtin.AtomicOrder) void {
|
|
switch (ordering) {
|
|
.seq_cst, .acq_rel => mb(),
|
|
.acquire => acq(),
|
|
.release => rel(),
|
|
else => @compileError("Unsupported ordering"),
|
|
}
|
|
}
|
|
|
|
// Full memory barrier
|
|
inline fn mb() void {
|
|
switch (builtin.cpu.arch) {
|
|
.x86_64 => {
|
|
// Faster than mfence
|
|
asm volatile ("lock addl $0, (%%rsp)" ::: .{ .memory = true });
|
|
},
|
|
.x86 => {
|
|
asm volatile ("lock addl $0, (%%esp)" ::: .{ .memory = true });
|
|
},
|
|
.aarch64, .arm => {
|
|
asm volatile ("dmb ish" ::: .{ .memory = true });
|
|
},
|
|
.riscv64, .riscv32 => {
|
|
asm volatile ("fence rw,rw" ::: .{ .memory = true });
|
|
},
|
|
else => @compileError("unsupported architecture"),
|
|
}
|
|
}
|
|
|
|
// Acquire barrier
|
|
inline fn acq() void {
|
|
switch (builtin.cpu.arch) {
|
|
.x86, .x86_64 => {
|
|
asm volatile ("" ::: .{ .memory = true });
|
|
},
|
|
.aarch64, .arm => {
|
|
asm volatile ("dmb ishld" ::: .{ .memory = true });
|
|
},
|
|
.riscv64, .riscv32 => {
|
|
asm volatile ("fence r,rw" ::: .{ .memory = true });
|
|
},
|
|
else => @compileError("unsupported architecture"),
|
|
}
|
|
}
|
|
|
|
// Release barrier
|
|
inline fn rel() void {
|
|
switch (builtin.cpu.arch) {
|
|
.x86, .x86_64 => {
|
|
asm volatile ("" ::: .{ .memory = true });
|
|
},
|
|
.aarch64, .arm => {
|
|
asm volatile ("dmb ish" ::: .{ .memory = true });
|
|
},
|
|
.riscv64, .riscv32 => {
|
|
asm volatile ("fence rw,w" ::: .{ .memory = true });
|
|
},
|
|
else => @compileError("unsupported architecture"),
|
|
}
|
|
}
|
|
|
|
/// Copy to dest from src with atomic loads.
|
|
/// Both addresses need to be aligned.
|
|
pub fn atomic_load_memcpy(
|
|
dest: anytype,
|
|
src: anytype,
|
|
comptime ordering: std.builtin.AtomicOrder,
|
|
) void {
|
|
const T = @TypeOf(dest.*);
|
|
const Word = switch (@alignOf(T)) {
|
|
1 => u8,
|
|
2 => u16,
|
|
4 => u32,
|
|
else => u64,
|
|
};
|
|
|
|
const word_size = @sizeOf(Word);
|
|
const n = @sizeOf(T);
|
|
comptime assert(n % word_size == 0);
|
|
|
|
const src_words: [*]const volatile Word = @ptrCast(src);
|
|
const dest_words: [*]Word = @ptrCast(dest);
|
|
|
|
var i: usize = 0;
|
|
while (i < n / word_size) : (i += 1) {
|
|
dest_words[i] = @atomicLoad(Word, &src_words[i], ordering);
|
|
}
|
|
}
|
|
|
|
/// Copy to dest from src with atomic stores.
|
|
/// Both addresses need to be aligned.
|
|
pub fn atomic_store_memcpy(
|
|
dest: anytype,
|
|
src: anytype,
|
|
comptime ordering: std.builtin.AtomicOrder,
|
|
) void {
|
|
const T = @TypeOf(dest.*);
|
|
const Word = switch (@alignOf(T)) {
|
|
1 => u8,
|
|
2 => u16,
|
|
4 => u32,
|
|
else => u64,
|
|
};
|
|
|
|
const word_size = @sizeOf(Word);
|
|
const n = @sizeOf(T);
|
|
comptime assert(n % word_size == 0);
|
|
|
|
const src_words: [*]const volatile Word = @ptrCast(src);
|
|
const dest_words: [*]Word = @ptrCast(dest);
|
|
|
|
var i: usize = 0;
|
|
while (i < n / word_size) : (i += 1) {
|
|
@atomicStore(Word, &dest_words[i], src_words[i], ordering);
|
|
}
|
|
}
|