From b4c571301be7dd2174b2d067d643a2a093797e7e Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 7 Jul 2020 14:55:44 -0400 Subject: Stage2: Refactor in preparation for C backend --- src-self-hosted/Module.zig | 60 +++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 24 deletions(-) (limited to 'src-self-hosted/Module.zig') diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index f4d65ab7c0..ac7bd00170 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -26,7 +26,7 @@ root_pkg: *Package, /// Module owns this resource. /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. root_scope: *Scope, -bin_file: link.ElfFile, +bin_file: *link.File, bin_file_dir: std.fs.Dir, bin_file_path: []const u8, /// It's rare for a decl to be exported, so we save memory by having a sparse map of @@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export), decl_table: DeclTable, optimize_mode: std.builtin.Mode, -link_error_flags: link.ElfFile.ErrorFlags = .{}, +link_error_flags: link.File.ErrorFlags = .{}, work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), @@ -91,7 +91,7 @@ pub const Export = struct { /// Byte offset into the file that contains the export directive. src: usize, /// Represents the position of the export, if any, in the output file. - link: link.ElfFile.Export, + link: link.File.Elf.Export, /// The Decl that performs the export. Note that this is *not* the Decl being exported. owner_decl: *Decl, /// The Decl being exported. Note this is *not* the Decl performing the export. @@ -169,7 +169,7 @@ pub const Decl = struct { /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. - link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty, + link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty, contents_hash: std.zig.SrcHash, @@ -722,6 +722,12 @@ pub const AllErrors = struct { } }; +pub const CStandard = enum { + C99, + GNU99, + C11, +}; + pub const InitOptions = struct { target: std.Target, root_pkg: *Package, @@ -732,17 +738,19 @@ pub const InitOptions = struct { object_format: ?std.builtin.ObjectFormat = null, optimize_mode: std.builtin.Mode = .Debug, keep_source_files_loaded: bool = false, + c_standard: ?CStandard = null, }; pub fn init(gpa: *Allocator, options: InitOptions) !Module { const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); - var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{ + const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{ .target = options.target, .output_mode = options.output_mode, .link_mode = options.link_mode orelse .Static, .object_format = options.object_format orelse options.target.getObjectFormat(), + .c_standard = options.c_standard, }); - errdefer bin_file.deinit(); + errdefer bin_file.*.deinit(); const root_scope = blk: { if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) { @@ -793,6 +801,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { pub fn deinit(self: *Module) void { self.bin_file.deinit(); const allocator = self.allocator; + allocator.destroy(self.bin_file); self.deletion_set.deinit(allocator); self.work_queue.deinit(); @@ -840,7 +849,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void { } pub fn target(self: Module) std.Target { - return self.bin_file.options.target; + return self.bin_file.options().target; } /// Detect changes to source files, perform semantic analysis, and update the output files. @@ -882,7 +891,7 @@ pub fn update(self: *Module) !void { try self.deleteDecl(decl); } - self.link_error_flags = self.bin_file.error_flags; + self.link_error_flags = self.bin_file.errorFlags(); // If there are any errors, we anticipate the source files being loaded // to report error messages. Otherwise we unload all source files to save memory. @@ -1898,8 +1907,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { self.decl_exports.removeAssertDiscard(exp.exported_decl); } } - - self.bin_file.deleteExport(exp.link); + if (self.bin_file.cast(link.File.Elf)) |elf| { + elf.deleteExport(exp.link); + } if (self.failed_exports.remove(exp)) |entry| { entry.value.destroy(self.allocator); } @@ -1961,7 +1971,7 @@ fn allocateNewDecl( .analysis = .unreferenced, .deletion_flag = false, .contents_hash = contents_hash, - .link = link.ElfFile.TextBlock.empty, + .link = link.File.Elf.TextBlock.empty, .generation = 0, }; return new_decl; @@ -2189,19 +2199,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const } try self.symbol_exports.putNoClobber(symbol_name, new_export); - self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => { - try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1); - self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( - self.allocator, - src, - "unable to export: {}", - .{@errorName(err)}, - )); - new_export.status = .failed_retryable; - }, - }; + if (self.bin_file.cast(link.File.Elf)) |elf| { + elf.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1); + self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( + self.allocator, + src, + "unable to export: {}", + .{@errorName(err)}, + )); + new_export.status = .failed_retryable; + }, + }; + } } fn addNewInstArgs( -- cgit v1.2.3 From b91cf1597267275dbbf57551acbe0461216ff08e Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 7 Jul 2020 22:57:34 -0400 Subject: CBE: Move standards determination to generated code --- src-self-hosted/Module.zig | 10 ++-------- src-self-hosted/cbe.h | 8 ++++++++ src-self-hosted/cgen.zig | 9 ++++++--- src-self-hosted/link.zig | 8 +++++--- src-self-hosted/main.zig | 20 +++++--------------- src-self-hosted/test.zig | 42 +++++++++++++++++++++--------------------- test/stage2/cbe.zig | 20 ++++++++++---------- 7 files changed, 57 insertions(+), 60 deletions(-) create mode 100644 src-self-hosted/cbe.h (limited to 'src-self-hosted/Module.zig') diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index ac7bd00170..2ef85c4d50 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -722,12 +722,6 @@ pub const AllErrors = struct { } }; -pub const CStandard = enum { - C99, - GNU99, - C11, -}; - pub const InitOptions = struct { target: std.Target, root_pkg: *Package, @@ -738,7 +732,7 @@ pub const InitOptions = struct { object_format: ?std.builtin.ObjectFormat = null, optimize_mode: std.builtin.Mode = .Debug, keep_source_files_loaded: bool = false, - c_standard: ?CStandard = null, + cbe: bool = false, }; pub fn init(gpa: *Allocator, options: InitOptions) !Module { @@ -748,7 +742,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { .output_mode = options.output_mode, .link_mode = options.link_mode orelse .Static, .object_format = options.object_format orelse options.target.getObjectFormat(), - .c_standard = options.c_standard, + .cbe = options.cbe, }); errdefer bin_file.*.deinit(); diff --git a/src-self-hosted/cbe.h b/src-self-hosted/cbe.h new file mode 100644 index 0000000000..85b07eb48a --- /dev/null +++ b/src-self-hosted/cbe.h @@ -0,0 +1,8 @@ +#if __STDC_VERSION__ >= 201112L +#define noreturn _Noreturn +#elif !__STRICT_ANSI__ +#define noreturn __attribute__ ((noreturn)) +#else +#define noreturn +#endif + diff --git a/src-self-hosted/cgen.zig b/src-self-hosted/cgen.zig index 5b04d98119..872fa3e830 100644 --- a/src-self-hosted/cgen.zig +++ b/src-self-hosted/cgen.zig @@ -7,7 +7,6 @@ const std = @import("std"); const C = link.File.C; const Decl = Module.Decl; -const CStandard = Module.CStandard; const mem = std.mem; /// Maps a name from Zig source to C. This will always give the same output for @@ -22,7 +21,10 @@ fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type) !void { try writer.writeAll("size_t"); } else { switch (T.zigTypeTag()) { - .NoReturn => try writer.writeAll("_Noreturn void"), + .NoReturn => { + file.need_noreturn = true; + try writer.writeAll("noreturn void"); + }, .Void => try writer.writeAll("void"), else => return error.Unimplemented, } @@ -41,13 +43,14 @@ fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *De } } -pub fn generate(file: *C, decl: *Decl, standard: CStandard) !void { +pub fn generate(file: *C, decl: *Decl) !void { const writer = file.main.writer(); const header = file.header.writer(); const tv = decl.typed_value.most_recent.typed_value; switch (tv.ty.zigTypeTag()) { .Fn => { try renderFunctionSignature(file, writer, decl); + try writer.writeAll(" {"); const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 8352f64fca..0efab9657f 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -22,7 +22,7 @@ pub const Options = struct { /// Used for calculating how much space to reserve for executable program code in case /// the binary file deos not already have such a section. program_code_size_hint: u64 = 256 * 1024, - c_standard: ?Module.CStandard = null, + cbe: bool = false, }; /// Attempts incremental linking, if the file already exists. @@ -38,7 +38,7 @@ pub fn openBinFilePath( const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) }); errdefer file.close(); - if (options.c_standard) |cstd| { + if (options.cbe) { var bin_file = try allocator.create(File.C); errdefer allocator.destroy(bin_file); bin_file.* = try openCFile(allocator, file, options); @@ -217,6 +217,7 @@ pub const File = struct { called: std.StringHashMap(void), need_stddef: bool = false, need_stdint: bool = false, + need_noreturn: bool = false, pub fn makeWritable(self: *File.C, dir: fs.Dir, sub_path: []const u8) !void { assert(self.owns_file_handle); @@ -239,11 +240,12 @@ pub const File = struct { } pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void { - try cgen.generate(self, decl, self.options.c_standard.?); + try cgen.generate(self, decl); } pub fn flush(self: *File.C) !void { const writer = self.file.?.writer(); + try writer.writeAll(@embedFile("cbe.h")); var includes = false; if (self.need_stddef) { try writer.writeAll("#include \n"); diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index fdbc5fc1ae..fc02a57cc8 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -191,7 +191,7 @@ fn buildOutputType( var emit_zir: Emit = .no; var target_arch_os_abi: []const u8 = "native"; var target_mcpu: ?[]const u8 = null; - var target_c_standard: ?Module.CStandard = null; + var cbe: bool = false; var target_dynamic_linker: ?[]const u8 = null; var system_libs = std.ArrayList([]const u8).init(gpa); @@ -279,18 +279,8 @@ fn buildOutputType( } i += 1; target_mcpu = args[i]; - } else if (mem.eql(u8, arg, "--c-standard")) { - if (i + 1 >= args.len) { - std.debug.print("expected parameter after --c-standard\n", .{}); - process.exit(1); - } - i += 1; - if (std.meta.stringToEnum(Module.CStandard, args[i])) |cstd| { - target_c_standard = cstd; - } else { - std.debug.print("Invalid C standard: {}\n", .{args[i]}); - process.exit(1); - } + } else if (mem.eql(u8, arg, "--c")) { + cbe = true; } else if (mem.startsWith(u8, arg, "-mcpu=")) { target_mcpu = arg["-mcpu=".len..]; } else if (mem.eql(u8, arg, "--dynamic-linker")) { @@ -374,7 +364,7 @@ fn buildOutputType( } } - if (target_c_standard != null and output_mode != .Obj) { + if (cbe and output_mode != .Obj) { std.debug.print("The C backend must be used with build-obj\n", .{}); process.exit(1); } @@ -479,7 +469,7 @@ fn buildOutputType( .object_format = object_format, .optimize_mode = build_mode, .keep_source_files_loaded = zir_out_path != null, - .c_standard = target_c_standard, + .cbe = cbe, }); defer module.deinit(); diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 80e40900a0..6eca7ab651 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -5,6 +5,8 @@ const Allocator = std.mem.Allocator; const zir = @import("zir.zig"); const Package = @import("Package.zig"); +const cheader = @embedFile("cbe.h"); + test "self-hosted" { var ctx = TestContext.init(); defer ctx.deinit(); @@ -68,7 +70,7 @@ pub const TestContext = struct { output_mode: std.builtin.OutputMode, updates: std.ArrayList(Update), extension: TestType, - c_standard: ?Module.CStandard = null, + cbe: bool = false, /// Adds a subcase in which the module is updated with `src`, and the /// resulting ZIR is validated against `result`. @@ -188,20 +190,20 @@ pub const TestContext = struct { return ctx.addObj(name, target, .ZIR); } - pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType, standard: Module.CStandard) *Case { + pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case { ctx.cases.append(Case{ .name = name, .target = target, .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Obj, .extension = T, - .c_standard = standard, + .cbe = true, }) catch unreachable; return &ctx.cases.items[ctx.cases.items.len - 1]; } - pub fn c11(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, c: [:0]const u8) void { - ctx.addC(name, target, .Zig, .C11).addTransform(src, c); + pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { + ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out); } pub fn addCompareOutput( @@ -382,13 +384,13 @@ pub const TestContext = struct { } fn deinit(self: *TestContext) void { - for (self.cases.items) |c| { - for (c.updates.items) |u| { + for (self.cases.items) |case| { + for (case.updates.items) |u| { if (u.case == .Error) { - c.updates.allocator.free(u.case.Error); + case.updates.allocator.free(u.case.Error); } } - c.updates.deinit(); + case.updates.deinit(); } self.cases.deinit(); self.* = undefined; @@ -442,7 +444,7 @@ pub const TestContext = struct { .bin_file_path = bin_name, .root_pkg = root_pkg, .keep_source_files_loaded = true, - .c_standard = case.c_standard, + .cbe = case.cbe, }); defer module.deinit(); @@ -477,24 +479,22 @@ pub const TestContext = struct { switch (update.case) { .Transformation => |expected_output| { - var label: []const u8 = "ZIR"; - if (case.c_standard) |cstd| { - label = @tagName(cstd); - var c: *link.File.C = module.bin_file.cast(link.File.C).?; - c.file.?.close(); - c.file = null; + if (case.cbe) { + var cfile: *link.File.C = module.bin_file.cast(link.File.C).?; + cfile.file.?.close(); + cfile.file = null; var file = try tmp.dir.openFile(bin_name, .{ .read = true }); defer file.close(); var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!"); defer allocator.free(out); if (expected_output.len != out.len) { - std.debug.warn("\nTransformed {} length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ label, expected_output, out }); + std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); std.process.exit(1); } for (expected_output) |e, i| { if (out[i] != e) { - std.debug.warn("\nTransformed {} differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ label, expected_output, out }); + std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); std.process.exit(1); } } @@ -518,12 +518,12 @@ pub const TestContext = struct { defer test_node.end(); if (expected_output.len != out_zir.items.len) { - std.debug.warn("{}\nTransformed {} length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, label, expected_output, out_zir.items }); + std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items }); std.process.exit(1); } for (expected_output) |e, i| { if (out_zir.items[i] != e) { - std.debug.warn("{}\nTransformed {} differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, label, expected_output, out_zir.items }); + std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items }); std.process.exit(1); } } @@ -561,7 +561,7 @@ pub const TestContext = struct { } }, .Execution => |expected_stdout| { - std.debug.assert(case.c_standard == null); + std.debug.assert(!case.cbe); update_node.estimated_total_items = 4; var exec_result = x: { diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 9a28c25e27..e56507bd84 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -9,30 +9,30 @@ const linux_x64 = std.zig.CrossTarget{ }; pub fn addCases(ctx: *TestContext) !void { - ctx.c11("empty start function", linux_x64, + ctx.c("empty start function", linux_x64, \\export fn _start() noreturn {} , - \\_Noreturn void _start(void) {} + \\noreturn void _start(void) {} \\ ); - ctx.c11("less empty start function", linux_x64, + ctx.c("less empty start function", linux_x64, \\fn main() noreturn {} \\ \\export fn _start() noreturn { \\ main(); \\} , - \\_Noreturn void main(void); + \\noreturn void main(void); \\ - \\_Noreturn void _start(void) { + \\noreturn void _start(void) { \\ main(); \\} \\ - \\_Noreturn void main(void) {} + \\noreturn void main(void) {} \\ ); // TODO: implement return values - ctx.c11("inline asm", linux_x64, + ctx.c("inline asm", linux_x64, \\fn exitGood() void { \\ asm volatile ("syscall" \\ : @@ -49,7 +49,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ \\void exitGood(void); \\ - \\_Noreturn void _start(void) { + \\noreturn void _start(void) { \\ exitGood(); \\} \\ @@ -60,7 +60,7 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ ); - //ctx.c11("basic return", linux_x64, + //ctx.c("basic return", linux_x64, // \\fn main() u8 { // \\ return 103; // \\} @@ -73,7 +73,7 @@ pub fn addCases(ctx: *TestContext) !void { // \\ // \\uint8_t main(void); // \\ - // \\_Noreturn void _start(void) { + // \\noreturn void _start(void) { // \\ (void)main(); // \\} // \\ -- cgit v1.2.3 From 089c056dbe1fdbb1da9b1a5bab970ca688d3aa01 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 7 Jul 2020 23:24:30 -0400 Subject: CBE: Improve resource cleanup --- src-self-hosted/Module.zig | 5 ++--- src-self-hosted/link.zig | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'src-self-hosted/Module.zig') diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 2ef85c4d50..9df02dd186 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -744,7 +744,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { .object_format = options.object_format orelse options.target.getObjectFormat(), .cbe = options.cbe, }); - errdefer bin_file.*.deinit(); + errdefer bin_file.destroy(); const root_scope = blk: { if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) { @@ -793,9 +793,8 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { } pub fn deinit(self: *Module) void { - self.bin_file.deinit(); + self.bin_file.destroy(); const allocator = self.allocator; - allocator.destroy(self.bin_file); self.deletion_set.deinit(allocator); self.work_queue.deinit(); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 297b3d8800..da01282995 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -159,6 +159,22 @@ pub const File = struct { } } + pub fn destroy(base: *File) void { + switch (base.tag) { + .Elf => { + const parent = @fieldParentPtr(Elf, "base", base); + parent.deinit(); + parent.allocator.destroy(parent); + }, + .C => { + const parent = @fieldParentPtr(C, "base", base); + parent.deinit(); + parent.allocator.destroy(parent); + }, + else => unreachable, + } + } + pub fn flush(base: *File) !void { try switch (base.tag) { .Elf => @fieldParentPtr(Elf, "base", base).flush(), -- cgit v1.2.3