authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-27 08:26:35-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-27 08:26:35-07:00
logaa3405aabc539c66a9f110bc044924db07e892b6
treeb01a89cae7a85a3552235115b37fac3d51a43e4b
parent8c52c6ec6c4d6648ae4e2f11dd620d95599f4039
parent1d971817031f046990d60d1808ca48e73b838929
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15474 from Luukdegram/wasm-atomics

wasm: implement atomic instructions

3 files changed, 424 insertions(+), 23 deletions(-)

src/arch/wasm/CodeGen.zig+342-9
......@@ -940,6 +940,20 @@ fn addMemArg(func: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOf
940940 try func.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
941941}
942942
943/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the
944/// given `tag`.
945fn addAtomicMemArg(func: *CodeGen, tag: wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
946 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));
947 _ = try func.addExtra(mem_arg);
948 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
949}
950
951/// Helper function to emit atomic mir opcodes.
952fn addAtomicTag(func: *CodeGen, tag: wasm.AtomicsOpcode) error{OutOfMemory}!void {
953 const extra_index = try func.addExtra(@as(struct { val: u32 }, .{ .val = wasm.atomicsOpcode(tag) }));
954 try func.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
955}
956
943957/// Appends entries to `mir_extra` based on the type of `extra`.
944958/// Returns the index into `mir_extra`
945959fn addExtra(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
......@@ -1958,15 +1972,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19581972 .is_err_ptr,
19591973 .is_non_err_ptr,
19601974
1961 .cmpxchg_weak,
1962 .cmpxchg_strong,
1963 .fence,
1964 .atomic_load,
1965 .atomic_store_unordered,
1966 .atomic_store_monotonic,
1967 .atomic_store_release,
1968 .atomic_store_seq_cst,
1969 .atomic_rmw,
19701975 .err_return_trace,
19711976 .set_err_return_trace,
19721977 .save_err_return_trace_index,
......@@ -1979,6 +1984,18 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19791984 .c_va_start,
19801985 => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
19811986
1987 .atomic_load => func.airAtomicLoad(inst),
1988 .atomic_store_unordered,
1989 .atomic_store_monotonic,
1990 .atomic_store_release,
1991 .atomic_store_seq_cst,
1992 // in WebAssembly, all atomic instructions are sequentially ordered.
1993 => func.airAtomicStore(inst),
1994 .atomic_rmw => func.airAtomicRmw(inst),
1995 .cmpxchg_weak => func.airCmpxchg(inst),
1996 .cmpxchg_strong => func.airCmpxchg(inst),
1997 .fence => func.airFence(inst),
1998
19821999 .add_optimized,
19832000 .addwrap_optimized,
19842001 .sub_optimized,
......@@ -6636,3 +6653,319 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
66366653
66376654 return func.finishAir(inst, result, &.{ty_op.operand});
66386655}
6656
6657inline fn useAtomicFeature(func: *const CodeGen) bool {
6658 return std.Target.wasm.featureSetHas(func.target.cpu.features, .atomics);
6659}
6660
6661fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6662 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
6663 const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
6664
6665 const ptr_ty = func.air.typeOf(extra.ptr);
6666 const ty = ptr_ty.childType();
6667 const result_ty = func.air.typeOfIndex(inst);
6668
6669 const ptr_operand = try func.resolveInst(extra.ptr);
6670 const expected_val = try func.resolveInst(extra.expected_value);
6671 const new_val = try func.resolveInst(extra.new_value);
6672
6673 const cmp_result = try func.allocLocal(Type.bool);
6674
6675 const ptr_val = if (func.useAtomicFeature()) val: {
6676 const val_local = try func.allocLocal(ty);
6677 try func.emitWValue(ptr_operand);
6678 try func.lowerToStack(expected_val);
6679 try func.lowerToStack(new_val);
6680 try func.addAtomicMemArg(switch (ty.abiSize(func.target)) {
6681 1 => .i32_atomic_rmw8_cmpxchg_u,
6682 2 => .i32_atomic_rmw16_cmpxchg_u,
6683 4 => .i32_atomic_rmw_cmpxchg,
6684 8 => .i32_atomic_rmw_cmpxchg,
6685 else => |size| return func.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),
6686 }, .{
6687 .offset = ptr_operand.offset(),
6688 .alignment = ty.abiAlignment(func.target),
6689 });
6690 try func.addLabel(.local_tee, val_local.local.value);
6691 _ = try func.cmp(.stack, expected_val, ty, .eq);
6692 try func.addLabel(.local_set, cmp_result.local.value);
6693 break :val val_local;
6694 } else val: {
6695 if (ty.abiSize(func.target) > 8) {
6696 return func.fail("TODO: Implement `@cmpxchg` for types larger than abi size of 8 bytes", .{});
6697 }
6698 const ptr_val = try WValue.toLocal(try func.load(ptr_operand, ty, 0), func, ty);
6699
6700 try func.lowerToStack(ptr_operand);
6701 try func.lowerToStack(new_val);
6702 try func.emitWValue(ptr_val);
6703 _ = try func.cmp(ptr_val, expected_val, ty, .eq);
6704 try func.addLabel(.local_tee, cmp_result.local.value);
6705 try func.addTag(.select);
6706 try func.store(.stack, .stack, ty, 0);
6707
6708 break :val ptr_val;
6709 };
6710
6711 const result_ptr = if (isByRef(result_ty, func.target)) val: {
6712 try func.emitWValue(cmp_result);
6713 try func.addImm32(-1);
6714 try func.addTag(.i32_xor);
6715 try func.addImm32(1);
6716 try func.addTag(.i32_and);
6717 const and_result = try WValue.toLocal(.stack, func, Type.bool);
6718 const result_ptr = try func.allocStack(result_ty);
6719 try func.store(result_ptr, and_result, Type.bool, @intCast(u32, ty.abiSize(func.target)));
6720 try func.store(result_ptr, ptr_val, ty, 0);
6721 break :val result_ptr;
6722 } else val: {
6723 try func.addImm32(0);
6724 try func.emitWValue(ptr_val);
6725 try func.emitWValue(cmp_result);
6726 try func.addTag(.select);
6727 break :val try WValue.toLocal(.stack, func, result_ty);
6728 };
6729
6730 return func.finishAir(inst, result_ptr, &.{ extra.ptr, extra.new_value, extra.expected_value });
6731}
6732
6733fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6734 const atomic_load = func.air.instructions.items(.data)[inst].atomic_load;
6735 const ptr = try func.resolveInst(atomic_load.ptr);
6736 const ty = func.air.typeOfIndex(inst);
6737
6738 if (func.useAtomicFeature()) {
6739 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(func.target)) {
6740 1 => .i32_atomic_load8_u,
6741 2 => .i32_atomic_load16_u,
6742 4 => .i32_atomic_load,
6743 8 => .i64_atomic_load,
6744 else => |size| return func.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),
6745 };
6746 try func.emitWValue(ptr);
6747 try func.addAtomicMemArg(tag, .{
6748 .offset = ptr.offset(),
6749 .alignment = ty.abiAlignment(func.target),
6750 });
6751 } else {
6752 _ = try func.load(ptr, ty, 0);
6753 }
6754
6755 const result = try WValue.toLocal(.stack, func, ty);
6756 return func.finishAir(inst, result, &.{atomic_load.ptr});
6757}
6758
6759fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6760 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
6761 const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data;
6762
6763 const ptr = try func.resolveInst(pl_op.operand);
6764 const operand = try func.resolveInst(extra.operand);
6765 const ty = func.air.typeOfIndex(inst);
6766 const op: std.builtin.AtomicRmwOp = extra.op();
6767
6768 if (func.useAtomicFeature()) {
6769 switch (op) {
6770 .Max,
6771 .Min,
6772 .Nand,
6773 => {
6774 const tmp = try func.load(ptr, ty, 0);
6775 const value = try tmp.toLocal(func, ty);
6776
6777 // create a loop to cmpxchg the new value
6778 try func.startBlock(.loop, wasm.block_empty);
6779
6780 try func.emitWValue(ptr);
6781 try func.emitWValue(value);
6782 if (op == .Nand) {
6783 const wasm_bits = toWasmBits(@intCast(u16, ty.bitSize(func.target))).?;
6784
6785 const and_res = try func.binOp(value, operand, ty, .@"and");
6786 if (wasm_bits == 32)
6787 try func.addImm32(-1)
6788 else if (wasm_bits == 64)
6789 try func.addImm64(@bitCast(u64, @as(i64, -1)))
6790 else
6791 return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
6792 _ = try func.binOp(and_res, .stack, ty, .xor);
6793 } else {
6794 try func.emitWValue(value);
6795 try func.emitWValue(operand);
6796 _ = try func.cmp(value, operand, ty, if (op == .Max) .gt else .lt);
6797 try func.addTag(.select);
6798 }
6799 try func.addAtomicMemArg(
6800 switch (ty.abiSize(func.target)) {
6801 1 => .i32_atomic_rmw8_cmpxchg_u,
6802 2 => .i32_atomic_rmw16_cmpxchg_u,
6803 4 => .i32_atomic_rmw_cmpxchg,
6804 8 => .i64_atomic_rmw_cmpxchg,
6805 else => return func.fail("TODO: implement `@atomicRmw` with operation `{s}` for types larger than 64 bits", .{@tagName(op)}),
6806 },
6807 .{
6808 .offset = ptr.offset(),
6809 .alignment = ty.abiAlignment(func.target),
6810 },
6811 );
6812 const select_res = try func.allocLocal(ty);
6813 try func.addLabel(.local_tee, select_res.local.value);
6814 _ = try func.cmp(.stack, value, ty, .neq); // leave on stack so we can use it for br_if
6815
6816 try func.emitWValue(select_res);
6817 try func.addLabel(.local_set, value.local.value);
6818
6819 try func.addLabel(.br_if, 0);
6820 try func.endBlock();
6821 return func.finishAir(inst, value, &.{ pl_op.operand, extra.operand });
6822 },
6823
6824 // the other operations have their own instructions for Wasm.
6825 else => {
6826 try func.emitWValue(ptr);
6827 try func.emitWValue(operand);
6828 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(func.target)) {
6829 1 => switch (op) {
6830 .Xchg => .i32_atomic_rmw8_xchg_u,
6831 .Add => .i32_atomic_rmw8_add_u,
6832 .Sub => .i32_atomic_rmw8_sub_u,
6833 .And => .i32_atomic_rmw8_and_u,
6834 .Or => .i32_atomic_rmw8_or_u,
6835 .Xor => .i32_atomic_rmw8_xor_u,
6836 else => unreachable,
6837 },
6838 2 => switch (op) {
6839 .Xchg => .i32_atomic_rmw16_xchg_u,
6840 .Add => .i32_atomic_rmw16_add_u,
6841 .Sub => .i32_atomic_rmw16_sub_u,
6842 .And => .i32_atomic_rmw16_and_u,
6843 .Or => .i32_atomic_rmw16_or_u,
6844 .Xor => .i32_atomic_rmw16_xor_u,
6845 else => unreachable,
6846 },
6847 4 => switch (op) {
6848 .Xchg => .i32_atomic_rmw_xchg,
6849 .Add => .i32_atomic_rmw_add,
6850 .Sub => .i32_atomic_rmw_sub,
6851 .And => .i32_atomic_rmw_and,
6852 .Or => .i32_atomic_rmw_or,
6853 .Xor => .i32_atomic_rmw_xor,
6854 else => unreachable,
6855 },
6856 8 => switch (op) {
6857 .Xchg => .i64_atomic_rmw_xchg,
6858 .Add => .i64_atomic_rmw_add,
6859 .Sub => .i64_atomic_rmw_sub,
6860 .And => .i64_atomic_rmw_and,
6861 .Or => .i64_atomic_rmw_or,
6862 .Xor => .i64_atomic_rmw_xor,
6863 else => unreachable,
6864 },
6865 else => |size| return func.fail("TODO: Implement `@atomicRmw` for types with abi size {d}", .{size}),
6866 };
6867 try func.addAtomicMemArg(tag, .{
6868 .offset = ptr.offset(),
6869 .alignment = ty.abiAlignment(func.target),
6870 });
6871 const result = try WValue.toLocal(.stack, func, ty);
6872 return func.finishAir(inst, result, &.{ pl_op.operand, extra.operand });
6873 },
6874 }
6875 } else {
6876 const loaded = try func.load(ptr, ty, 0);
6877 const result = try loaded.toLocal(func, ty);
6878
6879 switch (op) {
6880 .Xchg => {
6881 try func.store(ptr, operand, ty, 0);
6882 },
6883 .Add,
6884 .Sub,
6885 .And,
6886 .Or,
6887 .Xor,
6888 => {
6889 try func.emitWValue(ptr);
6890 _ = try func.binOp(result, operand, ty, switch (op) {
6891 .Add => .add,
6892 .Sub => .sub,
6893 .And => .@"and",
6894 .Or => .@"or",
6895 .Xor => .xor,
6896 else => unreachable,
6897 });
6898 if (ty.isInt() and (op == .Add or op == .Sub)) {
6899 _ = try func.wrapOperand(.stack, ty);
6900 }
6901 try func.store(.stack, .stack, ty, ptr.offset());
6902 },
6903 .Max,
6904 .Min,
6905 => {
6906 try func.emitWValue(ptr);
6907 try func.emitWValue(result);
6908 try func.emitWValue(operand);
6909 _ = try func.cmp(result, operand, ty, if (op == .Max) .gt else .lt);
6910 try func.addTag(.select);
6911 try func.store(.stack, .stack, ty, ptr.offset());
6912 },
6913 .Nand => {
6914 const wasm_bits = toWasmBits(@intCast(u16, ty.bitSize(func.target))).?;
6915
6916 try func.emitWValue(ptr);
6917 const and_res = try func.binOp(result, operand, ty, .@"and");
6918 if (wasm_bits == 32)
6919 try func.addImm32(-1)
6920 else if (wasm_bits == 64)
6921 try func.addImm64(@bitCast(u64, @as(i64, -1)))
6922 else
6923 return func.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
6924 _ = try func.binOp(and_res, .stack, ty, .xor);
6925 try func.store(.stack, .stack, ty, ptr.offset());
6926 },
6927 }
6928
6929 return func.finishAir(inst, result, &.{ pl_op.operand, extra.operand });
6930 }
6931}
6932
6933fn airFence(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6934 // Only when the atomic feature is enabled, and we're not building
6935 // for a single-threaded build, can we emit the `fence` instruction.
6936 // In all other cases, we emit no instructions for a fence.
6937 if (func.useAtomicFeature() and !func.bin_file.base.options.single_threaded) {
6938 try func.addAtomicTag(.atomic_fence);
6939 }
6940
6941 return func.finishAir(inst, .none, &.{});
6942}
6943
6944fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6945 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
6946
6947 const ptr = try func.resolveInst(bin_op.lhs);
6948 const operand = try func.resolveInst(bin_op.rhs);
6949 const ptr_ty = func.air.typeOf(bin_op.lhs);
6950 const ty = ptr_ty.childType();
6951
6952 if (func.useAtomicFeature()) {
6953 const tag: wasm.AtomicsOpcode = switch (ty.abiSize(func.target)) {
6954 1 => .i32_atomic_store8,
6955 2 => .i32_atomic_store16,
6956 4 => .i32_atomic_store,
6957 8 => .i64_atomic_store,
6958 else => |size| return func.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),
6959 };
6960 try func.emitWValue(ptr);
6961 try func.lowerToStack(operand);
6962 try func.addAtomicMemArg(tag, .{
6963 .offset = ptr.offset(),
6964 .alignment = ty.abiAlignment(func.target),
6965 });
6966 } else {
6967 try func.store(ptr, operand, ty, 0);
6968 }
6969
6970 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
6971}
src/arch/wasm/Emit.zig+82-2
......@@ -521,8 +521,88 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
521521}
522522
523523fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
524 _ = inst;
525 return emit.fail("TODO: Implement atomics instructions", .{});
524 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
525 const opcode = emit.mir.extra[extra_index];
526 const writer = emit.code.writer();
527 try emit.code.append(std.wasm.opcode(.atomics_prefix));
528 try leb128.writeULEB128(writer, opcode);
529 switch (@intToEnum(std.wasm.AtomicsOpcode, opcode)) {
530 .i32_atomic_load,
531 .i64_atomic_load,
532 .i32_atomic_load8_u,
533 .i32_atomic_load16_u,
534 .i64_atomic_load8_u,
535 .i64_atomic_load16_u,
536 .i64_atomic_load32_u,
537 .i32_atomic_store,
538 .i64_atomic_store,
539 .i32_atomic_store8,
540 .i32_atomic_store16,
541 .i64_atomic_store8,
542 .i64_atomic_store16,
543 .i64_atomic_store32,
544 .i32_atomic_rmw_add,
545 .i64_atomic_rmw_add,
546 .i32_atomic_rmw8_add_u,
547 .i32_atomic_rmw16_add_u,
548 .i64_atomic_rmw8_add_u,
549 .i64_atomic_rmw16_add_u,
550 .i64_atomic_rmw32_add_u,
551 .i32_atomic_rmw_sub,
552 .i64_atomic_rmw_sub,
553 .i32_atomic_rmw8_sub_u,
554 .i32_atomic_rmw16_sub_u,
555 .i64_atomic_rmw8_sub_u,
556 .i64_atomic_rmw16_sub_u,
557 .i64_atomic_rmw32_sub_u,
558 .i32_atomic_rmw_and,
559 .i64_atomic_rmw_and,
560 .i32_atomic_rmw8_and_u,
561 .i32_atomic_rmw16_and_u,
562 .i64_atomic_rmw8_and_u,
563 .i64_atomic_rmw16_and_u,
564 .i64_atomic_rmw32_and_u,
565 .i32_atomic_rmw_or,
566 .i64_atomic_rmw_or,
567 .i32_atomic_rmw8_or_u,
568 .i32_atomic_rmw16_or_u,
569 .i64_atomic_rmw8_or_u,
570 .i64_atomic_rmw16_or_u,
571 .i64_atomic_rmw32_or_u,
572 .i32_atomic_rmw_xor,
573 .i64_atomic_rmw_xor,
574 .i32_atomic_rmw8_xor_u,
575 .i32_atomic_rmw16_xor_u,
576 .i64_atomic_rmw8_xor_u,
577 .i64_atomic_rmw16_xor_u,
578 .i64_atomic_rmw32_xor_u,
579 .i32_atomic_rmw_xchg,
580 .i64_atomic_rmw_xchg,
581 .i32_atomic_rmw8_xchg_u,
582 .i32_atomic_rmw16_xchg_u,
583 .i64_atomic_rmw8_xchg_u,
584 .i64_atomic_rmw16_xchg_u,
585 .i64_atomic_rmw32_xchg_u,
586
587 .i32_atomic_rmw_cmpxchg,
588 .i64_atomic_rmw_cmpxchg,
589 .i32_atomic_rmw8_cmpxchg_u,
590 .i32_atomic_rmw16_cmpxchg_u,
591 .i64_atomic_rmw8_cmpxchg_u,
592 .i64_atomic_rmw16_cmpxchg_u,
593 .i64_atomic_rmw32_cmpxchg_u,
594 => {
595 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
596 try encodeMemArg(mem_arg, writer);
597 },
598 .atomic_fence => {
599 // TODO: When multi-memory proposal is accepted and implemented in the compiler,
600 // change this to (user-)specified index, rather than hardcode it to memory index 0.
601 const memory_index: u32 = 0;
602 try leb128.writeULEB128(writer, memory_index);
603 },
604 else => |tag| return emit.fail("TODO: Implement atomic instruction: {s}", .{@tagName(tag)}),
605 }
526606}
527607
528608fn emitMemFill(emit: *Emit) !void {
test/behavior/atomics.zig-12
......@@ -11,7 +11,6 @@ const supports_128_bit_atomics = switch (builtin.cpu.arch) {
1111};
1212
1313test "cmpxchg" {
14 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1514 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1615 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1716 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -38,7 +37,6 @@ fn testCmpxchg() !void {
3837}
3938
4039test "fence" {
41 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
4240 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4442
......@@ -48,7 +46,6 @@ test "fence" {
4846}
4947
5048test "atomicrmw and atomicload" {
51 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
5249 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5350 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5451 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -76,7 +73,6 @@ fn testAtomicLoad(ptr: *u8) !void {
7673}
7774
7875test "cmpxchg with ptr" {
79 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
8076 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8177 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8278 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -101,7 +97,6 @@ test "cmpxchg with ptr" {
10197}
10298
10399test "cmpxchg with ignored result" {
104 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
105100 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
106101 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
107102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -146,7 +141,6 @@ fn test_u128_cmpxchg() !void {
146141var a_global_variable = @as(u32, 1234);
147142
148143test "cmpxchg on a global variable" {
149 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
150144 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
151145 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
152146 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -161,7 +155,6 @@ test "cmpxchg on a global variable" {
161155}
162156
163157test "atomic load and rmw with enum" {
164 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
165158 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
166159 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
167160 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -178,7 +171,6 @@ test "atomic load and rmw with enum" {
178171}
179172
180173test "atomic store" {
181 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
182174 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
183175 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
184176 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -191,7 +183,6 @@ test "atomic store" {
191183}
192184
193185test "atomic store comptime" {
194 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
195186 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
196187 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
197188 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -209,7 +200,6 @@ fn testAtomicStore() !void {
209200}
210201
211202test "atomicrmw with floats" {
212 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
213203 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
214204 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
215205 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -239,7 +229,6 @@ fn testAtomicRmwFloat() !void {
239229}
240230
241231test "atomicrmw with ints" {
242 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
243232 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
244233 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
245234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -413,7 +402,6 @@ fn testAtomicsWithType(comptime T: type, a: T, b: T) !void {
413402}
414403
415404test "return @atomicStore, using it as a void value" {
416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
417405 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
418406 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
419407 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO