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(); // \\} // \\