diff options
| -rw-r--r-- | src/codegen/c.zig | 81 | ||||
| -rw-r--r-- | test/stage2/cbe.zig | 15 |
2 files changed, 95 insertions, 1 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1a323441d9..ac3ddca990 100644 --- a/src/codegen/c.zig +++ b/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(); diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 873f0e1198..b22ef44148 100644 --- a/test/stage2/cbe.zig +++ b/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; |
