| ... | @@ -217,6 +217,31 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void { | ... | @@ -217,6 +217,31 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void { |
| 217 | return atomic_store_N(u64, dst, value, model); | 217 | return atomic_store_N(u64, dst, value, model); |
| 218 | } | 218 | } |
| 219 | | 219 | |
| | 220 | fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T { |
| | 221 | const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8); |
| | 222 | |
| | 223 | const addr = @ptrToInt(ptr); |
| | 224 | const wide_addr = addr & ~(@as(T, smallest_atomic_fetch_exch_size) - 1); |
| | 225 | const wide_ptr = @alignCast(smallest_atomic_fetch_exch_size, @intToPtr(*WideAtomic, wide_addr)); |
| | 226 | |
| | 227 | const inner_offset = addr & (@as(T, smallest_atomic_fetch_exch_size) - 1); |
| | 228 | const inner_shift = @intCast(std.math.Log2Int(T), inner_offset * 8); |
| | 229 | |
| | 230 | const mask = @as(WideAtomic, std.math.maxInt(T)) << inner_shift; |
| | 231 | |
| | 232 | var wide_old = @atomicLoad(WideAtomic, wide_ptr, .SeqCst); |
| | 233 | while (true) { |
| | 234 | const old = @truncate(T, (wide_old & mask) >> inner_shift); |
| | 235 | const new = update(val, old); |
| | 236 | const wide_new = wide_old & ~mask | (@as(WideAtomic, new) << inner_shift); |
| | 237 | if (@cmpxchgWeak(WideAtomic, wide_ptr, wide_old, wide_new, .SeqCst, .SeqCst)) |new_wide_old| { |
| | 238 | wide_old = new_wide_old; |
| | 239 | } else { |
| | 240 | return old; |
| | 241 | } |
| | 242 | } |
| | 243 | } |
| | 244 | |
| 220 | inline fn atomic_exchange_N(comptime T: type, ptr: *T, val: T, model: i32) T { | 245 | inline fn atomic_exchange_N(comptime T: type, ptr: *T, val: T, model: i32) T { |
| 221 | _ = model; | 246 | _ = model; |
| 222 | if (@sizeOf(T) > largest_atomic_size) { | 247 | if (@sizeOf(T) > largest_atomic_size) { |
| ... | @@ -227,29 +252,13 @@ inline fn atomic_exchange_N(comptime T: type, ptr: *T, val: T, model: i32) T { | ... | @@ -227,29 +252,13 @@ inline fn atomic_exchange_N(comptime T: type, ptr: *T, val: T, model: i32) T { |
| 227 | return value; | 252 | return value; |
| 228 | } else if (@sizeOf(T) < smallest_atomic_fetch_exch_size) { | 253 | } else if (@sizeOf(T) < smallest_atomic_fetch_exch_size) { |
| 229 | // Machine does not support this type, but it does support a larger type. | 254 | // Machine does not support this type, but it does support a larger type. |
| 230 | const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8); | 255 | const Updater = struct { |
| 231 | | 256 | fn update(new: T, old: T) T { |
| 232 | const addr = @ptrToInt(ptr); | 257 | _ = old; |
| 233 | const wide_addr = addr & ~(@as(T, smallest_atomic_fetch_exch_size) - 1); | 258 | return new; |
| 234 | const wide_ptr = @alignCast(smallest_atomic_fetch_exch_size, @intToPtr(*WideAtomic, wide_addr)); | | |
| 235 | | | |
| 236 | const inner_offset = addr & (@as(T, smallest_atomic_fetch_exch_size) - 1); | | |
| 237 | const inner_shift = @intCast(std.math.Log2Int(T), inner_offset * 8); | | |
| 238 | | | |
| 239 | // Put the interesting bits at the right position (branch has dynamic RHS). | | |
| 240 | const shifted_value = @as(WideAtomic, val) << inner_shift; | | |
| 241 | // Mask that guards the bits we care about | | |
| 242 | const mask = @as(WideAtomic, std.math.maxInt(T)) << inner_shift; | | |
| 243 | while (true) { | | |
| 244 | const wide_old = @atomicLoad(WideAtomic, wide_ptr, .Acquire); | | |
| 245 | // Insert new bytes in old value. | | |
| 246 | const wide_new = wide_old & ~mask | shifted_value; | | |
| 247 | // CAS the new value until the result stabilizes. | | |
| 248 | if (@cmpxchgWeak(WideAtomic, wide_ptr, wide_old, wide_new, .SeqCst, .SeqCst) == null) { | | |
| 249 | // Mask-and-Shift back the old bits to get the old value. | | |
| 250 | return @truncate(T, (wide_old & mask) >> inner_shift); | | |
| 251 | } | 259 | } |
| 252 | } | 260 | }; |
| | 261 | return wideUpdate(T, ptr, val, Updater.update); |
| 253 | } else { | 262 | } else { |
| 254 | return @atomicRmw(T, ptr, .Xchg, val, .SeqCst); | 263 | return @atomicRmw(T, ptr, .Xchg, val, .SeqCst); |
| 255 | } | 264 | } |
| ... | @@ -318,54 +327,30 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success: | ... | @@ -318,54 +327,30 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success: |
| 318 | | 327 | |
| 319 | inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T { | 328 | inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T { |
| 320 | _ = model; | 329 | _ = model; |
| | 330 | const Updater = struct { |
| | 331 | fn update(new: T, old: T) T { |
| | 332 | return switch (op) { |
| | 333 | .Add => old +% new, |
| | 334 | .Sub => old -% new, |
| | 335 | .And => old & new, |
| | 336 | .Nand => ~(old & new), |
| | 337 | .Or => old | new, |
| | 338 | .Xor => old ^ new, |
| | 339 | else => @compileError("unsupported atomic op"), |
| | 340 | }; |
| | 341 | } |
| | 342 | }; |
| | 343 | |
| 321 | if (@sizeOf(T) > largest_atomic_size) { | 344 | if (@sizeOf(T) > largest_atomic_size) { |
| 322 | var sl = spinlocks.get(@ptrToInt(ptr)); | 345 | var sl = spinlocks.get(@ptrToInt(ptr)); |
| 323 | defer sl.release(); | 346 | defer sl.release(); |
| 324 | | 347 | |
| 325 | const value = ptr.*; | 348 | const value = ptr.*; |
| 326 | ptr.* = switch (op) { | 349 | ptr.* = Updater.update(val, value); |
| 327 | .Add => value +% val, | | |
| 328 | .Sub => value -% val, | | |
| 329 | .And => value & val, | | |
| 330 | .Nand => ~(value & val), | | |
| 331 | .Or => value | val, | | |
| 332 | .Xor => value ^ val, | | |
| 333 | else => @compileError("unsupported atomic op"), | | |
| 334 | }; | | |
| 335 | | | |
| 336 | return value; | 350 | return value; |
| 337 | } else if (@sizeOf(T) < smallest_atomic_fetch_exch_size) { | 351 | } else if (@sizeOf(T) < smallest_atomic_fetch_exch_size) { |
| 338 | // Machine does not support this type, but it does support a larger type. | 352 | // Machine does not support this type, but it does support a larger type. |
| 339 | const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8); | 353 | return wideUpdate(T, ptr, val, Updater.update); |
| 340 | | | |
| 341 | const addr = @ptrToInt(ptr); | | |
| 342 | const wide_addr = addr & ~(@as(T, smallest_atomic_fetch_exch_size) - 1); | | |
| 343 | const wide_ptr = @alignCast(smallest_atomic_fetch_exch_size, @intToPtr(*WideAtomic, wide_addr)); | | |
| 344 | | | |
| 345 | const inner_offset = addr & (@as(T, smallest_atomic_fetch_exch_size) - 1); | | |
| 346 | const inner_shift = @intCast(std.math.Log2Int(T), inner_offset * 8); | | |
| 347 | | | |
| 348 | const mask = @as(WideAtomic, std.math.maxInt(T)) << inner_shift; | | |
| 349 | | | |
| 350 | while (true) { | | |
| 351 | // Compute new wide value with updated bits. | | |
| 352 | const wide_old = @atomicLoad(WideAtomic, wide_ptr, .Acquire); | | |
| 353 | const old = @truncate(T, (wide_old & mask) >> inner_shift); | | |
| 354 | const new = switch (op) { | | |
| 355 | .Add => old +% val, | | |
| 356 | .Sub => old -% val, | | |
| 357 | .And => old & val, | | |
| 358 | .Nand => ~(old & val), | | |
| 359 | .Or => old | val, | | |
| 360 | .Xor => old ^ val, | | |
| 361 | else => @compileError("unsupported atomic op"), | | |
| 362 | }; | | |
| 363 | const wide_new = wide_old & ~mask | (@as(WideAtomic, new) << inner_shift); | | |
| 364 | // CAS the new value until the result stabilizes. | | |
| 365 | if (@cmpxchgWeak(WideAtomic, wide_ptr, wide_old, wide_new, .SeqCst, .SeqCst) == null) { | | |
| 366 | return old; | | |
| 367 | } | | |
| 368 | } | | |
| 369 | } | 354 | } |
| 370 | | 355 | |
| 371 | return @atomicRmw(T, ptr, op, val, .SeqCst); | 356 | return @atomicRmw(T, ptr, op, val, .SeqCst); |