Skip to content

Commit 0395b35

Browse files
committed
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.
1 parent 5d14590 commit 0395b35

19 files changed

+682
-115
lines changed

src/Air.zig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

450+
pub 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+
446467
pub 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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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

22512268
pub 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| {

0 commit comments

Comments
 (0)