diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-07-07 00:39:23 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-07-07 00:39:23 -0700 |
| commit | 13f04e3012b6b2eee141923f9780fce55f7a999d (patch) | |
| tree | fd8d164d7926d76a1e967b89283322ee6ad88bb0 /src/codegen | |
| parent | d481acc7dbebb5501b5fef608ee1f6b13c442c6a (diff) | |
| download | zig-13f04e3012b6b2eee141923f9780fce55f7a999d.tar.gz zig-13f04e3012b6b2eee141923f9780fce55f7a999d.zip | |
stage2: implement `@panic` and beginnigs of inferred error sets
* ZIR: add two instructions:
- ret_err_value_code
- ret_err_value
* AstGen: add countDefers and utilize it to emit more efficient ZIR for
return expressions in the presence of defers.
* AstGen: implement |err| payloads for `errdefer` syntax.
- There is not an "unused capture" error for it yet.
* AstGen: `return error.Foo` syntax gets a hot path in return
expressions, using the new ZIR instructions. This also is part of
implementing inferred error sets, since we need to tell Sema to add
an error value to the inferred error set before it gets coerced.
* Sema: implement `@setCold`.
- Implement `@setCold` support for C backend.
* `@panic` and regular safety panics such as `unreachable` now properly
invoke `std.builtin.panic`.
* C backend: improve pointer and function value rendering.
* C linker: fix redundant typedefs.
* Add Type.error_set_inferred.
* Fix Value.format for enum_literal, enum_field_index, bytes.
* Remove the C backend test that checks for identical text
I measured a 14% reduction in Total ZIR Bytes from master branch
for std/os.zig.
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 289 |
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 => {}, } |
