aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/c.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-04-02 19:11:51 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-04-02 19:11:51 -0700
commit97d7fddfb78d17132749cae59fea36fe661bf642 (patch)
tree5b01a32f9ee35e6cef838cffb5058e5f0f777229 /src/codegen/c.zig
parent43d364afef7f0609f9d897c7ff129ba6b9b3cab0 (diff)
downloadzig-97d7fddfb78d17132749cae59fea36fe661bf642.tar.gz
zig-97d7fddfb78d17132749cae59fea36fe661bf642.zip
stage2: progress towards basic structs
Introduce `ResultLoc.none_or_ref` which is used by field access expressions to avoid unnecessary loads when the field access itself will do the load. This turns: ```zig p.y - p.x - p.x ``` from ```zir %14 = load(%4) node_offset:8:12 %15 = field_val(%14, "y") node_offset:8:13 %16 = load(%4) node_offset:8:18 %17 = field_val(%16, "x") node_offset:8:19 %18 = sub(%15, %17) node_offset:8:16 %19 = load(%4) node_offset:8:24 %20 = field_val(%19, "x") node_offset:8:25 ``` to ```zir %14 = field_val(%4, "y") node_offset:8:13 %15 = field_val(%4, "x") node_offset:8:19 %16 = sub(%14, %15) node_offset:8:16 %17 = field_val(%4, "x") node_offset:8:25 ``` Much more compact. This requires `Sema.zirFieldVal` to support both pointers and non-pointers. C backend: Implement typedefs for struct types, as well as the following TZIR instructions: * mul * mulwrap * addwrap * subwrap * ref * struct_field_ptr Note that add, addwrap, sub, subwrap, mul, mulwrap instructions are all incorrect currently and need to be updated to properly handle wrapping and non wrapping for signed and unsigned. C backend: change indentation delta to 1, to make the output smaller and to process fewer bytes. I promise I will add a test case as soon as I fix those warnings that are being printed for my test case.
Diffstat (limited to 'src/codegen/c.zig')
-rw-r--r--src/codegen/c.zig118
1 files changed, 106 insertions, 12 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 6e68a43607..876f86ed02 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -44,22 +44,34 @@ fn formatTypeAsCIdentifier(
var buffer = [1]u8{0} ** 128;
// We don't care if it gets cut off, it's still more unique than a number
var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer;
+ return formatIdent(buf, "", .{}, writer);
+}
+
+pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) {
+ return .{ .data = t };
+}
- for (buf) |c, i| {
+fn formatIdent(
+ ident: []const u8,
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ for (ident) |c, i| {
switch (c) {
- 0 => return writer.writeAll(buf[0..i]),
- 'a'...'z', 'A'...'Z', '_', '$' => {},
+ 'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c),
'0'...'9' => if (i == 0) {
- buf[i] = '_';
+ try writer.print("${x:2}", .{c});
+ } else {
+ try writer.writeByte(c);
},
- else => buf[i] = '_',
+ else => try writer.print("${x:2}", .{c}),
}
}
- return writer.writeAll(buf);
}
-pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) {
- return .{ .data = t };
+pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
+ return .{ .data = ident };
}
/// This data is available when outputting .c code for a Module.
@@ -430,6 +442,36 @@ pub const DeclGen = struct {
try w.writeAll(name);
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
},
+ .Struct => {
+ if (dg.typedefs.get(t)) |some| {
+ return w.writeAll(some.name);
+ }
+ const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
+ const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
+ defer dg.typedefs.allocator.free(fqn);
+
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+
+ try buffer.appendSlice("typedef struct {\n");
+ for (struct_obj.fields.entries.items) |entry| {
+ try buffer.append(' ');
+ try dg.renderType(buffer.writer(), entry.value.ty);
+ try buffer.writer().print(" {s};\n", .{fmtIdent(entry.key)});
+ }
+ try buffer.appendSlice("} ");
+
+ const name_start = buffer.items.len;
+ try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)});
+
+ const rendered = buffer.toOwnedSlice();
+ errdefer dg.typedefs.allocator.free(rendered);
+ const name = rendered[name_start .. rendered.len - 2];
+
+ try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
+ try w.writeAll(name);
+ dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
+ },
.Null, .Undefined => unreachable, // must be const or comptime
else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{
@tagName(e),
@@ -525,8 +567,23 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
for (body.instructions) |inst| {
const result_value = switch (inst.tag) {
- .constant => unreachable, // excluded from function bodies
+ // TODO use a different strategy for add that communicates to the optimizer
+ // that wrapping is UB.
.add => try genBinOp(o, inst.castTag(.add).?, " + "),
+ // TODO make this do wrapping arithmetic for signed ints
+ .addwrap => try genBinOp(o, inst.castTag(.add).?, " + "),
+ // TODO use a different strategy for sub that communicates to the optimizer
+ // that wrapping is UB.
+ .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
+ // TODO make this do wrapping arithmetic for signed ints
+ .subwrap => try genBinOp(o, inst.castTag(.sub).?, " - "),
+ // TODO use a different strategy for mul that communicates to the optimizer
+ // that wrapping is UB.
+ .mul => try genBinOp(o, inst.castTag(.sub).?, " * "),
+ // TODO make this do wrapping multiplication for signed ints
+ .mulwrap => try genBinOp(o, inst.castTag(.sub).?, " * "),
+
+ .constant => unreachable, // excluded from function bodies
.alloc => try genAlloc(o, inst.castTag(.alloc).?),
.arg => genArg(o),
.assembly => try genAsm(o, inst.castTag(.assembly).?),
@@ -546,7 +603,6 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
.ret => try genRet(o, inst.castTag(.ret).?),
.retvoid => try genRetVoid(o),
.store => try genStore(o, inst.castTag(.store).?),
- .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
.unreach => try genUnreach(o, inst.castTag(.unreach).?),
.loop => try genLoop(o, inst.castTag(.loop).?),
.condbr => try genCondBr(o, inst.castTag(.condbr).?),
@@ -567,17 +623,24 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
.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_ptr).?),
+ .ref => try genRef(o, inst.castTag(.ref).?),
+ .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
+
.is_err => try genIsErr(o, inst.castTag(.is_err).?),
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
.error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),
.int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),
+
.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
.unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
.unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),
.wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),
.wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
- else => |e| return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for {}", .{e}),
+ .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}),
+ .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}),
+ .varptr => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for varptr", .{}),
+ .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}),
};
switch (result_value) {
.none => {},
@@ -996,6 +1059,37 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
return local;
}
+fn genRef(o: *Object, inst: *Inst.UnOp) !CValue {
+ const writer = o.writer();
+ const operand = try o.resolveInst(inst.operand);
+
+ const local = try o.allocLocal(inst.base.ty, .Const);
+ try writer.writeAll(" = ");
+ try o.writeCValue(writer, operand);
+ try writer.writeAll(";\n");
+ return local;
+}
+
+fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
+ const writer = o.writer();
+ const struct_ptr = try o.resolveInst(inst.struct_ptr);
+ const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data;
+ const field_name = struct_obj.fields.entries.items[inst.field_index].key;
+
+ const local = try o.allocLocal(inst.base.ty, .Const);
+ switch (struct_ptr) {
+ .local_ref => |i| {
+ try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
+ },
+ else => {
+ try writer.writeAll(" = &");
+ try o.writeCValue(writer, struct_ptr);
+ try writer.print("->{};\n", .{fmtIdent(field_name)});
+ },
+ }
+ return local;
+}
+
// *(E!T) -> E NOT *E
fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
const writer = o.writer();
@@ -1088,7 +1182,7 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
pub const Error = UnderlyingWriter.Error;
pub const Writer = std.io.Writer(*Self, Error, write);
- pub const indent_delta = 4;
+ pub const indent_delta = 1;
underlying_writer: UnderlyingWriter,
indent_count: usize = 0,