Commit b91cf15972

Noam Preil <pleasantatk@gmail.com>
2020-07-08 04:57:34
CBE: Move standards determination to generated code
1 parent 5461c48
Changed files (7)
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
+
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;
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 <stddef.h>\n");
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();
 
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();
 
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: {
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();
     //    \\}
     //    \\