Commit ca20d0ea26

Veikka Tuominen <git@vexu.eu>
2021-01-28 18:40:53
stage2 cbe: pointer like optionals
1 parent 5d215cc
Changed files (2)
src
codegen
test
stage2
src/codegen/c.zig
@@ -140,7 +140,7 @@ pub const DeclGen = struct {
                 return writer.print("{d}", .{val.toUnsignedInt()});
             },
             .Pointer => switch (val.tag()) {
-                .undef, .zero => try writer.writeAll("0"),
+                .null_value, .zero => try writer.writeAll("NULL"),
                 .one => try writer.writeAll("1"),
                 .decl_ref => {
                     const decl = val.castTag(.decl_ref).?.data;
@@ -201,6 +201,20 @@ pub const DeclGen = struct {
                 }
             },
             .Bool => return writer.print("{}", .{val.toBool()}),
+            .Optional => {
+                var opt_buf: Type.Payload.ElemType = undefined;
+                const child_type = t.optionalChild(&opt_buf);
+                if (t.isPtrLikeOptional()) {
+                    return dg.renderValue(writer, child_type, val);
+                }
+                if (val.tag() == .null_value) {
+                    try writer.writeAll("{ .is_null = true }");
+                } else {
+                    try writer.writeAll("{ .is_null = false, .payload = ");
+                    try dg.renderValue(writer, child_type, val);
+                    try writer.writeAll(" }");
+                }
+            },
             else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
                 @tagName(e),
             }),
@@ -299,6 +313,14 @@ pub const DeclGen = struct {
                 try dg.renderType(w, t.elemType());
                 try w.writeAll(" *");
             },
+            .Optional => {
+                var opt_buf: Type.Payload.ElemType = undefined;
+                const child_type = t.optionalChild(&opt_buf);
+                if (t.isPtrLikeOptional()) {
+                    return dg.renderType(w, child_type);
+                }
+                return dg.fail(dg.decl.src(), "TODO: C backend: more optional types", .{});
+            },
             .Null, .Undefined => unreachable, // must be const or comptime
             else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
                 @tagName(e),
@@ -429,6 +451,13 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
             .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "),
             .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "),
             .not => try genUnOp(o, inst.castTag(.not).?, "!"),
+            .is_null => try genIsNull(o, inst.castTag(.is_null).?),
+            .is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?),
+            .is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?),
+            .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?),
+            .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?),
+            .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?),
+            .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload).?),
             else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
         };
         switch (result_value) {
@@ -802,6 +831,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
     return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
 }
 
+fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr;
+    const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else "";
+    const operand = try o.resolveInst(inst.operand);
+
+    const local = try o.allocLocal(Type.initTag(.bool), .Const);
+    try writer.writeAll(" = (");
+    try o.writeCValue(writer, operand);
+
+    if (inst.operand.ty.isPtrLikeOptional()) {
+        // operand is a regular pointer, test `operand !=/== NULL`
+        const operator = if (invert_logic) "!=" else "==";
+        try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator });
+    } else {
+        const operator = if (invert_logic) "!=" else "==";
+        try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator });
+    }
+    return local;
+}
+
+fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const operand = try o.resolveInst(inst.operand);
+
+    if (opt_ty.isPtrLikeOptional()) {
+        // the operand is just a regular pointer, no need to do anything special.
+        return operand;
+    }
+
+    const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer)
+        inst.operand.ty.elemType()
+    else
+        inst.operand.ty;
+
+    return o.dg.fail(o.dg.decl.src(), "TODO: C backend: genOptionalPayload non ptr-like optionals", .{});
+}
+
+fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const operand = try o.resolveInst(inst.operand);
+
+    if (inst.base.ty.isPtrLikeOptional()) {
+        // the operand is just a regular pointer, no need to do anything special.
+        return operand;
+    }
+
+    return o.dg.fail(o.dg.decl.src(), "TODO: C backend: genWrapOptional non ptr-like optionals", .{});
+}
+
 fn IndentWriter(comptime UnderlyingWriter: type) type {
     return struct {
         const Self = @This();
test/stage2/cbe.zig
@@ -244,6 +244,21 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , "");
     }
+    {
+        var case = ctx.exeFromCompiledC("optionals", .{});
+
+        // Simple while loop
+        case.addCompareOutput(
+            \\export fn main() c_int {
+            \\    var count: c_int = 0;
+            \\    var opt_ptr: ?*c_int = &count;
+            \\    while (opt_ptr) |_| : (count += 1) {
+            \\        if (count == 4) opt_ptr = null;
+            \\    }
+            \\    return count - 5;
+            \\}
+        , "");
+    }
     ctx.c("empty start function", linux_x64,
         \\export fn _start() noreturn {
         \\    unreachable;