aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/c.zig
diff options
context:
space:
mode:
authorCody Tapscott <topolarity@tapscott.me>2022-03-21 12:18:58 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-03-23 16:29:38 -0400
commita9a91a5d49070acaa783d9d65f34a652659160c9 (patch)
tree4cd4783cdaa0c5c1303dcb673ede437196fe503e /src/codegen/c.zig
parent2f9264d8dcedba3a75dcf9d6a31b82447dfc57e8 (diff)
downloadzig-a9a91a5d49070acaa783d9d65f34a652659160c9.tar.gz
zig-a9a91a5d49070acaa783d9d65f34a652659160c9.zip
stage2 CBE: Improve support for unions and error sets
This includes various fixes/improvements to the C backend to improve error/union support. It also fixes up our handling of decls, where some decls were not correctly marked alive.
Diffstat (limited to 'src/codegen/c.zig')
-rw-r--r--src/codegen/c.zig140
1 files changed, 105 insertions, 35 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index c62abdb3f6..16558c6e04 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -375,8 +375,6 @@ pub const DeclGen = struct {
val: Value,
decl: *Decl,
) error{ OutOfMemory, AnalysisFail }!void {
- decl.markAlive();
-
const target = dg.module.getTarget();
if (ty.isSlice()) {
@@ -461,12 +459,14 @@ pub const DeclGen = struct {
}
}
- // Renders a "child" pointer (e.g. ElemPtr, FieldPtr) by recursing
- // to the root decl/variable that acts as its parent
+ // Renders a "parent" pointer by recursing to the root decl/variable
+ // that its contents are defined with respect to.
//
- // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, since
- // the Type of their container cannot be retrieved from their own Type
- fn renderChildPtr(dg: *DeclGen, writer: anytype, ptr_val: Value) error{ OutOfMemory, AnalysisFail }!Type {
+ // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr
+ fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type) error{ OutOfMemory, AnalysisFail }!void {
+ try writer.writeByte('(');
+ try dg.renderTypecast(writer, ptr_ty);
+ try writer.writeByte(')');
switch (ptr_val.tag()) {
.decl_ref_mut, .decl_ref, .variable => {
const decl = switch (ptr_val.tag()) {
@@ -475,16 +475,12 @@ pub const DeclGen = struct {
.variable => ptr_val.castTag(.variable).?.data.owner_decl,
else => unreachable,
};
- try dg.renderDeclName(writer, decl);
- return decl.ty;
+ try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl);
},
.field_ptr => {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
+ const container_ty = field_ptr.container_ty;
const index = field_ptr.field_index;
-
- try writer.writeAll("&(");
- const container_ty = try dg.renderChildPtr(writer, field_ptr.container_ptr);
-
const field_name = switch (container_ty.zigTypeTag()) {
.Struct => container_ty.structFields().keys()[index],
.Union => container_ty.unionFields().keys()[index],
@@ -495,19 +491,48 @@ pub const DeclGen = struct {
.Union => container_ty.unionFields().values()[index].ty,
else => unreachable,
};
- try writer.print(").{ }", .{fmtIdent(field_name)});
+ var container_ptr_ty_pl: Type.Payload.ElemType = .{
+ .base = .{ .tag = .c_mut_pointer },
+ .data = field_ptr.container_ty,
+ };
+ const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base);
- return field_ty;
+ if (field_ty.hasRuntimeBitsIgnoreComptime()) {
+ try writer.writeAll("&(");
+ try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty);
+ if (field_ptr.container_ty.tag() == .union_tagged) {
+ try writer.print(")->payload.{ }", .{fmtIdent(field_name)});
+ } else {
+ try writer.print(")->{ }", .{fmtIdent(field_name)});
+ }
+ } else {
+ try dg.renderParentPtr(writer, field_ptr.container_ptr, field_ty);
+ }
},
.elem_ptr => {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
+ var elem_ptr_ty_pl: Type.Payload.ElemType = .{
+ .base = .{ .tag = .c_mut_pointer },
+ .data = elem_ptr.elem_ty,
+ };
+ const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base);
+
try writer.writeAll("&(");
- const container_ty = try dg.renderChildPtr(writer, elem_ptr.array_ptr);
+ try dg.renderParentPtr(writer, elem_ptr.array_ptr, elem_ptr_ty);
try writer.print(")[{d}]", .{elem_ptr.index});
- return container_ty.childType();
},
- .opt_payload_ptr => return dg.fail("implement renderChildPtr for optional payload", .{}),
- .eu_payload_ptr => return dg.fail("implement renderChildPtr for error union payload", .{}),
+ .opt_payload_ptr, .eu_payload_ptr => {
+ const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data;
+ var container_ptr_ty_pl: Type.Payload.ElemType = .{
+ .base = .{ .tag = .c_mut_pointer },
+ .data = payload_ptr.container_ty,
+ };
+ const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base);
+
+ try writer.writeAll("&(");
+ try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty);
+ try writer.writeAll(")->payload");
+ },
else => unreachable,
}
}
@@ -559,6 +584,13 @@ pub const DeclGen = struct {
.Int => switch (val.tag()) {
.int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()),
.int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true),
+ .field_ptr,
+ .elem_ptr,
+ .opt_payload_ptr,
+ .eu_payload_ptr,
+ .decl_ref_mut,
+ .decl_ref,
+ => try dg.renderParentPtr(writer, val, ty),
else => {
if (ty.isSignedInt())
return writer.print("{d}", .{val.toSignedInt()});
@@ -586,10 +618,6 @@ pub const DeclGen = struct {
// to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc)
// between pointers and 0, which is an extension to begin with.
.zero => try writer.writeByte('0'),
- .decl_ref => {
- const decl = val.castTag(.decl_ref).?.data;
- return dg.renderDeclValue(writer, ty, val, decl);
- },
.variable => {
const decl = val.castTag(.variable).?.data.owner_decl;
return dg.renderDeclValue(writer, ty, val, decl);
@@ -606,9 +634,6 @@ pub const DeclGen = struct {
try dg.renderValue(writer, Type.usize, slice.len);
try writer.writeAll("}");
},
- .field_ptr, .elem_ptr, .opt_payload_ptr, .eu_payload_ptr => {
- _ = try dg.renderChildPtr(writer, val);
- },
.function => {
const func = val.castTag(.function).?.data;
try dg.renderDeclName(writer, func.owner_decl);
@@ -622,6 +647,13 @@ pub const DeclGen = struct {
try dg.renderTypecast(writer, ty);
try writer.print(")0x{x}u)", .{val.toUnsignedInt(target)});
},
+ .field_ptr,
+ .elem_ptr,
+ .opt_payload_ptr,
+ .eu_payload_ptr,
+ .decl_ref_mut,
+ .decl_ref,
+ => try dg.renderParentPtr(writer, val, ty),
else => unreachable,
},
.Array => {
@@ -1326,7 +1358,7 @@ pub const DeclGen = struct {
return w.writeAll(name);
},
.Struct => {
- const name = dg.getTypedefName(t) orelse if (t.isTuple())
+ const name = dg.getTypedefName(t) orelse if (t.isTuple() or t.tag() == .anon_struct)
try dg.renderTupleTypedef(t)
else
try dg.renderStructTypedef(t);
@@ -1346,11 +1378,11 @@ pub const DeclGen = struct {
try dg.renderType(w, int_tag_ty);
},
+ .Opaque => return w.writeAll("void"),
.Frame,
.AnyFrame,
.Vector,
- .Opaque,
=> |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{
@tagName(tag),
}),
@@ -1497,6 +1529,8 @@ pub const DeclGen = struct {
}
fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void {
+ decl.markAlive();
+
if (dg.module.decl_exports.get(decl)) |exports| {
return writer.writeAll(exports[0].options.name);
} else if (decl.val.tag() == .extern_fn) {
@@ -3188,7 +3222,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
field_name = fields.keys()[index];
field_val_ty = fields.values()[index].ty;
},
- .tuple => {
+ .tuple, .anon_struct => {
const tuple = struct_ty.tupleFields();
if (tuple.values[index].tag() != .unreachable_value) return CValue.none;
@@ -3203,9 +3237,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const);
- try writer.print(" = &", .{});
- try f.writeCValueDeref(writer, struct_ptr);
- try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
+ if (field_val_ty.hasRuntimeBitsIgnoreComptime()) {
+ try writer.writeAll(" = &");
+ try f.writeCValueDeref(writer, struct_ptr);
+ try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
+ } else {
+ try writer.writeAll(" = (");
+ try f.renderTypecast(writer, inst_ty);
+ try writer.writeByte(')');
+ try f.writeCValue(writer, struct_ptr);
+ try writer.writeAll(";\n");
+ }
return local;
}
@@ -3223,7 +3265,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
const field_name = switch (struct_ty.tag()) {
.@"struct" => struct_ty.structFields().keys()[extra.field_index],
.@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
- .tuple => blk: {
+ .tuple, .anon_struct => blk: {
const tuple = struct_ty.tupleFields();
if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
@@ -3348,8 +3390,36 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
}
fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
- _ = inst;
- return f.fail("TODO: C backend: implement airErrUnionPayloadPtrSet", .{});
+ const writer = f.object.writer();
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
+ const error_union_ty = f.air.typeOf(ty_op.operand).childType();
+
+ const error_ty = error_union_ty.errorUnionSet();
+ const payload_ty = error_union_ty.errorUnionPayload();
+
+ // First, set the non-error value.
+ if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
+ try f.writeCValueDeref(writer, operand);
+ try writer.writeAll(" = ");
+ try f.object.dg.renderValue(writer, error_ty, Value.zero);
+ try writer.writeAll(";\n ");
+
+ return operand;
+ }
+ try f.writeCValueDeref(writer, operand);
+ try writer.writeAll(".error = ");
+ try f.object.dg.renderValue(writer, error_ty, Value.zero);
+ try writer.writeAll(";\n");
+
+ // Then return the payload pointer (only if it is used)
+ if (f.liveness.isUnused(inst)) return CValue.none;
+
+ const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
+ try writer.writeAll(" = &(");
+ try f.writeCValueDeref(writer, operand);
+ try writer.writeAll(").payload;\n");
+ return local;
}
fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {