authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-14 21:58:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-14 21:58:22-07:00
log0395b35cee8d4082cc40b0dcd0298f797f42309d
treec4592f4e4cdb555836bb422d485b2c134e3f7547
parent5d14590ed15ce23e7ef8032f8075dcfe76ba9dd8

stage2: implement cmpxchg and improve comptime eval

* Implement Sema for `@cmpxchgWeak` and `@cmpxchgStrong`. Both runtime and comptime codepaths are implement. * Implement Codegen for LLVM backend and C backend. * Add LazySrcLoc.node_offset_builtin_call_argX 3...5 * Sema: rework comptime control flow. - `error.ComptimeReturn` is used to signal that a comptime function call has returned a result (stored in the Inlining struct). `analyzeCall` notices this and handles the result. - The ZIR instructions `break_inline`, `block_inline`, `condbr_inline` are now redundant and can be deleted. `break`, `block`, and `condbr` function equivalently inside a comptime scope. - The ZIR instructions `loop` and `repeat` also are modified to directly perform comptime control flow inside a comptime scope, skipping an unnecessary mechanism for analysis of runtime code. This makes Zig perform closer to an interpreter when evaluating comptime code. * Sema: zirRetErrValue looks at Sema.ret_fn_ty rather than sema.func for adding to the inferred error set. This fixes a bug for inlined/comptime function calls. * Implement ZIR printing for cmpxchg. * stage1: make cmpxchg respect --single-threaded - Our LLVM C++ API wrapper failed to expose this boolean flag before. * Fix AIR printing for struct fields showing incorrect liveness data.

19 files changed, 682 insertions(+), 115 deletions(-)

src/Air.zig+23
......@@ -309,6 +309,10 @@ pub const Inst = struct {
309309 /// Given a pointer to an array, return a slice.
310310 /// Uses the `ty_op` field.
311311 array_to_slice,
312 /// Uses the `ty_pl` field with payload `Cmpxchg`.
313 cmpxchg_weak,
314 /// Uses the `ty_pl` field with payload `Cmpxchg`.
315 cmpxchg_strong,
312316
313317 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
314318 return switch (op) {
......@@ -443,6 +447,23 @@ pub const Asm = struct {
443447 zir_index: u32,
444448};
445449
450pub const Cmpxchg = struct {
451 ptr: Inst.Ref,
452 expected_value: Inst.Ref,
453 new_value: Inst.Ref,
454 /// 0b00000000000000000000000000000XXX - success_order
455 /// 0b00000000000000000000000000XXX000 - failure_order
456 flags: u32,
457
458 pub fn successOrder(self: Cmpxchg) std.builtin.AtomicOrder {
459 return @intToEnum(std.builtin.AtomicOrder, @truncate(u3, self.flags));
460 }
461
462 pub fn failureOrder(self: Cmpxchg) std.builtin.AtomicOrder {
463 return @intToEnum(std.builtin.AtomicOrder, @truncate(u3, self.flags >> 3));
464 }
465};
466
446467pub fn getMainBody(air: Air) []const Air.Inst.Index {
447468 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
448469 const extra = air.extraData(Block, body_index);
......@@ -507,6 +528,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
507528 .struct_field_ptr,
508529 .struct_field_val,
509530 .ptr_elem_ptr,
531 .cmpxchg_weak,
532 .cmpxchg_strong,
510533 => return air.getRefType(datas[inst].ty_pl.ty),
511534
512535 .not,
src/AstGen.zig+6-5
......@@ -7542,6 +7542,7 @@ fn cmpxchg(
75427542 tag: Zir.Inst.Tag,
75437543) InnerError!Zir.Inst.Ref {
75447544 const int_type = try typeExpr(gz, scope, params[0]);
7545 // TODO: allow this to be volatile
75457546 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
75467547 .ptr_type_simple = .{
75477548 .is_allowzero = false,
......@@ -7553,11 +7554,11 @@ fn cmpxchg(
75537554 } });
75547555 const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{
75557556 // zig fmt: off
7556 .ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]),
7557 .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]),
7558 .new_value = try expr(gz, scope, .{ .ty = int_type }, params[3]),
7559 .success_order = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[4]),
7560 .fail_order = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[5]),
7557 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7558 .expected_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]),
7559 .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]),
7560 .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]),
7561 .failure_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]),
75617562 // zig fmt: on
75627563 });
75637564 return rvalue(gz, rl, result, node);
src/Liveness.zig+4
......@@ -340,6 +340,10 @@ fn analyzeInst(
340340 const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data;
341341 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
342342 },
343 .cmpxchg_strong, .cmpxchg_weak => {
344 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
345 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });
346 },
343347 .br => {
344348 const br = inst_datas[inst].br;
345349 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
src/Module.zig+57-37
......@@ -1321,6 +1321,7 @@ pub const Scope = struct {
13211321 /// It is shared among all the blocks in an inline or comptime called
13221322 /// function.
13231323 pub const Inlining = struct {
1324 comptime_result: Air.Inst.Ref,
13241325 merges: Merges,
13251326 };
13261327
......@@ -1643,36 +1644,12 @@ pub const SrcLoc = struct {
16431644 const token_starts = tree.tokens.items(.start);
16441645 return token_starts[tok_index];
16451646 },
1646 .node_offset_builtin_call_arg0 => |node_off| {
1647 const tree = try src_loc.file_scope.getTree(gpa);
1648 const node_datas = tree.nodes.items(.data);
1649 const node_tags = tree.nodes.items(.tag);
1650 const node = src_loc.declRelativeToNodeIndex(node_off);
1651 const param = switch (node_tags[node]) {
1652 .builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs,
1653 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs],
1654 else => unreachable,
1655 };
1656 const main_tokens = tree.nodes.items(.main_token);
1657 const tok_index = main_tokens[param];
1658 const token_starts = tree.tokens.items(.start);
1659 return token_starts[tok_index];
1660 },
1661 .node_offset_builtin_call_arg1 => |node_off| {
1662 const tree = try src_loc.file_scope.getTree(gpa);
1663 const node_datas = tree.nodes.items(.data);
1664 const node_tags = tree.nodes.items(.tag);
1665 const node = src_loc.declRelativeToNodeIndex(node_off);
1666 const param = switch (node_tags[node]) {
1667 .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs,
1668 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1],
1669 else => unreachable,
1670 };
1671 const main_tokens = tree.nodes.items(.main_token);
1672 const tok_index = main_tokens[param];
1673 const token_starts = tree.tokens.items(.start);
1674 return token_starts[tok_index];
1675 },
1647 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),
1648 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),
1649 .node_offset_builtin_call_arg2 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 2),
1650 .node_offset_builtin_call_arg3 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 3),
1651 .node_offset_builtin_call_arg4 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 4),
1652 .node_offset_builtin_call_arg5 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 5),
16761653 .node_offset_array_access_index => |node_off| {
16771654 const tree = try src_loc.file_scope.getTree(gpa);
16781655 const node_datas = tree.nodes.items(.data);
......@@ -1965,6 +1942,31 @@ pub const SrcLoc = struct {
19651942 },
19661943 }
19671944 }
1945
1946 pub fn byteOffsetBuiltinCallArg(
1947 src_loc: SrcLoc,
1948 gpa: *Allocator,
1949 node_off: i32,
1950 arg_index: u32,
1951 ) !u32 {
1952 const tree = try src_loc.file_scope.getTree(gpa);
1953 const node_datas = tree.nodes.items(.data);
1954 const node_tags = tree.nodes.items(.tag);
1955 const node = src_loc.declRelativeToNodeIndex(node_off);
1956 const param = switch (node_tags[node]) {
1957 .builtin_call_two, .builtin_call_two_comma => switch (arg_index) {
1958 0 => node_datas[node].lhs,
1959 1 => node_datas[node].rhs,
1960 else => unreachable,
1961 },
1962 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + arg_index],
1963 else => unreachable,
1964 };
1965 const main_tokens = tree.nodes.items(.main_token);
1966 const tok_index = main_tokens[param];
1967 const token_starts = tree.tokens.items(.start);
1968 return token_starts[tok_index];
1969 }
19681970};
19691971
19701972/// Resolving a source location into a byte offset may require doing work
......@@ -2032,6 +2034,10 @@ pub const LazySrcLoc = union(enum) {
20322034 node_offset_builtin_call_arg0: i32,
20332035 /// Same as `node_offset_builtin_call_arg0` except arg index 1.
20342036 node_offset_builtin_call_arg1: i32,
2037 node_offset_builtin_call_arg2: i32,
2038 node_offset_builtin_call_arg3: i32,
2039 node_offset_builtin_call_arg4: i32,
2040 node_offset_builtin_call_arg5: i32,
20352041 /// The source location points to the index expression of an array access
20362042 /// expression, found by taking this AST node index offset from the containing
20372043 /// Decl AST node, which points to an array access AST node. Next, navigate
......@@ -2157,6 +2163,10 @@ pub const LazySrcLoc = union(enum) {
21572163 .node_offset_for_cond,
21582164 .node_offset_builtin_call_arg0,
21592165 .node_offset_builtin_call_arg1,
2166 .node_offset_builtin_call_arg2,
2167 .node_offset_builtin_call_arg3,
2168 .node_offset_builtin_call_arg4,
2169 .node_offset_builtin_call_arg5,
21602170 .node_offset_array_access_index,
21612171 .node_offset_slice_sentinel,
21622172 .node_offset_call_func,
......@@ -2205,6 +2215,10 @@ pub const LazySrcLoc = union(enum) {
22052215 .node_offset_for_cond,
22062216 .node_offset_builtin_call_arg0,
22072217 .node_offset_builtin_call_arg1,
2218 .node_offset_builtin_call_arg2,
2219 .node_offset_builtin_call_arg3,
2220 .node_offset_builtin_call_arg4,
2221 .node_offset_builtin_call_arg5,
22082222 .node_offset_array_access_index,
22092223 .node_offset_slice_sentinel,
22102224 .node_offset_call_func,
......@@ -2246,6 +2260,9 @@ pub const CompileError = error{
22462260 /// because the function is generic. This is only seen when analyzing the body of a param
22472261 /// instruction.
22482262 GenericPoison,
2263 /// In a comptime scope, a return instruction was encountered. This error is only seen when
2264 /// doing a comptime function call.
2265 ComptimeReturn,
22492266};
22502267
22512268pub fn deinit(mod: *Module) void {
......@@ -3928,8 +3945,10 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
39283945 log.debug("set {s} to in_progress", .{decl.name});
39293946
39303947 _ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
3948 // TODO make these unreachable instead of @panic
39313949 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
39323950 error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
3951 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
39333952 else => |e| return e,
39343953 };
39353954
......@@ -4534,7 +4553,6 @@ pub const PeerTypeCandidateSrc = union(enum) {
45344553 self: PeerTypeCandidateSrc,
45354554 gpa: *Allocator,
45364555 decl: *Decl,
4537 candidates: usize,
45384556 candidate_i: usize,
45394557 ) ?LazySrcLoc {
45404558 @setCold(true);
......@@ -4547,12 +4565,14 @@ pub const PeerTypeCandidateSrc = union(enum) {
45474565 return candidate_srcs[candidate_i];
45484566 },
45494567 .typeof_builtin_call_node_offset => |node_offset| {
4550 if (candidates <= 2) {
4551 switch (candidate_i) {
4552 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = node_offset },
4553 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = node_offset },
4554 else => unreachable,
4555 }
4568 switch (candidate_i) {
4569 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = node_offset },
4570 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = node_offset },
4571 2 => return LazySrcLoc{ .node_offset_builtin_call_arg2 = node_offset },
4572 3 => return LazySrcLoc{ .node_offset_builtin_call_arg3 = node_offset },
4573 4 => return LazySrcLoc{ .node_offset_builtin_call_arg4 = node_offset },
4574 5 => return LazySrcLoc{ .node_offset_builtin_call_arg5 = node_offset },
4575 else => {},
45564576 }
45574577
45584578 const tree = decl.namespace.file_scope.getTree(gpa) catch |err| {
src/Sema.zig+230-28
......@@ -159,7 +159,6 @@ pub fn analyzeBody(
159159 .bit_or => try sema.zirBitwise(block, inst, .bit_or),
160160 .bitcast => try sema.zirBitcast(block, inst),
161161 .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
162 .block => try sema.zirBlock(block, inst),
163162 .suspend_block => try sema.zirSuspendBlock(block, inst),
164163 .bool_not => try sema.zirBoolNot(block, inst),
165164 .bool_br_and => try sema.zirBoolBr(block, inst, false),
......@@ -215,7 +214,6 @@ pub fn analyzeBody(
215214 .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst),
216215 .is_non_null => try sema.zirIsNonNull(block, inst),
217216 .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
218 .loop => try sema.zirLoop(block, inst),
219217 .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
220218 .negate => try sema.zirNegate(block, inst, .sub),
221219 .negate_wrap => try sema.zirNegate(block, inst, .subwrap),
......@@ -308,8 +306,8 @@ pub fn analyzeBody(
308306 .shr_exact => try sema.zirShrExact(block, inst),
309307 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
310308 .offset_of => try sema.zirOffsetOf(block, inst),
311 .cmpxchg_strong => try sema.zirCmpxchg(block, inst),
312 .cmpxchg_weak => try sema.zirCmpxchg(block, inst),
309 .cmpxchg_strong => try sema.zirCmpxchg(block, inst, .cmpxchg_strong),
310 .cmpxchg_weak => try sema.zirCmpxchg(block, inst, .cmpxchg_weak),
313311 .splat => try sema.zirSplat(block, inst),
314312 .reduce => try sema.zirReduce(block, inst),
315313 .shuffle => try sema.zirShuffle(block, inst),
......@@ -364,16 +362,12 @@ pub fn analyzeBody(
364362 // Instructions that we know to *always* be noreturn based solely on their tag.
365363 // These functions match the return type of analyzeBody so that we can
366364 // tail call them here.
367 .break_inline => return inst,
368 .condbr => return sema.zirCondbr(block, inst),
369 .@"break" => return sema.zirBreak(block, inst),
370365 .compile_error => return sema.zirCompileError(block, inst),
371366 .ret_coerce => return sema.zirRetCoerce(block, inst),
372367 .ret_node => return sema.zirRetNode(block, inst),
373368 .ret_load => return sema.zirRetLoad(block, inst),
374369 .ret_err_value => return sema.zirRetErrValue(block, inst),
375370 .@"unreachable" => return sema.zirUnreachable(block, inst),
376 .repeat => return sema.zirRepeat(block, inst),
377371 .panic => return sema.zirPanic(block, inst),
378372 // zig fmt: on
379373
......@@ -499,6 +493,28 @@ pub fn analyzeBody(
499493 },
500494
501495 // Special case instructions to handle comptime control flow.
496 .@"break" => {
497 if (block.is_comptime) {
498 return inst; // same as break_inline
499 } else {
500 return sema.zirBreak(block, inst);
501 }
502 },
503 .break_inline => return inst,
504 .repeat => {
505 if (block.is_comptime) {
506 // Send comptime control flow back to the beginning of this block.
507 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
508 try sema.emitBackwardBranch(block, src);
509 i = 0;
510 continue;
511 } else {
512 const src_node = sema.code.instructions.items(.data)[inst].node;
513 const src: LazySrcLoc = .{ .node_offset = src_node };
514 try sema.requireRuntimeBlock(block, src);
515 return always_noreturn;
516 }
517 },
502518 .repeat_inline => {
503519 // Send comptime control flow back to the beginning of this block.
504520 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
......@@ -506,6 +522,34 @@ pub fn analyzeBody(
506522 i = 0;
507523 continue;
508524 },
525 .loop => blk: {
526 if (!block.is_comptime) break :blk try sema.zirLoop(block, inst);
527 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
528 const inst_data = datas[inst].pl_node;
529 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
530 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
531 const break_inst = try sema.analyzeBody(block, inline_body);
532 const break_data = datas[break_inst].@"break";
533 if (inst == break_data.block_inst) {
534 break :blk sema.resolveInst(break_data.operand);
535 } else {
536 return break_inst;
537 }
538 },
539 .block => blk: {
540 if (!block.is_comptime) break :blk try sema.zirBlock(block, inst);
541 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
542 const inst_data = datas[inst].pl_node;
543 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
544 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
545 const break_inst = try sema.analyzeBody(block, inline_body);
546 const break_data = datas[break_inst].@"break";
547 if (inst == break_data.block_inst) {
548 break :blk sema.resolveInst(break_data.operand);
549 } else {
550 return break_inst;
551 }
552 },
509553 .block_inline => blk: {
510554 // Directly analyze the block body without introducing a new block.
511555 const inst_data = datas[inst].pl_node;
......@@ -519,6 +563,24 @@ pub fn analyzeBody(
519563 return break_inst;
520564 }
521565 },
566 .condbr => blk: {
567 if (!block.is_comptime) return sema.zirCondbr(block, inst);
568 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
569 const inst_data = datas[inst].pl_node;
570 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
571 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
572 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
573 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
574 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
575 const inline_body = if (cond.val.toBool()) then_body else else_body;
576 const break_inst = try sema.analyzeBody(block, inline_body);
577 const break_data = datas[break_inst].@"break";
578 if (inst == break_data.block_inst) {
579 break :blk sema.resolveInst(break_data.operand);
580 } else {
581 return break_inst;
582 }
583 },
522584 .condbr_inline => blk: {
523585 const inst_data = datas[inst].pl_node;
524586 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
......@@ -1933,16 +1995,6 @@ fn zirCompileLog(
19331995 return Air.Inst.Ref.void_value;
19341996}
19351997
1936fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
1937 const tracy = trace(@src());
1938 defer tracy.end();
1939
1940 const src_node = sema.code.instructions.items(.data)[inst].node;
1941 const src: LazySrcLoc = .{ .node_offset = src_node };
1942 try sema.requireRuntimeBlock(block, src);
1943 return always_noreturn;
1944}
1945
19461998fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
19471999 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
19482000 const src: LazySrcLoc = inst_data.src();
......@@ -2003,7 +2055,6 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Compil
20032055
20042056 _ = try sema.analyzeBody(&loop_block, body);
20052057
2006 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
20072058 try child_block.instructions.append(gpa, loop_inst);
20082059
20092060 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
......@@ -2615,6 +2666,7 @@ fn analyzeCall(
26152666 // This one is shared among sub-blocks within the same callee, but not
26162667 // shared among the entire inline/comptime call stack.
26172668 var inlining: Scope.Block.Inlining = .{
2669 .comptime_result = undefined,
26182670 .merges = .{
26192671 .results = .{},
26202672 .br_list = .{},
......@@ -2770,8 +2822,13 @@ fn analyzeCall(
27702822 }
27712823 }
27722824
2773 _ = try sema.analyzeBody(&child_block, fn_info.body);
2774 const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges);
2825 const result = result: {
2826 _ = sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
2827 error.ComptimeReturn => break :result inlining.comptime_result,
2828 else => |e| return e,
2829 };
2830 break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
2831 };
27752832
27762833 if (is_comptime_call) {
27772834 const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result);
......@@ -6662,9 +6719,9 @@ fn zirRetErrValue(
66626719 const src = inst_data.src();
66636720
66646721 // Add the error tag to the inferred error set of the in-scope function.
6665 if (sema.func) |func| {
6666 if (func.getInferredErrorSet()) |map| {
6667 _ = try map.getOrPut(sema.gpa, err_name);
6722 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
6723 if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
6724 _ = try payload.data.map.getOrPut(sema.gpa, err_name);
66686725 }
66696726 }
66706727 // Return the error code from the function.
......@@ -6699,6 +6756,10 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
66996756 const operand = sema.resolveInst(inst_data.operand);
67006757 const src = inst_data.src();
67016758
6759 // TODO: we pass false here for the `need_coercion` boolean, but I'm pretty sure we need
6760 // to remove this parameter entirely. Observe the problem by looking at the incorrect compile
6761 // error that occurs when a behavior test case being executed at comptime fails, e.g.
6762 // `test { comptime foo(); } fn foo() { try expect(false); }`
67026763 return sema.analyzeRet(block, operand, src, false);
67036764}
67046765
......@@ -6730,6 +6791,10 @@ fn analyzeRet(
67306791 try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
67316792
67326793 if (block.inlining) |inlining| {
6794 if (block.is_comptime) {
6795 inlining.comptime_result = operand;
6796 return error.ComptimeReturn;
6797 }
67336798 // We are inlining a function call; rewrite the `ret` as a `break`.
67346799 try inlining.merges.results.append(sema.gpa, operand);
67356800 _ = try block.addBr(inlining.merges.block_inst, operand);
......@@ -7425,10 +7490,149 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
74257490 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
74267491}
74277492
7428fn zirCmpxchg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7493fn checkAtomicOperandType(
7494 sema: *Sema,
7495 block: *Scope.Block,
7496 ty_src: LazySrcLoc,
7497 ty: Type,
7498) CompileError!void {
7499 var buffer: Type.Payload.Bits = undefined;
7500 const target = sema.mod.getTarget();
7501 const max_atomic_bits = target_util.largestAtomicBits(target);
7502 const int_ty = switch (ty.zigTypeTag()) {
7503 .Int => ty,
7504 .Enum => ty.enumTagType(&buffer),
7505 .Float => {
7506 const bit_count = ty.floatBits(target);
7507 if (bit_count > max_atomic_bits) {
7508 return sema.mod.fail(
7509 &block.base,
7510 ty_src,
7511 "expected {d}-bit float type or smaller; found {d}-bit float type",
7512 .{ max_atomic_bits, bit_count },
7513 );
7514 }
7515 return;
7516 },
7517 .Bool => return, // Will be treated as `u8`.
7518 else => return sema.mod.fail(
7519 &block.base,
7520 ty_src,
7521 "expected bool, integer, float, enum, or pointer type; found {}",
7522 .{ty},
7523 ),
7524 };
7525 const bit_count = int_ty.intInfo(target).bits;
7526 if (bit_count > max_atomic_bits) {
7527 return sema.mod.fail(
7528 &block.base,
7529 ty_src,
7530 "expected {d}-bit integer type or smaller; found {d}-bit integer type",
7531 .{ max_atomic_bits, bit_count },
7532 );
7533 }
7534}
7535
7536fn resolveAtomicOrder(
7537 sema: *Sema,
7538 block: *Scope.Block,
7539 src: LazySrcLoc,
7540 zir_ref: Zir.Inst.Ref,
7541) CompileError!std.builtin.AtomicOrder {
7542 const atomic_order_ty = try sema.getBuiltinType(block, src, "AtomicOrder");
7543 const air_ref = sema.resolveInst(zir_ref);
7544 const coerced = try sema.coerce(block, atomic_order_ty, air_ref, src);
7545 const val = try sema.resolveConstValue(block, src, coerced);
7546 return val.toEnum(std.builtin.AtomicOrder);
7547}
7548
7549fn zirCmpxchg(
7550 sema: *Sema,
7551 block: *Scope.Block,
7552 inst: Zir.Inst.Index,
7553 air_tag: Air.Inst.Tag,
7554) CompileError!Air.Inst.Ref {
7555 const mod = sema.mod;
74297556 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7557 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, inst_data.payload_index).data;
74307558 const src = inst_data.src();
7431 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCmpxchg", .{});
7559 // zig fmt: off
7560 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
7561 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
7562 const expected_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
7563 const new_value_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
7564 const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
7565 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node };
7566 // zig fmt: on
7567 const ptr = sema.resolveInst(extra.ptr);
7568 const elem_ty = sema.typeOf(ptr).elemType();
7569 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
7570 if (elem_ty.zigTypeTag() == .Float) {
7571 return mod.fail(
7572 &block.base,
7573 elem_ty_src,
7574 "expected bool, integer, enum, or pointer type; found '{}'",
7575 .{elem_ty},
7576 );
7577 }
7578 const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src);
7579 const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src);
7580 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order);
7581 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order);
7582
7583 if (@enumToInt(success_order) < @enumToInt(std.builtin.AtomicOrder.Monotonic)) {
7584 return mod.fail(&block.base, success_order_src, "success atomic ordering must be Monotonic or stricter", .{});
7585 }
7586 if (@enumToInt(failure_order) < @enumToInt(std.builtin.AtomicOrder.Monotonic)) {
7587 return mod.fail(&block.base, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{});
7588 }
7589 if (@enumToInt(failure_order) > @enumToInt(success_order)) {
7590 return mod.fail(&block.base, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
7591 }
7592 if (failure_order == .Release or failure_order == .AcqRel) {
7593 return mod.fail(&block.base, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{});
7594 }
7595
7596 const result_ty = try Module.optionalType(sema.arena, elem_ty);
7597
7598 // special case zero bit types
7599 if ((try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) != null) {
7600 return sema.addConstant(result_ty, Value.initTag(.null_value));
7601 }
7602
7603 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
7604 if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| {
7605 if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| {
7606 if (expected_val.isUndef() or new_val.isUndef()) {
7607 return sema.addConstUndef(result_ty);
7608 }
7609 const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src;
7610 const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: {
7611 try sema.storePtr(block, src, ptr, new_value);
7612 break :blk Value.initTag(.null_value);
7613 } else try Value.Tag.opt_payload.create(sema.arena, stored_val);
7614
7615 return sema.addConstant(result_ty, result_val);
7616 } else break :rs new_value_src;
7617 } else break :rs expected_src;
7618 } else ptr_src;
7619
7620 const flags: u32 = @as(u32, @enumToInt(success_order)) |
7621 (@as(u32, @enumToInt(failure_order)) << 3);
7622
7623 try sema.requireRuntimeBlock(block, runtime_src);
7624 return block.addInst(.{
7625 .tag = air_tag,
7626 .data = .{ .ty_pl = .{
7627 .ty = try sema.addType(result_ty),
7628 .payload = try sema.addExtra(Air.Cmpxchg{
7629 .ptr = ptr,
7630 .expected_value = expected_value,
7631 .new_value = new_value,
7632 .flags = flags,
7633 }),
7634 } },
7635 });
74327636}
74337637
74347638fn zirSplat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9576,13 +9780,11 @@ fn resolvePeerTypes(
95769780 const chosen_src = candidate_srcs.resolve(
95779781 sema.gpa,
95789782 block.src_decl,
9579 instructions.len,
95809783 chosen_i,
95819784 );
95829785 const candidate_src = candidate_srcs.resolve(
95839786 sema.gpa,
95849787 block.src_decl,
9585 instructions.len,
95869788 candidate_i + 1,
95879789 );
95889790
src/Zir.zig+22-3
......@@ -2778,7 +2778,7 @@ pub const Inst = struct {
27782778 expected_value: Ref,
27792779 new_value: Ref,
27802780 success_order: Ref,
2781 fail_order: Ref,
2781 failure_order: Ref,
27822782 };
27832783
27842784 pub const AtomicRmw = struct {
......@@ -3054,8 +3054,6 @@ const Writer = struct {
30543054 .array_init_ref,
30553055 .array_init_anon_ref,
30563056 .union_init_ptr,
3057 .cmpxchg_strong,
3058 .cmpxchg_weak,
30593057 .shuffle,
30603058 .select,
30613059 .atomic_rmw,
......@@ -3072,6 +3070,10 @@ const Writer = struct {
30723070 .struct_init_ref,
30733071 => try self.writeStructInit(stream, inst),
30743072
3073 .cmpxchg_strong,
3074 .cmpxchg_weak,
3075 => try self.writeCmpxchg(stream, inst),
3076
30753077 .struct_init_anon,
30763078 .struct_init_anon_ref,
30773079 => try self.writeStructInitAnon(stream, inst),
......@@ -3474,6 +3476,23 @@ const Writer = struct {
34743476 try self.writeSrc(stream, inst_data.src());
34753477 }
34763478
3479 fn writeCmpxchg(self: *Writer, stream: anytype, inst: Inst.Index) !void {
3480 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
3481 const extra = self.code.extraData(Inst.Cmpxchg, inst_data.payload_index).data;
3482
3483 try self.writeInstRef(stream, extra.ptr);
3484 try stream.writeAll(", ");
3485 try self.writeInstRef(stream, extra.expected_value);
3486 try stream.writeAll(", ");
3487 try self.writeInstRef(stream, extra.new_value);
3488 try stream.writeAll(", ");
3489 try self.writeInstRef(stream, extra.success_order);
3490 try stream.writeAll(", ");
3491 try self.writeInstRef(stream, extra.failure_order);
3492 try stream.writeAll(") ");
3493 try self.writeSrc(stream, inst_data.src());
3494 }
3495
34773496 fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Inst.Index) !void {
34783497 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
34793498 const extra = self.code.extraData(Inst.StructInitAnon, inst_data.payload_index);
src/codegen.zig+13
......@@ -857,6 +857,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
857857 .struct_field_ptr=> try self.airStructFieldPtr(inst),
858858 .struct_field_val=> try self.airStructFieldVal(inst),
859859 .array_to_slice => try self.airArrayToSlice(inst),
860 .cmpxchg_strong => try self.airCmpxchg(inst),
861 .cmpxchg_weak => try self.airCmpxchg(inst),
860862
861863 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
862864 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
......@@ -4751,6 +4753,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
47514753 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
47524754 }
47534755
4756 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
4757 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4758 const extra = self.air.extraData(Air.Block, ty_pl.payload);
4759 const result: MCValue = switch (arch) {
4760 else => return self.fail("TODO implement airCmpxchg for {}", .{
4761 self.target.cpu.arch,
4762 }),
4763 };
4764 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
4765 }
4766
47544767 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
47554768 // First section of indexes correspond to a set number of constant values.
47564769 const ref_int = @enumToInt(inst);
src/codegen/c.zig+39
......@@ -911,6 +911,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
911911 .wrap_optional => try airWrapOptional(o, inst),
912912 .struct_field_ptr => try airStructFieldPtr(o, inst),
913913 .array_to_slice => try airArrayToSlice(o, inst),
914 .cmpxchg_weak => try airCmpxchg(o, inst, "weak"),
915 .cmpxchg_strong => try airCmpxchg(o, inst, "strong"),
914916
915917 .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),
916918 .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),
......@@ -1878,6 +1880,43 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
18781880 return local;
18791881}
18801882
1883fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
1884 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1885 const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
1886 const inst_ty = o.air.typeOfIndex(inst);
1887 const ptr = try o.resolveInst(extra.ptr);
1888 const expected_value = try o.resolveInst(extra.expected_value);
1889 const new_value = try o.resolveInst(extra.new_value);
1890 const local = try o.allocLocal(inst_ty, .Const);
1891 const writer = o.writer();
1892
1893 try writer.print(" = zig_cmpxchg_{s}(", .{flavor});
1894 try o.writeCValue(writer, ptr);
1895 try writer.writeAll(", ");
1896 try o.writeCValue(writer, expected_value);
1897 try writer.writeAll(", ");
1898 try o.writeCValue(writer, new_value);
1899 try writer.writeAll(", ");
1900 try writeMemoryOrder(writer, extra.successOrder());
1901 try writer.writeAll(", ");
1902 try writeMemoryOrder(writer, extra.failureOrder());
1903 try writer.writeAll(");\n");
1904
1905 return local;
1906}
1907
1908fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
1909 const str = switch (order) {
1910 .Unordered => "memory_order_relaxed",
1911 .Monotonic => "memory_order_consume",
1912 .Acquire => "memory_order_acquire",
1913 .Release => "memory_order_release",
1914 .AcqRel => "memory_order_acq_rel",
1915 .SeqCst => "memory_order_seq_cst",
1916 };
1917 return w.writeAll(str);
1918}
1919
18811920fn IndentWriter(comptime UnderlyingWriter: type) type {
18821921 return struct {
18831922 const Self = @This();
src/codegen/llvm.zig+93
......@@ -389,6 +389,7 @@ pub const Object = struct {
389389 .latest_alloca_inst = null,
390390 .llvm_func = llvm_func,
391391 .blocks = .{},
392 .single_threaded = module.comp.bin_file.options.single_threaded,
392393 };
393394 defer fg.deinit();
394395
......@@ -906,6 +907,31 @@ pub const DeclGen = struct {
906907 // TODO: improve this API, `addAttr(-1, attr_name)`
907908 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name);
908909 }
910
911 /// If the operand type of an atomic operation is not byte sized we need to
912 /// widen it before using it and then truncate the result.
913 /// RMW exchange of floating-point values is bitcasted to same-sized integer
914 /// types to work around a LLVM deficiency when targeting ARM/AArch64.
915 fn getAtomicAbiType(dg: *DeclGen, ty: Type, is_rmw_xchg: bool) ?*const llvm.Type {
916 const target = dg.module.getTarget();
917 var buffer: Type.Payload.Bits = undefined;
918 const int_ty = switch (ty.zigTypeTag()) {
919 .Int => ty,
920 .Enum => ty.enumTagType(&buffer),
921 .Float => {
922 if (!is_rmw_xchg) return null;
923 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));
924 },
925 .Bool => return dg.context.intType(8),
926 else => return null,
927 };
928 const bit_count = int_ty.intInfo(target).bits;
929 if (!std.math.isPowerOfTwo(bit_count) or (bit_count % 8) != 0) {
930 return dg.context.intType(@intCast(c_uint, int_ty.abiSize(target) * 8));
931 } else {
932 return null;
933 }
934 }
909935};
910936
911937pub const FuncGen = struct {
......@@ -940,6 +966,8 @@ pub const FuncGen = struct {
940966 break_vals: *BreakValues,
941967 }),
942968
969 single_threaded: bool,
970
943971 const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock);
944972 const BreakValues = std.ArrayListUnmanaged(*const llvm.Value);
945973
......@@ -1029,6 +1057,8 @@ pub const FuncGen = struct {
10291057 .slice_ptr => try self.airSliceField(inst, 0),
10301058 .slice_len => try self.airSliceField(inst, 1),
10311059 .array_to_slice => try self.airArrayToSlice(inst),
1060 .cmpxchg_weak => try self.airCmpxchg(inst, true),
1061 .cmpxchg_strong => try self.airCmpxchg(inst, false),
10321062
10331063 .struct_field_ptr => try self.airStructFieldPtr(inst),
10341064 .struct_field_val => try self.airStructFieldVal(inst),
......@@ -1975,6 +2005,58 @@ pub const FuncGen = struct {
19752005 return null;
19762006 }
19772007
2008 fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value {
2009 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2010 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
2011 var ptr = try self.resolveInst(extra.ptr);
2012 var expected_value = try self.resolveInst(extra.expected_value);
2013 var new_value = try self.resolveInst(extra.new_value);
2014 const operand_ty = self.air.typeOf(extra.ptr).elemType();
2015 const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false);
2016 if (opt_abi_ty) |abi_ty| {
2017 // operand needs widening and truncating
2018 ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), "");
2019 if (operand_ty.isSignedInt()) {
2020 expected_value = self.builder.buildSExt(expected_value, abi_ty, "");
2021 new_value = self.builder.buildSExt(new_value, abi_ty, "");
2022 } else {
2023 expected_value = self.builder.buildZExt(expected_value, abi_ty, "");
2024 new_value = self.builder.buildZExt(new_value, abi_ty, "");
2025 }
2026 }
2027 const success_order = toLlvmAtomicOrdering(extra.successOrder());
2028 const failure_order = toLlvmAtomicOrdering(extra.failureOrder());
2029 const result = self.builder.buildCmpXchg(
2030 ptr,
2031 expected_value,
2032 new_value,
2033 success_order,
2034 failure_order,
2035 is_weak,
2036 self.single_threaded,
2037 );
2038
2039 const optional_ty = self.air.typeOfIndex(inst);
2040 var buffer: Type.Payload.ElemType = undefined;
2041 const child_ty = optional_ty.optionalChild(&buffer);
2042
2043 var payload = self.builder.buildExtractValue(result, 0, "");
2044 if (opt_abi_ty != null) {
2045 payload = self.builder.buildTrunc(payload, try self.dg.llvmType(operand_ty), "");
2046 }
2047 const success_bit = self.builder.buildExtractValue(result, 1, "");
2048
2049 if (optional_ty.isPtrLikeOptional()) {
2050 const child_llvm_ty = try self.dg.llvmType(child_ty);
2051 return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, "");
2052 }
2053
2054 const optional_llvm_ty = try self.dg.llvmType(optional_ty);
2055 const non_null_bit = self.builder.buildNot(success_bit, "");
2056 const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, "");
2057 return self.builder.buildInsertValue(partial, non_null_bit, 1, "");
2058 }
2059
19782060 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
19792061 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
19802062 assert(id != 0);
......@@ -2125,3 +2207,14 @@ fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {
21252207 .spirv64 => {},
21262208 }
21272209}
2210
2211fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrdering {
2212 return switch (atomic_order) {
2213 .Unordered => .Unordered,
2214 .Monotonic => .Monotonic,
2215 .Acquire => .Acquire,
2216 .Release => .Release,
2217 .AcqRel => .AcquireRelease,
2218 .SeqCst => .SequentiallyConsistent,
2219 };
2220}
src/codegen/llvm/bindings.zig+39
......@@ -298,6 +298,14 @@ pub const Builder = opaque {
298298 Name: [*:0]const u8,
299299 ) *const Value;
300300
301 pub const buildSExt = LLVMBuildSExt;
302 extern fn LLVMBuildSExt(
303 *const Builder,
304 Val: *const Value,
305 DestTy: *const Type,
306 Name: [*:0]const u8,
307 ) *const Value;
308
301309 pub const buildCall = LLVMBuildCall;
302310 extern fn LLVMBuildCall(
303311 *const Builder,
......@@ -493,6 +501,27 @@ pub const Builder = opaque {
493501 Index: c_uint,
494502 Name: [*:0]const u8,
495503 ) *const Value;
504
505 pub const buildCmpXchg = ZigLLVMBuildCmpXchg;
506 extern fn ZigLLVMBuildCmpXchg(
507 builder: *const Builder,
508 ptr: *const Value,
509 cmp: *const Value,
510 new_val: *const Value,
511 success_ordering: AtomicOrdering,
512 failure_ordering: AtomicOrdering,
513 is_weak: bool,
514 is_single_threaded: bool,
515 ) *const Value;
516
517 pub const buildSelect = LLVMBuildSelect;
518 extern fn LLVMBuildSelect(
519 *const Builder,
520 If: *const Value,
521 Then: *const Value,
522 Else: *const Value,
523 Name: [*:0]const u8,
524 ) *const Value;
496525};
497526
498527pub const IntPredicate = enum(c_uint) {
......@@ -854,3 +883,13 @@ pub const Linkage = enum(c_uint) {
854883 LinkerPrivate,
855884 LinkerPrivateWeak,
856885};
886
887pub const AtomicOrdering = enum(c_uint) {
888 NotAtomic = 0,
889 Unordered = 1,
890 Monotonic = 2,
891 Acquire = 4,
892 Release = 5,
893 AcquireRelease = 6,
894 SequentiallyConsistent = 7,
895};
src/link/C/zig.h+12
......@@ -60,6 +60,18 @@
6060#define zig_breakpoint() raise(SIGTRAP)
6161#endif
6262
63#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
64#include <stdatomic.h>
65#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail)
66#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail)
67#elif __GNUC__
68#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)
69#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)
70#else
71#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented()
72#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented()
73#endif
74
6375#include <stdint.h>
6476#include <stddef.h>
6577#include <limits.h>
src/print_air.zig+16-1
......@@ -191,6 +191,7 @@ const Writer = struct {
191191 .br => try w.writeBr(s, inst),
192192 .cond_br => try w.writeCondBr(s, inst),
193193 .switch_br => try w.writeSwitchBr(s, inst),
194 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
194195 }
195196 }
196197
......@@ -258,7 +259,21 @@ const Writer = struct {
258259
259260 try w.writeOperand(s, inst, 0, extra.lhs);
260261 try s.writeAll(", ");
261 try w.writeOperand(s, inst, 0, extra.rhs);
262 try w.writeOperand(s, inst, 1, extra.rhs);
263 }
264
265 fn writeCmpxchg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
266 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
267 const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
268
269 try w.writeOperand(s, inst, 0, extra.ptr);
270 try s.writeAll(", ");
271 try w.writeOperand(s, inst, 1, extra.expected_value);
272 try s.writeAll(", ");
273 try w.writeOperand(s, inst, 2, extra.new_value);
274 try s.print(", {s}, {s}", .{
275 @tagName(extra.successOrder()), @tagName(extra.failureOrder()),
276 });
262277 }
263278
264279 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
src/stage1/codegen.cpp+1-1
......@@ -5723,7 +5723,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A
57235723 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);
57245724
57255725 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
5726 success_order, failure_order, instruction->is_weak);
5726 success_order, failure_order, instruction->is_weak, g->is_single_threaded);
57275727
57285728 ZigType *optional_type = instruction->base.value->type;
57295729 assert(optional_type->id == ZigTypeIdOptional);
src/target.zig+69
......@@ -475,3 +475,72 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {
475475pub fn needUnwindTables(target: std.Target) bool {
476476 return target.os.tag == .windows;
477477}
478
479/// TODO this was ported from stage1 but it does not take into account CPU features,
480/// which can affect this value. Audit this!
481pub fn largestAtomicBits(target: std.Target) u32 {
482 return switch (target.cpu.arch) {
483 .avr,
484 .msp430,
485 .spu_2,
486 => 16,
487
488 .arc,
489 .arm,
490 .armeb,
491 .hexagon,
492 .le32,
493 .mips,
494 .mipsel,
495 .nvptx,
496 .powerpc,
497 .powerpcle,
498 .r600,
499 .riscv32,
500 .sparc,
501 .sparcel,
502 .tce,
503 .tcele,
504 .thumb,
505 .thumbeb,
506 .i386,
507 .xcore,
508 .amdil,
509 .hsail,
510 .spir,
511 .kalimba,
512 .lanai,
513 .shave,
514 .wasm32,
515 .renderscript32,
516 .csky,
517 .spirv32,
518 => 32,
519
520 .aarch64,
521 .aarch64_be,
522 .aarch64_32,
523 .amdgcn,
524 .bpfel,
525 .bpfeb,
526 .le64,
527 .mips64,
528 .mips64el,
529 .nvptx64,
530 .powerpc64,
531 .powerpc64le,
532 .riscv64,
533 .sparcv9,
534 .s390x,
535 .amdil64,
536 .hsail64,
537 .spir64,
538 .wasm64,
539 .renderscript64,
540 .ve,
541 .spirv64,
542 => 64,
543
544 .x86_64 => 128,
545 };
546}
src/type.zig+29
......@@ -2886,6 +2886,35 @@ pub const Type = extern union {
28862886 }
28872887 }
28882888
2889 /// Returns the integer tag type of the enum.
2890 pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type {
2891 switch (ty.tag()) {
2892 .enum_full, .enum_nonexhaustive => {
2893 const enum_full = ty.cast(Payload.EnumFull).?.data;
2894 return enum_full.tag_ty;
2895 },
2896 .enum_simple => {
2897 const enum_simple = ty.castTag(.enum_simple).?.data;
2898 buffer.* = .{
2899 .base = .{ .tag = .int_unsigned },
2900 .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()),
2901 };
2902 return Type.initPayload(&buffer.base);
2903 },
2904 .atomic_order,
2905 .atomic_rmw_op,
2906 .calling_convention,
2907 .float_mode,
2908 .reduce_op,
2909 .call_options,
2910 .export_options,
2911 .extern_options,
2912 => @panic("TODO resolve std.builtin types"),
2913
2914 else => unreachable,
2915 }
2916 }
2917
28892918 pub fn isNonexhaustiveEnum(ty: Type) bool {
28902919 return switch (ty.tag()) {
28912920 .enum_nonexhaustive => true,
src/zig_llvm.cpp+6-17
......@@ -1087,10 +1087,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
10871087
10881088LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
10891089 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
1090 LLVMAtomicOrdering failure_ordering, bool is_weak)
1090 LLVMAtomicOrdering failure_ordering, bool is_weak, bool is_single_threaded)
10911091{
1092 AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp),
1093 unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering));
1092 AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr),
1093 unwrap(cmp), unwrap(new_val),
1094 mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering),
1095 is_single_threaded ? SyncScope::SingleThread : SyncScope::System);
10941096 inst->setWeak(is_weak);
10951097 return wrap(inst);
10961098}
......@@ -1308,19 +1310,6 @@ static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {
13081310 }
13091311}
13101312
1311static AtomicOrdering toLLVMOrdering(LLVMAtomicOrdering Ordering) {
1312 switch (Ordering) {
1313 default:
1314 case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic;
1315 case LLVMAtomicOrderingUnordered: return AtomicOrdering::Unordered;
1316 case LLVMAtomicOrderingMonotonic: return AtomicOrdering::Monotonic;
1317 case LLVMAtomicOrderingAcquire: return AtomicOrdering::Acquire;
1318 case LLVMAtomicOrderingRelease: return AtomicOrdering::Release;
1319 case LLVMAtomicOrderingAcquireRelease: return AtomicOrdering::AcquireRelease;
1320 case LLVMAtomicOrderingSequentiallyConsistent: return AtomicOrdering::SequentiallyConsistent;
1321 }
1322}
1323
13241313inline LLVMAttributeRef wrap(Attribute Attr) {
13251314 return reinterpret_cast<LLVMAttributeRef>(Attr.getRawPointer());
13261315}
......@@ -1335,7 +1324,7 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp
13351324{
13361325 AtomicRMWInst::BinOp intop = toLLVMRMWBinOp(op);
13371326 return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR),
1338 unwrap(Val), toLLVMOrdering(ordering),
1327 unwrap(Val), mapFromLLVMOrdering(ordering),
13391328 singleThread ? SyncScope::SingleThread : SyncScope::System));
13401329}
13411330
src/zig_llvm.h+1-1
......@@ -148,7 +148,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSShlSat(LLVMBuilderRef builder, LLVMValueR
148148
149149ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
150150 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
151 LLVMAtomicOrdering failure_ordering, bool is_weak);
151 LLVMAtomicOrdering failure_ordering, bool is_weak, bool is_single_threaded);
152152
153153ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
154154 const char *name);
test/behavior/atomics.zig+22
......@@ -2,3 +2,25 @@ const std = @import("std");
22const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const builtin = @import("builtin");
5
6test "cmpxchg" {
7 try testCmpxchg();
8 comptime try testCmpxchg();
9}
10
11fn testCmpxchg() !void {
12 var x: i32 = 1234;
13 if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| {
14 try expect(x1 == 1234);
15 } else {
16 @panic("cmpxchg should have failed");
17 }
18
19 while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| {
20 try expect(x1 == 1234);
21 }
22 try expect(x == 5678);
23
24 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);
25 try expect(x == 42);
26}
test/behavior/atomics_stage1.zig-22
......@@ -3,28 +3,6 @@ const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const builtin = @import("builtin");
55
6test "cmpxchg" {
7 try testCmpxchg();
8 comptime try testCmpxchg();
9}
10
11fn testCmpxchg() !void {
12 var x: i32 = 1234;
13 if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| {
14 try expect(x1 == 1234);
15 } else {
16 @panic("cmpxchg should have failed");
17 }
18
19 while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| {
20 try expect(x1 == 1234);
21 }
22 try expect(x == 5678);
23
24 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);
25 try expect(x == 42);
26}
27
286test "fence" {
297 var x: i32 = 1234;
308 @fence(.SeqCst);