atomics A.1b: real RMW emission (atomicrmw) + unit test (green)
emitAtomicRmw: LLVMBuildAtomicRMW (binop from RmwKind; signed Min/Max vs unsigned UMin/UMax from val_ty; singleThread=0; LLVM supplies ABI alignment). examples/1701 green (add/sub/and/or/xor/min/max return old values, results verified). Unit test 'emit: atomic rmw (add + signed/unsigned min)' locks 'atomicrmw add' + signed 'min' vs unsigned 'umin'. Suite green (716/0).
This commit is contained in:
@@ -396,13 +396,33 @@ pub const Ops = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// A.1a (Stream A) lock: emission BAILS LOUDLY until A.1b wires the real
|
||||
// LLVMBuildAtomicRMW (binop from kind; signed/unsigned Min/Max from val_ty).
|
||||
// atomicrmw returns the OLD value. The binop comes from the kind; min/max
|
||||
// pick signed vs unsigned from val_ty. singleThread stays 0. LLVM gives the
|
||||
// op the type's ABI alignment automatically (no explicit SetAlignment needed,
|
||||
// unlike plain load/store).
|
||||
fn rmwBinOp(kind: ir_inst.RmwKind, is_unsigned: bool) c.LLVMAtomicRMWBinOp {
|
||||
return switch (kind) {
|
||||
.add => c.LLVMAtomicRMWBinOpAdd,
|
||||
.sub => c.LLVMAtomicRMWBinOpSub,
|
||||
.@"and" => c.LLVMAtomicRMWBinOpAnd,
|
||||
.@"or" => c.LLVMAtomicRMWBinOpOr,
|
||||
.xor => c.LLVMAtomicRMWBinOpXor,
|
||||
.min => if (is_unsigned) c.LLVMAtomicRMWBinOpUMin else c.LLVMAtomicRMWBinOpMin,
|
||||
.max => if (is_unsigned) c.LLVMAtomicRMWBinOpUMax else c.LLVMAtomicRMWBinOpMax,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn emitAtomicRmw(self: Ops, instruction: *const Inst, a: AtomicRmw) void {
|
||||
_ = a;
|
||||
std.debug.print("error: atomic rmw LLVM emission not yet implemented (Stream A, A.1b)\n", .{});
|
||||
self.e.comptime_failed = true;
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.toLLVMType(if (instruction.ty == .void) .i64 else instruction.ty)));
|
||||
const ptr = self.e.resolveRef(a.ptr);
|
||||
const val = self.e.resolveRef(a.operand);
|
||||
const ptr_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(ptr));
|
||||
if (ptr_kind == c.LLVMPointerTypeKind and instruction.ty != .void) {
|
||||
const is_unsigned = self.e.ir_mod.types.isUnsignedInt(a.val_ty);
|
||||
const result = c.LLVMBuildAtomicRMW(self.e.builder, rmwBinOp(a.kind, is_unsigned), ptr, val, llvmOrdering(a.ordering), 0);
|
||||
self.e.mapRef(result);
|
||||
} else {
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.toLLVMType(if (instruction.ty == .void) .i64 else instruction.ty)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitAtomicStore(self: Ops, a: AtomicStore) void {
|
||||
|
||||
@@ -248,6 +248,38 @@ test "emit: atomic load/store (seq_cst, aligned)" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "align 8") != null);
|
||||
}
|
||||
|
||||
test "emit: atomic rmw (add + signed/unsigned min)" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
_ = b.beginFunction(str(&module, "f"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const si = b.alloca(.i64);
|
||||
const ui = b.alloca(.u64);
|
||||
const five = b.constInt(5, .i64);
|
||||
_ = b.emit(.{ .atomic_rmw = .{ .ptr = si, .operand = five, .val_ty = .i64, .ordering = .seq_cst, .kind = .add } }, .i64);
|
||||
_ = b.emit(.{ .atomic_rmw = .{ .ptr = si, .operand = five, .val_ty = .i64, .ordering = .seq_cst, .kind = .min } }, .i64);
|
||||
_ = b.emit(.{ .atomic_rmw = .{ .ptr = ui, .operand = five, .val_ty = .u64, .ordering = .seq_cst, .kind = .min } }, .u64);
|
||||
b.ret(five, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_rmw", .{});
|
||||
defer emitter.deinit();
|
||||
emitter.emit();
|
||||
|
||||
try std.testing.expect(emitter.verify());
|
||||
|
||||
const ir_str = emitter.dumpToString();
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "atomicrmw add") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "atomicrmw min") != null); // signed i64
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "atomicrmw umin") != null); // unsigned u64
|
||||
}
|
||||
|
||||
test "emit: comparison and branch" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
|
||||
Reference in New Issue
Block a user