diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-04-02 19:11:51 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-04-02 19:11:51 -0700 |
| commit | 97d7fddfb78d17132749cae59fea36fe661bf642 (patch) | |
| tree | 5b01a32f9ee35e6cef838cffb5058e5f0f777229 /src/codegen/c.zig | |
| parent | 43d364afef7f0609f9d897c7ff129ba6b9b3cab0 (diff) | |
| download | zig-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.zig | 118 |
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, |
