authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 14:06:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 17:34:16-07:00
log5b1c0d922c1061706ae1673333fcfb1d8fdd4602
tree4df989a3854adeaad833ad1c9970c972a77918b4
parent259f784241fb44e0a1b570daaf31ba2b9f164106

stage2: improve semantics of atomic operations

ZIR instructions updated: atomic_load, atomic_rmw, atomic_store, cmpxchg These no longer construct a pointer type as the result location. This solves a TODO that was preventing the pointer from possibly being volatile, as well as properly handling allowzero and addrspace. It also allows the pointer to be over-aligned, which may be needed depending on the target. As a consequence, the element type needs to be communicated in the ZIR. This is done by strategically making one of the operands be ResultLoc.ty instead of ResultLoc.coerced_ty if possible, or otherwise explicitly adding elem_type into the ZIR encoding, such as in the case of atomic_load. The pointer type of atomic operations is now checked in Sema by coercing it to an expected pointer type, that maybe over-aligned according to target requirements. Together with the previous commit, Zig now has smaller alignment for large integers, depending on the target, and yet still has type safety for atomic operations that specially require higher alignment.

6 files changed, 183 insertions(+), 132 deletions(-)

src/AstGen.zig+10-50
......@@ -7423,42 +7423,22 @@ fn builtinCall(
74237423 },
74247424
74257425 .atomic_load => {
7426 const int_type = try typeExpr(gz, scope, params[0]);
7427 // TODO allow this pointer type to be volatile
7428 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7429 .ptr_type_simple = .{
7430 .is_allowzero = false,
7431 .is_mutable = false,
7432 .is_volatile = false,
7433 .size = .One,
7434 .elem_type = int_type,
7435 },
7436 } });
7437 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{
7426 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
74387427 // zig fmt: off
7439 .lhs = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7440 .rhs = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]),
7428 .elem_type = try typeExpr(gz, scope, params[0]),
7429 .ptr = try expr (gz, scope, .none, params[1]),
7430 .ordering = try expr (gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]),
74417431 // zig fmt: on
74427432 });
74437433 return rvalue(gz, rl, result, node);
74447434 },
74457435 .atomic_rmw => {
74467436 const int_type = try typeExpr(gz, scope, params[0]);
7447 // TODO allow this pointer type to be volatile
7448 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7449 .ptr_type_simple = .{
7450 .is_allowzero = false,
7451 .is_mutable = true,
7452 .is_volatile = false,
7453 .size = .One,
7454 .elem_type = int_type,
7455 },
7456 } });
74577437 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
74587438 // zig fmt: off
7459 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7439 .ptr = try expr(gz, scope, .none, params[1]),
74607440 .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]),
7461 .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]),
7441 .operand = try expr(gz, scope, .{ .ty = int_type }, params[3]),
74627442 .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]),
74637443 // zig fmt: on
74647444 });
......@@ -7466,20 +7446,10 @@ fn builtinCall(
74667446 },
74677447 .atomic_store => {
74687448 const int_type = try typeExpr(gz, scope, params[0]);
7469 // TODO allow this pointer type to be volatile
7470 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7471 .ptr_type_simple = .{
7472 .is_allowzero = false,
7473 .is_mutable = true,
7474 .is_volatile = false,
7475 .size = .One,
7476 .elem_type = int_type,
7477 },
7478 } });
74797449 const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
74807450 // zig fmt: off
7481 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7482 .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]),
7451 .ptr = try expr(gz, scope, .none, params[1]),
7452 .operand = try expr(gz, scope, .{ .ty = int_type }, params[2]),
74837453 .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]),
74847454 // zig fmt: on
74857455 });
......@@ -7684,20 +7654,10 @@ fn cmpxchg(
76847654 tag: Zir.Inst.Tag,
76857655) InnerError!Zir.Inst.Ref {
76867656 const int_type = try typeExpr(gz, scope, params[0]);
7687 // TODO: allow this to be volatile
7688 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7689 .ptr_type_simple = .{
7690 .is_allowzero = false,
7691 .is_mutable = true,
7692 .is_volatile = false,
7693 .size = .One,
7694 .elem_type = int_type,
7695 },
7696 } });
76977657 const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{
76987658 // zig fmt: off
7699 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7700 .expected_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]),
7659 .ptr = try expr(gz, scope, .none, params[1]),
7660 .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]),
77017661 .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]),
77027662 .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]),
77037663 .failure_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]),
src/Sema.zig+87-75
......@@ -14715,51 +14715,64 @@ fn checkNumericType(
1471514715 }
1471614716}
1471714717
14718fn checkAtomicOperandType(
14718/// Returns the casted pointer.
14719fn checkAtomicPtrOperand(
1471914720 sema: *Sema,
1472014721 block: *Block,
14721 ty_src: LazySrcLoc,
14722 ty: Type,
14723) CompileError!void {
14724 var buffer: Type.Payload.Bits = undefined;
14722 elem_ty: Type,
14723 elem_ty_src: LazySrcLoc,
14724 ptr: Air.Inst.Ref,
14725 ptr_src: LazySrcLoc,
14726 ptr_const: bool,
14727) CompileError!Air.Inst.Ref {
1472514728 const target = sema.mod.getTarget();
14726 const max_atomic_bits = target_util.largestAtomicBits(target);
14727 const int_ty = switch (ty.zigTypeTag()) {
14728 .Int => ty,
14729 .Enum => ty.intTagType(&buffer),
14730 .Float => {
14731 const bit_count = ty.floatBits(target);
14732 if (bit_count > max_atomic_bits) {
14733 return sema.fail(
14734 block,
14735 ty_src,
14736 "expected {d}-bit float type or smaller; found {d}-bit float type",
14737 .{ max_atomic_bits, bit_count },
14738 );
14739 }
14740 return;
14741 },
14742 .Bool => return, // Will be treated as `u8`.
14743 else => {
14744 if (ty.isPtrAtRuntime()) return;
14729 var diag: target_util.AtomicPtrAlignmentDiagnostics = .{};
14730 const alignment = target_util.atomicPtrAlignment(target, elem_ty, &diag) catch |err| switch (err) {
14731 error.FloatTooBig => return sema.fail(
14732 block,
14733 elem_ty_src,
14734 "expected {d}-bit float type or smaller; found {d}-bit float type",
14735 .{ diag.max_bits, diag.bits },
14736 ),
14737 error.IntTooBig => return sema.fail(
14738 block,
14739 elem_ty_src,
14740 "expected {d}-bit integer type or smaller; found {d}-bit integer type",
14741 .{ diag.max_bits, diag.bits },
14742 ),
14743 error.BadType => return sema.fail(
14744 block,
14745 elem_ty_src,
14746 "expected bool, integer, float, enum, or pointer type; found {}",
14747 .{elem_ty.fmt(sema.mod)},
14748 ),
14749 };
1474514750
14746 return sema.fail(
14747 block,
14748 ty_src,
14749 "expected bool, integer, float, enum, or pointer type; found {}",
14750 .{ty.fmt(sema.mod)},
14751 );
14751 var wanted_ptr_data: Type.Payload.Pointer.Data = .{
14752 .pointee_type = elem_ty,
14753 .@"align" = alignment,
14754 .@"addrspace" = .generic,
14755 .mutable = !ptr_const,
14756 };
14757
14758 const ptr_ty = sema.typeOf(ptr);
14759 const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison()) {
14760 .Pointer => ptr_ty.ptrInfo().data,
14761 else => {
14762 const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data);
14763 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
14764 unreachable;
1475214765 },
1475314766 };
14754 const bit_count = int_ty.intInfo(target).bits;
14755 if (bit_count > max_atomic_bits) {
14756 return sema.fail(
14757 block,
14758 ty_src,
14759 "expected {d}-bit integer type or smaller; found {d}-bit integer type",
14760 .{ max_atomic_bits, bit_count },
14761 );
14762 }
14767
14768 wanted_ptr_data.@"addrspace" = ptr_data.@"addrspace";
14769 wanted_ptr_data.@"allowzero" = ptr_data.@"allowzero";
14770 wanted_ptr_data.@"volatile" = ptr_data.@"volatile";
14771
14772 const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data);
14773 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
14774
14775 return casted_ptr;
1476314776}
1476414777
1476514778fn checkPtrIsNotComptimeMutable(
......@@ -15036,10 +15049,8 @@ fn zirCmpxchg(
1503615049 const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
1503715050 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node };
1503815051 // zig fmt: on
15039 const ptr = sema.resolveInst(extra.ptr);
15040 const ptr_ty = sema.typeOf(ptr);
15041 const elem_ty = ptr_ty.elemType();
15042 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
15052 const expected_value = sema.resolveInst(extra.expected_value);
15053 const elem_ty = sema.typeOf(expected_value);
1504315054 if (elem_ty.zigTypeTag() == .Float) {
1504415055 return sema.fail(
1504515056 block,
......@@ -15048,7 +15059,8 @@ fn zirCmpxchg(
1504815059 .{elem_ty.fmt(sema.mod)},
1504915060 );
1505015061 }
15051 const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src);
15062 const uncasted_ptr = sema.resolveInst(extra.ptr);
15063 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
1505215064 const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src);
1505315065 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order);
1505415066 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order);
......@@ -15081,6 +15093,7 @@ fn zirCmpxchg(
1508115093 // to become undef as well
1508215094 return sema.addConstUndef(result_ty);
1508315095 }
15096 const ptr_ty = sema.typeOf(ptr);
1508415097 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
1508515098 const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: {
1508615099 try sema.storePtr(block, src, ptr, new_value);
......@@ -15487,17 +15500,16 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1548715500
1548815501fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1548915502 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
15490 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
15503 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
1549115504 // zig fmt: off
1549215505 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1549315506 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1549415507 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
1549515508 // zig fmt: on
15496 const ptr = sema.resolveInst(extra.lhs);
15497 const ptr_ty = sema.typeOf(ptr);
15498 const elem_ty = ptr_ty.elemType();
15499 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
15500 const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs);
15509 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
15510 const uncasted_ptr = sema.resolveInst(extra.ptr);
15511 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
15512 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering);
1550115513
1550215514 switch (order) {
1550315515 .Release, .AcqRel => {
......@@ -15516,7 +15528,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1551615528 }
1551715529
1551815530 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
15519 if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| {
15531 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
1552015532 return sema.addConstant(elem_ty, elem_val);
1552115533 }
1552215534 }
......@@ -15536,19 +15548,19 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1553615548 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
1553715549 const src = inst_data.src();
1553815550 // zig fmt: off
15539 const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
15551 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1554015552 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1554115553 const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
1554215554 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
1554315555 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
1554415556 // zig fmt: on
15545 const ptr = sema.resolveInst(extra.ptr);
15546 const ptr_ty = sema.typeOf(ptr);
15547 const operand_ty = ptr_ty.elemType();
15548 try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);
15557 const operand = sema.resolveInst(extra.operand);
15558 const elem_ty = sema.typeOf(operand);
15559 const uncasted_ptr = sema.resolveInst(extra.ptr);
15560 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
1554915561 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
1555015562
15551 switch (operand_ty.zigTypeTag()) {
15563 switch (elem_ty.zigTypeTag()) {
1555215564 .Enum => if (op != .Xchg) {
1555315565 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
1555415566 },
......@@ -15561,7 +15573,6 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1556115573 },
1556215574 else => {},
1556315575 }
15564 const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src);
1556515576 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering);
1556615577
1556715578 if (order == .Unordered) {
......@@ -15569,8 +15580,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1556915580 }
1557015581
1557115582 // special case zero bit types
15572 if (try sema.typeHasOnePossibleValue(block, operand_ty_src, operand_ty)) |val| {
15573 return sema.addConstant(operand_ty, val);
15583 if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| {
15584 return sema.addConstant(elem_ty, val);
1557415585 }
1557515586
1557615587 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
......@@ -15581,22 +15592,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1558115592 };
1558215593 if (ptr_val.isComptimeMutablePtr()) {
1558315594 const target = sema.mod.getTarget();
15595 const ptr_ty = sema.typeOf(ptr);
1558415596 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
1558515597 const new_val = switch (op) {
1558615598 // zig fmt: off
1558715599 .Xchg => operand_val,
15588 .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target),
15589 .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target),
15590 .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena, target),
15591 .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target),
15592 .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena, target),
15593 .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena, target),
15594 .Max => stored_val.numberMax (operand_val, target),
15595 .Min => stored_val.numberMin (operand_val, target),
15600 .Add => try stored_val.numberAddWrap(operand_val, elem_ty, sema.arena, target),
15601 .Sub => try stored_val.numberSubWrap(operand_val, elem_ty, sema.arena, target),
15602 .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target),
15603 .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target),
15604 .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target),
15605 .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target),
15606 .Max => stored_val.numberMax (operand_val, target),
15607 .Min => stored_val.numberMin (operand_val, target),
1559615608 // zig fmt: on
1559715609 };
15598 try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty);
15599 return sema.addConstant(operand_ty, stored_val);
15610 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
15611 return sema.addConstant(elem_ty, stored_val);
1560015612 } else break :rs ptr_src;
1560115613 } else ptr_src;
1560215614
......@@ -15620,15 +15632,15 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1562015632 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
1562115633 const src = inst_data.src();
1562215634 // zig fmt: off
15623 const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
15635 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1562415636 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1562515637 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
1562615638 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
1562715639 // zig fmt: on
15628 const ptr = sema.resolveInst(extra.ptr);
15629 const operand_ty = sema.typeOf(ptr).elemType();
15630 try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);
15631 const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src);
15640 const operand = sema.resolveInst(extra.operand);
15641 const elem_ty = sema.typeOf(operand);
15642 const uncasted_ptr = sema.resolveInst(extra.ptr);
15643 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
1563215644 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering);
1563315645
1563415646 const air_tag: Air.Inst.Tag = switch (order) {
src/Zir.zig+7-1
......@@ -903,7 +903,7 @@ pub const Inst = struct {
903903 /// Uses the `pl_node` union field with payload `Select`.
904904 select,
905905 /// Implements the `@atomicLoad` builtin.
906 /// Uses the `pl_node` union field with payload `Bin`.
906 /// Uses the `pl_node` union field with payload `AtomicLoad`.
907907 atomic_load,
908908 /// Implements the `@atomicRmw` builtin.
909909 /// Uses the `pl_node` union field with payload `AtomicRmw`.
......@@ -3293,6 +3293,12 @@ pub const Inst = struct {
32933293 ordering: Ref,
32943294 };
32953295
3296 pub const AtomicLoad = struct {
3297 elem_type: Ref,
3298 ptr: Ref,
3299 ordering: Ref,
3300 };
3301
32963302 pub const MulAdd = struct {
32973303 mulend1: Ref,
32983304 mulend2: Ref,
src/print_zir.zig+14-1
......@@ -283,6 +283,7 @@ const Writer = struct {
283283 => try self.writeStructInit(stream, inst),
284284
285285 .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst),
286 .atomic_load => try self.writeAtomicLoad(stream, inst),
286287 .atomic_store => try self.writeAtomicStore(stream, inst),
287288 .atomic_rmw => try self.writeAtomicRmw(stream, inst),
288289 .memcpy => try self.writeMemcpy(stream, inst),
......@@ -351,7 +352,6 @@ const Writer = struct {
351352 .offset_of,
352353 .splat,
353354 .reduce,
354 .atomic_load,
355355 .bitcast,
356356 .vector_type,
357357 .maximum,
......@@ -929,6 +929,19 @@ const Writer = struct {
929929 try self.writeSrc(stream, inst_data.src());
930930 }
931931
932 fn writeAtomicLoad(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
933 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
934 const extra = self.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
935
936 try self.writeInstRef(stream, extra.elem_type);
937 try stream.writeAll(", ");
938 try self.writeInstRef(stream, extra.ptr);
939 try stream.writeAll(", ");
940 try self.writeInstRef(stream, extra.ordering);
941 try stream.writeAll(") ");
942 try self.writeSrc(stream, inst_data.src());
943 }
944
932945 fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
933946 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
934947 const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
src/target.zig+64-4
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const llvm = @import("codegen/llvm/bindings.zig");
3const Type = @import("type.zig").Type;
34
45pub const ArchOsAbi = struct {
56 arch: std.Target.Cpu.Arch,
......@@ -543,10 +544,28 @@ pub fn needUnwindTables(target: std.Target) bool {
543544 return target.os.tag == .windows;
544545}
545546
546/// TODO this was ported from stage1 but it does not take into account CPU features,
547/// which can affect this value. Audit this!
548pub fn largestAtomicBits(target: std.Target) u32 {
549 return switch (target.cpu.arch) {
547pub const AtomicPtrAlignmentError = error{
548 FloatTooBig,
549 IntTooBig,
550 BadType,
551};
552
553pub const AtomicPtrAlignmentDiagnostics = struct {
554 bits: u16 = undefined,
555 max_bits: u16 = undefined,
556};
557
558/// If ABI alignment of `ty` is OK for atomic operations, returs 0.
559/// Otherwise returns the alignment required on a pointer for the target
560/// to perform atomic operations.
561pub fn atomicPtrAlignment(
562 target: std.Target,
563 ty: Type,
564 diags: *AtomicPtrAlignmentDiagnostics,
565) AtomicPtrAlignmentError!u32 {
566 // TODO this was ported from stage1 but it does not take into account CPU features,
567 // which can affect this value. Audit this!
568 const max_atomic_bits: u16 = switch (target.cpu.arch) {
550569 .avr,
551570 .msp430,
552571 .spu_2,
......@@ -611,6 +630,47 @@ pub fn largestAtomicBits(target: std.Target) u32 {
611630
612631 .x86_64 => 128,
613632 };
633
634 var buffer: Type.Payload.Bits = undefined;
635
636 const int_ty = switch (ty.zigTypeTag()) {
637 .Int => ty,
638 .Enum => ty.intTagType(&buffer),
639 .Float => {
640 const bit_count = ty.floatBits(target);
641 if (bit_count > max_atomic_bits) {
642 diags.* = .{
643 .bits = bit_count,
644 .max_bits = max_atomic_bits,
645 };
646 return error.FloatTooBig;
647 }
648 if (target.cpu.arch == .x86_64 and bit_count > 64) {
649 return 16;
650 }
651 return 0;
652 },
653 .Bool => return 0,
654 else => {
655 if (ty.isPtrAtRuntime()) return 0;
656 return error.BadType;
657 },
658 };
659
660 const bit_count = int_ty.intInfo(target).bits;
661 if (bit_count > max_atomic_bits) {
662 diags.* = .{
663 .bits = bit_count,
664 .max_bits = max_atomic_bits,
665 };
666 return error.IntTooBig;
667 }
668
669 if (target.cpu.arch == .x86_64 and bit_count > 64) {
670 return 16;
671 }
672
673 return 0;
614674}
615675
616676pub fn defaultAddressSpace(
test/behavior/atomics.zig+1-1
......@@ -127,7 +127,7 @@ test "128-bit cmpxchg" {
127127}
128128
129129fn test_u128_cmpxchg() !void {
130 var x: u128 = 1234;
130 var x: u128 align(16) = 1234;
131131 if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| {
132132 try expect(x1 == 1234);
133133 } else {