authorgravatar for 30519309+Scibuild@users.noreply.github.comScibuild <30519309+Scibuild@users.noreply.github.com> 2021-11-18 08:26:15+11:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-23 13:46:56-07:00
log2e15a404e287982b261c14240db44af9175f765a
tree386b910dd63f698bd41abb84957efe3c788d824b
parent8f1e417757f2172485cdea792580e2a2afb53ffa

C backend: errors and optionals

* bitcast treats all pointers as pointers * correctly unwrapping error unions with pointers * equality operators for primitive optional types

4 files changed, 79 insertions(+), 28 deletions(-)

src/codegen/c.zig+52-8
......@@ -1162,12 +1162,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11621162
11631163 .slice => try airSlice(f, inst),
11641164
1165 .cmp_eq => try airBinOp(f, inst, " == "),
1165 .cmp_eq => try airEquality(f, inst, .cmp_eq),
11661166 .cmp_gt => try airBinOp(f, inst, " > "),
11671167 .cmp_gte => try airBinOp(f, inst, " >= "),
11681168 .cmp_lt => try airBinOp(f, inst, " < "),
11691169 .cmp_lte => try airBinOp(f, inst, " <= "),
1170 .cmp_neq => try airBinOp(f, inst, " != "),
1170 .cmp_neq => try airEquality(f, inst, .cmp_neq),
11711171
11721172 // bool_and and bool_or are non-short-circuit operations
11731173 .bool_and => try airBinOp(f, inst, " & "),
......@@ -1257,9 +1257,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
12571257 .slice_elem_ptr => try airSliceElemPtr(f, inst),
12581258 .array_elem_val => try airArrayElemVal(f, inst),
12591259
1260 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst),
1260 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst, ""),
12611261 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),
1262 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst),
1262 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, "&"),
12631263 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst),
12641264 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
12651265 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
......@@ -1908,6 +1908,51 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
19081908 return local;
19091909}
19101910
1911fn airEquality(f: *Function, inst: Air.Inst.Index, op: Air.Inst.Tag) !CValue {
1912 if (f.liveness.isUnused(inst))
1913 return CValue.none;
1914
1915 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1916 const lhs = try f.resolveInst(bin_op.lhs);
1917 const rhs = try f.resolveInst(bin_op.rhs);
1918
1919 const writer = f.object.writer();
1920 const inst_ty = f.air.typeOfIndex(inst);
1921 const local = try f.allocLocal(inst_ty, .Const);
1922
1923 try writer.writeAll(" = ");
1924
1925 const lhs_ty = f.air.typeOf(bin_op.lhs);
1926 if (lhs_ty.tag() == .optional) {
1927 // (A && B) || (C && (A == B))
1928 // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload
1929
1930 try writer.writeAll(if (op == .cmp_eq) "((" else "!((");
1931 try f.writeCValue(writer, lhs);
1932 try writer.writeAll(".is_null && ");
1933 try f.writeCValue(writer, rhs);
1934 try writer.writeAll(".is_null) || (");
1935 try f.writeCValue(writer, lhs);
1936 try writer.writeAll(".payload == ");
1937 try f.writeCValue(writer, rhs);
1938 try writer.writeAll(".payload && ");
1939 try f.writeCValue(writer, lhs);
1940 try writer.writeAll(".is_null == ");
1941 try f.writeCValue(writer, rhs);
1942 try writer.writeAll(".is_null));\n");
1943
1944 return local;
1945 }
1946
1947 const operator = if (op == .cmp_eq) "==" else "!=";
1948 try f.writeCValue(writer, lhs);
1949 try writer.print("{s}", .{operator});
1950 try f.writeCValue(writer, rhs);
1951 try writer.writeAll(";\n");
1952
1953 return local;
1954}
1955
19111956fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
19121957 if (f.liveness.isUnused(inst))
19131958 return CValue.none;
......@@ -2104,8 +2149,8 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
21042149
21052150 const writer = f.object.writer();
21062151 const inst_ty = f.air.typeOfIndex(inst);
2107 if (inst_ty.zigTypeTag() == .Pointer and
2108 f.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
2152 if (inst_ty.isPtrAtRuntime() and
2153 f.air.typeOf(ty_op.operand).isPtrAtRuntime())
21092154 {
21102155 const local = try f.allocLocal(inst_ty, .Const);
21112156 try writer.writeAll(" = (");
......@@ -2503,7 +2548,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
25032548 return local;
25042549}
25052550
2506fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
2551fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []const u8) !CValue {
25072552 if (f.liveness.isUnused(inst))
25082553 return CValue.none;
25092554
......@@ -2519,7 +2564,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
25192564
25202565 const inst_ty = f.air.typeOfIndex(inst);
25212566 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
2522 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
25232567
25242568 const local = try f.allocLocal(inst_ty, .Const);
25252569 try writer.print(" = {s}(", .{maybe_addrof});
test/behavior.zig+4-1
......@@ -41,6 +41,8 @@ test {
4141 _ = @import("behavior/member_func.zig");
4242 _ = @import("behavior/translate_c_macros.zig");
4343 _ = @import("behavior/generics.zig");
44 _ = @import("behavior/error.zig");
45 _ = @import("behavior/optional.zig");
4446
4547 if (builtin.object_format != .c) {
4648 // Tests that pass for stage1 and stage2 but not the C backend.
......@@ -55,6 +57,7 @@ test {
5557 _ = @import("behavior/bugs/2006.zig");
5658 _ = @import("behavior/bugs/3112.zig");
5759 _ = @import("behavior/cast_llvm.zig");
60 _ = @import("behavior/error.zig");
5861 _ = @import("behavior/eval.zig");
5962 _ = @import("behavior/floatop.zig");
6063 _ = @import("behavior/fn.zig");
......@@ -63,7 +66,7 @@ test {
6366 _ = @import("behavior/math.zig");
6467 _ = @import("behavior/maximum_minimum.zig");
6568 _ = @import("behavior/null_llvm.zig");
66 _ = @import("behavior/optional.zig");
69 _ = @import("behavior/optional_llvm.zig");
6770 _ = @import("behavior/popcount.zig");
6871 _ = @import("behavior/saturating_arithmetic.zig");
6972 _ = @import("behavior/sizeof_and_typeof.zig");
test/behavior/optional.zig-19
......@@ -18,25 +18,6 @@ test "passing an optional integer as a parameter" {
1818 comptime try expect(S.entry());
1919}
2020
21test "self-referential struct through a slice of optional" {
22 const S = struct {
23 const Node = struct {
24 children: []?Node,
25 data: ?u8,
26
27 fn new() Node {
28 return Node{
29 .children = undefined,
30 .data = null,
31 };
32 }
33 };
34 };
35
36 var n = S.Node.new();
37 try expect(n.data == null);
38}
39
4021pub const EmptyStruct = struct {};
4122
4223test "optional pointer to size zero struct" {
test/behavior/optional_llvm.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectEqual = testing.expectEqual;
5
6test "self-referential struct through a slice of optional" {
7 const S = struct {
8 const Node = struct {
9 children: []?Node,
10 data: ?u8,
11
12 fn new() Node {
13 return Node{
14 .children = undefined,
15 .data = null,
16 };
17 }
18 };
19 };
20
21 var n = S.Node.new();
22 try expect(n.data == null);
23}