aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/c.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen/c.zig')
-rw-r--r--src/codegen/c.zig289
1 files changed, 229 insertions, 60 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index ae439693b8..a9521d21a8 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -39,7 +39,12 @@ const BlockData = struct {
};
pub const CValueMap = std.AutoHashMap(*Inst, CValue);
-pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.HashContext, std.hash_map.default_max_load_percentage);
+pub const TypedefMap = std.HashMap(
+ Type,
+ struct { name: []const u8, rendered: []u8 },
+ Type.HashContext,
+ std.hash_map.default_max_load_percentage,
+);
fn formatTypeAsCIdentifier(
data: Type,
@@ -151,14 +156,49 @@ pub const Object = struct {
render_ty = render_ty.elemType();
}
- try o.dg.renderType(w, render_ty);
-
- const const_prefix = switch (mutability) {
- .Const => "const ",
- .Mut => "",
- };
- try w.print(" {s}", .{const_prefix});
- try o.writeCValue(w, name);
+ if (render_ty.zigTypeTag() == .Fn) {
+ const ret_ty = render_ty.fnReturnType();
+ if (ret_ty.zigTypeTag() == .NoReturn) {
+ // noreturn attribute is not allowed here.
+ try w.writeAll("void");
+ } else {
+ try o.dg.renderType(w, ret_ty);
+ }
+ try w.writeAll(" (*");
+ switch (mutability) {
+ .Const => try w.writeAll("const "),
+ .Mut => {},
+ }
+ try o.writeCValue(w, name);
+ try w.writeAll(")(");
+ const param_len = render_ty.fnParamLen();
+ const is_var_args = render_ty.fnIsVarArgs();
+ if (param_len == 0 and !is_var_args)
+ try w.writeAll("void")
+ else {
+ var index: usize = 0;
+ while (index < param_len) : (index += 1) {
+ if (index > 0) {
+ try w.writeAll(", ");
+ }
+ try o.dg.renderType(w, render_ty.fnParamType(index));
+ }
+ }
+ if (is_var_args) {
+ if (param_len != 0) try w.writeAll(", ");
+ try w.writeAll("...");
+ }
+ try w.writeByte(')');
+ } else {
+ try o.dg.renderType(w, render_ty);
+
+ const const_prefix = switch (mutability) {
+ .Const => "const ",
+ .Mut => "",
+ };
+ try w.print(" {s}", .{const_prefix});
+ try o.writeCValue(w, name);
+ }
try w.writeAll(suffix.items);
}
};
@@ -196,35 +236,72 @@ pub const DeclGen = struct {
return writer.print("{d}", .{val.toSignedInt()});
return writer.print("{d}", .{val.toUnsignedInt()});
},
- .Pointer => switch (val.tag()) {
- .null_value, .zero => try writer.writeAll("NULL"),
- .one => try writer.writeAll("1"),
- .decl_ref => {
- const decl = val.castTag(.decl_ref).?.data;
-
- // Determine if we must pointer cast.
- assert(decl.has_tv);
- if (t.eql(decl.ty)) {
- try writer.print("&{s}", .{decl.name});
- } else {
- try writer.writeAll("(");
- try dg.renderType(writer, t);
- try writer.print(")&{s}", .{decl.name});
- }
- },
- .function => {
- const func = val.castTag(.function).?.data;
- try writer.print("{s}", .{func.owner_decl.name});
+ .Pointer => switch (t.ptrSize()) {
+ .Slice => {
+ try writer.writeByte('(');
+ try dg.renderType(writer, t);
+ try writer.writeAll("){");
+ var buf: Type.Payload.ElemType = undefined;
+ try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
+ try writer.writeAll(", ");
+ try writer.print("{d}", .{val.sliceLen()});
+ try writer.writeAll("}");
},
- .extern_fn => {
- const decl = val.castTag(.extern_fn).?.data;
- try writer.print("{s}", .{decl.name});
+ else => switch (val.tag()) {
+ .null_value, .zero => try writer.writeAll("NULL"),
+ .one => try writer.writeAll("1"),
+ .decl_ref => {
+ const decl = val.castTag(.decl_ref).?.data;
+
+ // Determine if we must pointer cast.
+ assert(decl.has_tv);
+ if (t.eql(decl.ty)) {
+ try writer.print("&{s}", .{decl.name});
+ } else {
+ try writer.writeAll("(");
+ try dg.renderType(writer, t);
+ try writer.print(")&{s}", .{decl.name});
+ }
+ },
+ .function => {
+ const func = val.castTag(.function).?.data;
+ try writer.print("{s}", .{func.owner_decl.name});
+ },
+ .extern_fn => {
+ const decl = val.castTag(.extern_fn).?.data;
+ try writer.print("{s}", .{decl.name});
+ },
+ else => switch (t.ptrSize()) {
+ .Slice => unreachable,
+ .Many => {
+ if (val.castTag(.ref_val)) |ref_val_payload| {
+ const sub_val = ref_val_payload.data;
+ if (sub_val.castTag(.bytes)) |bytes_payload| {
+ const bytes = bytes_payload.data;
+ try writer.writeByte('(');
+ try dg.renderType(writer, t);
+ // TODO: make our own C string escape instead of using std.zig.fmtEscapes
+ try writer.print(")\"{}\"", .{std.zig.fmtEscapes(bytes)});
+ } else {
+ unreachable;
+ }
+ } else {
+ unreachable;
+ }
+ },
+ .One => {
+ var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
+ defer arena.deinit();
+
+ const elem_ty = t.elemType();
+ const elem_val = try val.pointerDeref(&arena.allocator);
+
+ try writer.writeAll("&");
+ try dg.renderValue(writer, elem_ty, elem_val);
+ },
+ .C => unreachable,
+ },
},
- else => |e| return dg.fail(
- .{ .node_offset = 0 },
- "TODO: C backend: implement Pointer value {s}",
- .{@tagName(e)},
- ),
},
.Array => {
// First try specific tag representations for more efficiency.
@@ -329,6 +406,32 @@ pub const DeclGen = struct {
},
}
},
+ .Fn => switch (val.tag()) {
+ .null_value, .zero => try writer.writeAll("NULL"),
+ .one => try writer.writeAll("1"),
+ .decl_ref => {
+ const decl = val.castTag(.decl_ref).?.data;
+
+ // Determine if we must pointer cast.
+ assert(decl.has_tv);
+ if (t.eql(decl.ty)) {
+ try writer.print("&{s}", .{decl.name});
+ } else {
+ try writer.writeAll("(");
+ try dg.renderType(writer, t);
+ try writer.print(")&{s}", .{decl.name});
+ }
+ },
+ .function => {
+ const func = val.castTag(.function).?.data;
+ try writer.print("{s}", .{func.owner_decl.name});
+ },
+ .extern_fn => {
+ const decl = val.castTag(.extern_fn).?.data;
+ try writer.print("{s}", .{decl.name});
+ },
+ else => unreachable,
+ },
else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
@tagName(e),
}),
@@ -339,6 +442,12 @@ pub const DeclGen = struct {
if (!is_global) {
try w.writeAll("static ");
}
+ if (dg.decl.val.castTag(.function)) |func_payload| {
+ const func: *Module.Fn = func_payload.data;
+ if (func.is_cold) {
+ try w.writeAll("ZIG_COLD ");
+ }
+ }
try dg.renderType(w, dg.decl.ty.fnReturnType());
const decl_name = mem.span(dg.decl.name);
try w.print(" {s}(", .{decl_name});
@@ -413,7 +522,35 @@ pub const DeclGen = struct {
.Pointer => {
if (t.isSlice()) {
- return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{});
+ if (dg.typedefs.get(t)) |some| {
+ return w.writeAll(some.name);
+ }
+
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+ const bw = buffer.writer();
+
+ try bw.writeAll("typedef struct { ");
+ const elem_type = t.elemType();
+ try dg.renderType(bw, elem_type);
+ try bw.writeAll(" *");
+ if (t.isConstPtr()) {
+ try bw.writeAll("const ");
+ }
+ if (t.isVolatilePtr()) {
+ try bw.writeAll("volatile ");
+ }
+ try bw.writeAll("ptr; size_t len; } ");
+ const name_index = buffer.items.len;
+ try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
+
+ const rendered = buffer.toOwnedSlice();
+ errdefer dg.typedefs.allocator.free(rendered);
+ const name = rendered[name_index .. rendered.len - 2];
+
+ try dg.typedefs.ensureUnusedCapacity(1);
+ try w.writeAll(name);
+ dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
} else {
try dg.renderType(w, t.elemType());
try w.writeAll(" *");
@@ -446,13 +583,13 @@ pub const DeclGen = struct {
try dg.renderType(bw, child_type);
try bw.writeAll(" payload; bool is_null; } ");
const name_index = buffer.items.len;
- try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)});
+ try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
const rendered = buffer.toOwnedSlice();
errdefer dg.typedefs.allocator.free(rendered);
const name = rendered[name_index .. rendered.len - 2];
- try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
+ try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
},
@@ -465,7 +602,7 @@ pub const DeclGen = struct {
return w.writeAll(some.name);
}
const child_type = t.errorUnionChild();
- const set_type = t.errorUnionSet();
+ const err_set_type = t.errorUnionSet();
var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
defer buffer.deinit();
@@ -475,13 +612,20 @@ pub const DeclGen = struct {
try dg.renderType(bw, child_type);
try bw.writeAll(" payload; uint16_t error; } ");
const name_index = buffer.items.len;
- try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) });
+ if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
+ const func = inf_err_set_payload.data;
+ try bw.print("zig_E_{s};\n", .{func.owner_decl.name});
+ } else {
+ try bw.print("zig_E_{s}_{s};\n", .{
+ typeToCIdentifier(err_set_type), typeToCIdentifier(child_type),
+ });
+ }
const rendered = buffer.toOwnedSlice();
errdefer dg.typedefs.allocator.free(rendered);
const name = rendered[name_index .. rendered.len - 2];
- try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
+ try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
},
@@ -514,7 +658,7 @@ pub const DeclGen = struct {
errdefer dg.typedefs.allocator.free(rendered);
const name = rendered[name_start .. rendered.len - 2];
- try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
+ try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
},
@@ -526,7 +670,28 @@ pub const DeclGen = struct {
try dg.renderType(w, int_tag_ty);
},
.Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}),
- .Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}),
+ .Fn => {
+ try dg.renderType(w, t.fnReturnType());
+ try w.writeAll(" (*)(");
+ const param_len = t.fnParamLen();
+ const is_var_args = t.fnIsVarArgs();
+ if (param_len == 0 and !is_var_args)
+ try w.writeAll("void")
+ else {
+ var index: usize = 0;
+ while (index < param_len) : (index += 1) {
+ if (index > 0) {
+ try w.writeAll(", ");
+ }
+ try dg.renderType(w, t.fnParamType(index));
+ }
+ }
+ if (is_var_args) {
+ if (param_len != 0) try w.writeAll(", ");
+ try w.writeAll("...");
+ }
+ try w.writeByte(')');
+ },
.Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}),
.Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}),
.AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}),
@@ -569,23 +734,27 @@ pub fn genDecl(o: *Object) !void {
.val = o.dg.decl.val,
};
if (tv.val.castTag(.function)) |func_payload| {
- const is_global = o.dg.declIsGlobal(tv);
- const fwd_decl_writer = o.dg.fwd_decl.writer();
- if (is_global) {
- try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
- }
- try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
- try fwd_decl_writer.writeAll(";\n");
-
const func: *Module.Fn = func_payload.data;
- try o.indent_writer.insertNewline();
- try o.dg.renderFunctionSignature(o.writer(), is_global);
+ if (func.owner_decl == o.dg.decl) {
+ const is_global = o.dg.declIsGlobal(tv);
+ const fwd_decl_writer = o.dg.fwd_decl.writer();
+ if (is_global) {
+ try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
+ }
+ try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
+ try fwd_decl_writer.writeAll(";\n");
- try o.writer().writeByte(' ');
- try genBody(o, func.body);
+ try o.indent_writer.insertNewline();
+ try o.dg.renderFunctionSignature(o.writer(), is_global);
- try o.indent_writer.insertNewline();
- } else if (tv.val.tag() == .extern_fn) {
+ try o.writer().writeByte(' ');
+ try genBody(o, func.body);
+
+ try o.indent_writer.insertNewline();
+ return;
+ }
+ }
+ if (tv.val.tag() == .extern_fn) {
const writer = o.writer();
try writer.writeAll("ZIG_EXTERN_C ");
try o.dg.renderFunctionSignature(writer, true);
@@ -644,9 +813,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
const is_global = dg.declIsGlobal(tv);
if (is_global) {
try writer.writeAll("ZIG_EXTERN_C ");
+ try dg.renderFunctionSignature(writer, is_global);
+ try dg.fwd_decl.appendSlice(";\n");
}
- try dg.renderFunctionSignature(writer, is_global);
- try dg.fwd_decl.appendSlice(";\n");
},
else => {},
}