| author | |
| committer | |
| log | b91cf1597267275dbbf57551acbe0461216ff08e |
| tree | 1452ddff902d80e49c3d88766fe8ee681ef9fea6 |
| parent | 5461c482d0643abd83ac834be0a95da066c53c69 |
| signature |
7 files changed, 57 insertions(+), 60 deletions(-)
src-self-hosted/Module.zig+2-8| ... | ... | @@ -722,12 +722,6 @@ pub const AllErrors = struct { |
| 722 | 722 | } |
| 723 | 723 | }; |
| 724 | 724 | |
| 725 | pub const CStandard = enum { | |
| 726 | C99, | |
| 727 | GNU99, | |
| 728 | C11, | |
| 729 | }; | |
| 730 | ||
| 731 | 725 | pub const InitOptions = struct { |
| 732 | 726 | target: std.Target, |
| 733 | 727 | root_pkg: *Package, |
| ... | ... | @@ -738,7 +732,7 @@ pub const InitOptions = struct { |
| 738 | 732 | object_format: ?std.builtin.ObjectFormat = null, |
| 739 | 733 | optimize_mode: std.builtin.Mode = .Debug, |
| 740 | 734 | keep_source_files_loaded: bool = false, |
| 741 | c_standard: ?CStandard = null, | |
| 735 | cbe: bool = false, | |
| 742 | 736 | }; |
| 743 | 737 | |
| 744 | 738 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| ... | ... | @@ -748,7 +742,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 748 | 742 | .output_mode = options.output_mode, |
| 749 | 743 | .link_mode = options.link_mode orelse .Static, |
| 750 | 744 | .object_format = options.object_format orelse options.target.getObjectFormat(), |
| 751 | .c_standard = options.c_standard, | |
| 745 | .cbe = options.cbe, | |
| 752 | 746 | }); |
| 753 | 747 | errdefer bin_file.*.deinit(); |
| 754 | 748 |
src-self-hosted/cbe.h created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | #if __STDC_VERSION__ >= 201112L | |
| 2 | #define noreturn _Noreturn | |
| 3 | #elif !__STRICT_ANSI__ | |
| 4 | #define noreturn __attribute__ ((noreturn)) | |
| 5 | #else | |
| 6 | #define noreturn | |
| 7 | #endif | |
| 8 |
src-self-hosted/cgen.zig+6-3| ... | ... | @@ -7,7 +7,6 @@ const std = @import("std"); |
| 7 | 7 | |
| 8 | 8 | const C = link.File.C; |
| 9 | 9 | const Decl = Module.Decl; |
| 10 | const CStandard = Module.CStandard; | |
| 11 | 10 | const mem = std.mem; |
| 12 | 11 | |
| 13 | 12 | /// 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 { |
| 22 | 21 | try writer.writeAll("size_t"); |
| 23 | 22 | } else { |
| 24 | 23 | switch (T.zigTypeTag()) { |
| 25 | .NoReturn => try writer.writeAll("_Noreturn void"), | |
| 24 | .NoReturn => { | |
| 25 | file.need_noreturn = true; | |
| 26 | try writer.writeAll("noreturn void"); | |
| 27 | }, | |
| 26 | 28 | .Void => try writer.writeAll("void"), |
| 27 | 29 | else => return error.Unimplemented, |
| 28 | 30 | } |
| ... | ... | @@ -41,13 +43,14 @@ fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *De |
| 41 | 43 | } |
| 42 | 44 | } |
| 43 | 45 | |
| 44 | pub fn generate(file: *C, decl: *Decl, standard: CStandard) !void { | |
| 46 | pub fn generate(file: *C, decl: *Decl) !void { | |
| 45 | 47 | const writer = file.main.writer(); |
| 46 | 48 | const header = file.header.writer(); |
| 47 | 49 | const tv = decl.typed_value.most_recent.typed_value; |
| 48 | 50 | switch (tv.ty.zigTypeTag()) { |
| 49 | 51 | .Fn => { |
| 50 | 52 | try renderFunctionSignature(file, writer, decl); |
| 53 | ||
| 51 | 54 | try writer.writeAll(" {"); |
| 52 | 55 | |
| 53 | 56 | const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func; |
src-self-hosted/link.zig+5-3| ... | ... | @@ -22,7 +22,7 @@ pub const Options = struct { |
| 22 | 22 | /// Used for calculating how much space to reserve for executable program code in case |
| 23 | 23 | /// the binary file deos not already have such a section. |
| 24 | 24 | program_code_size_hint: u64 = 256 * 1024, |
| 25 | c_standard: ?Module.CStandard = null, | |
| 25 | cbe: bool = false, | |
| 26 | 26 | }; |
| 27 | 27 | |
| 28 | 28 | /// Attempts incremental linking, if the file already exists. |
| ... | ... | @@ -38,7 +38,7 @@ pub fn openBinFilePath( |
| 38 | 38 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) }); |
| 39 | 39 | errdefer file.close(); |
| 40 | 40 | |
| 41 | if (options.c_standard) |cstd| { | |
| 41 | if (options.cbe) { | |
| 42 | 42 | var bin_file = try allocator.create(File.C); |
| 43 | 43 | errdefer allocator.destroy(bin_file); |
| 44 | 44 | bin_file.* = try openCFile(allocator, file, options); |
| ... | ... | @@ -217,6 +217,7 @@ pub const File = struct { |
| 217 | 217 | called: std.StringHashMap(void), |
| 218 | 218 | need_stddef: bool = false, |
| 219 | 219 | need_stdint: bool = false, |
| 220 | need_noreturn: bool = false, | |
| 220 | 221 | |
| 221 | 222 | pub fn makeWritable(self: *File.C, dir: fs.Dir, sub_path: []const u8) !void { |
| 222 | 223 | assert(self.owns_file_handle); |
| ... | ... | @@ -239,11 +240,12 @@ pub const File = struct { |
| 239 | 240 | } |
| 240 | 241 | |
| 241 | 242 | pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void { |
| 242 | try cgen.generate(self, decl, self.options.c_standard.?); | |
| 243 | try cgen.generate(self, decl); | |
| 243 | 244 | } |
| 244 | 245 | |
| 245 | 246 | pub fn flush(self: *File.C) !void { |
| 246 | 247 | const writer = self.file.?.writer(); |
| 248 | try writer.writeAll(@embedFile("cbe.h")); | |
| 247 | 249 | var includes = false; |
| 248 | 250 | if (self.need_stddef) { |
| 249 | 251 | try writer.writeAll("#include <stddef.h>\n"); |
src-self-hosted/main.zig+5-15| ... | ... | @@ -191,7 +191,7 @@ fn buildOutputType( |
| 191 | 191 | var emit_zir: Emit = .no; |
| 192 | 192 | var target_arch_os_abi: []const u8 = "native"; |
| 193 | 193 | var target_mcpu: ?[]const u8 = null; |
| 194 | var target_c_standard: ?Module.CStandard = null; | |
| 194 | var cbe: bool = false; | |
| 195 | 195 | var target_dynamic_linker: ?[]const u8 = null; |
| 196 | 196 | |
| 197 | 197 | var system_libs = std.ArrayList([]const u8).init(gpa); |
| ... | ... | @@ -279,18 +279,8 @@ fn buildOutputType( |
| 279 | 279 | } |
| 280 | 280 | i += 1; |
| 281 | 281 | target_mcpu = args[i]; |
| 282 | } else if (mem.eql(u8, arg, "--c-standard")) { | |
| 283 | if (i + 1 >= args.len) { | |
| 284 | std.debug.print("expected parameter after --c-standard\n", .{}); | |
| 285 | process.exit(1); | |
| 286 | } | |
| 287 | i += 1; | |
| 288 | if (std.meta.stringToEnum(Module.CStandard, args[i])) |cstd| { | |
| 289 | target_c_standard = cstd; | |
| 290 | } else { | |
| 291 | std.debug.print("Invalid C standard: {}\n", .{args[i]}); | |
| 292 | process.exit(1); | |
| 293 | } | |
| 282 | } else if (mem.eql(u8, arg, "--c")) { | |
| 283 | cbe = true; | |
| 294 | 284 | } else if (mem.startsWith(u8, arg, "-mcpu=")) { |
| 295 | 285 | target_mcpu = arg["-mcpu=".len..]; |
| 296 | 286 | } else if (mem.eql(u8, arg, "--dynamic-linker")) { |
| ... | ... | @@ -374,7 +364,7 @@ fn buildOutputType( |
| 374 | 364 | } |
| 375 | 365 | } |
| 376 | 366 | |
| 377 | if (target_c_standard != null and output_mode != .Obj) { | |
| 367 | if (cbe and output_mode != .Obj) { | |
| 378 | 368 | std.debug.print("The C backend must be used with build-obj\n", .{}); |
| 379 | 369 | process.exit(1); |
| 380 | 370 | } |
| ... | ... | @@ -479,7 +469,7 @@ fn buildOutputType( |
| 479 | 469 | .object_format = object_format, |
| 480 | 470 | .optimize_mode = build_mode, |
| 481 | 471 | .keep_source_files_loaded = zir_out_path != null, |
| 482 | .c_standard = target_c_standard, | |
| 472 | .cbe = cbe, | |
| 483 | 473 | }); |
| 484 | 474 | defer module.deinit(); |
| 485 | 475 |
src-self-hosted/test.zig+21-21| ... | ... | @@ -5,6 +5,8 @@ const Allocator = std.mem.Allocator; |
| 5 | 5 | const zir = @import("zir.zig"); |
| 6 | 6 | const Package = @import("Package.zig"); |
| 7 | 7 | |
| 8 | const cheader = @embedFile("cbe.h"); | |
| 9 | ||
| 8 | 10 | test "self-hosted" { |
| 9 | 11 | var ctx = TestContext.init(); |
| 10 | 12 | defer ctx.deinit(); |
| ... | ... | @@ -68,7 +70,7 @@ pub const TestContext = struct { |
| 68 | 70 | output_mode: std.builtin.OutputMode, |
| 69 | 71 | updates: std.ArrayList(Update), |
| 70 | 72 | extension: TestType, |
| 71 | c_standard: ?Module.CStandard = null, | |
| 73 | cbe: bool = false, | |
| 72 | 74 | |
| 73 | 75 | /// Adds a subcase in which the module is updated with `src`, and the |
| 74 | 76 | /// resulting ZIR is validated against `result`. |
| ... | ... | @@ -188,20 +190,20 @@ pub const TestContext = struct { |
| 188 | 190 | return ctx.addObj(name, target, .ZIR); |
| 189 | 191 | } |
| 190 | 192 | |
| 191 | pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType, standard: Module.CStandard) *Case { | |
| 193 | pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case { | |
| 192 | 194 | ctx.cases.append(Case{ |
| 193 | 195 | .name = name, |
| 194 | 196 | .target = target, |
| 195 | 197 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 196 | 198 | .output_mode = .Obj, |
| 197 | 199 | .extension = T, |
| 198 | .c_standard = standard, | |
| 200 | .cbe = true, | |
| 199 | 201 | }) catch unreachable; |
| 200 | 202 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 201 | 203 | } |
| 202 | 204 | |
| 203 | pub fn c11(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, c: [:0]const u8) void { | |
| 204 | ctx.addC(name, target, .Zig, .C11).addTransform(src, c); | |
| 205 | pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { | |
| 206 | ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out); | |
| 205 | 207 | } |
| 206 | 208 | |
| 207 | 209 | pub fn addCompareOutput( |
| ... | ... | @@ -382,13 +384,13 @@ pub const TestContext = struct { |
| 382 | 384 | } |
| 383 | 385 | |
| 384 | 386 | fn deinit(self: *TestContext) void { |
| 385 | for (self.cases.items) |c| { | |
| 386 | for (c.updates.items) |u| { | |
| 387 | for (self.cases.items) |case| { | |
| 388 | for (case.updates.items) |u| { | |
| 387 | 389 | if (u.case == .Error) { |
| 388 | c.updates.allocator.free(u.case.Error); | |
| 390 | case.updates.allocator.free(u.case.Error); | |
| 389 | 391 | } |
| 390 | 392 | } |
| 391 | c.updates.deinit(); | |
| 393 | case.updates.deinit(); | |
| 392 | 394 | } |
| 393 | 395 | self.cases.deinit(); |
| 394 | 396 | self.* = undefined; |
| ... | ... | @@ -442,7 +444,7 @@ pub const TestContext = struct { |
| 442 | 444 | .bin_file_path = bin_name, |
| 443 | 445 | .root_pkg = root_pkg, |
| 444 | 446 | .keep_source_files_loaded = true, |
| 445 | .c_standard = case.c_standard, | |
| 447 | .cbe = case.cbe, | |
| 446 | 448 | }); |
| 447 | 449 | defer module.deinit(); |
| 448 | 450 | |
| ... | ... | @@ -477,24 +479,22 @@ pub const TestContext = struct { |
| 477 | 479 | |
| 478 | 480 | switch (update.case) { |
| 479 | 481 | .Transformation => |expected_output| { |
| 480 | var label: []const u8 = "ZIR"; | |
| 481 | if (case.c_standard) |cstd| { | |
| 482 | label = @tagName(cstd); | |
| 483 | var c: *link.File.C = module.bin_file.cast(link.File.C).?; | |
| 484 | c.file.?.close(); | |
| 485 | c.file = null; | |
| 482 | if (case.cbe) { | |
| 483 | var cfile: *link.File.C = module.bin_file.cast(link.File.C).?; | |
| 484 | cfile.file.?.close(); | |
| 485 | cfile.file = null; | |
| 486 | 486 | var file = try tmp.dir.openFile(bin_name, .{ .read = true }); |
| 487 | 487 | defer file.close(); |
| 488 | 488 | var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!"); |
| 489 | 489 | defer allocator.free(out); |
| 490 | 490 | |
| 491 | 491 | if (expected_output.len != out.len) { |
| 492 | std.debug.warn("\nTransformed {} length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ label, expected_output, out }); | |
| 492 | std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); | |
| 493 | 493 | std.process.exit(1); |
| 494 | 494 | } |
| 495 | 495 | for (expected_output) |e, i| { |
| 496 | 496 | if (out[i] != e) { |
| 497 | std.debug.warn("\nTransformed {} differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ label, expected_output, out }); | |
| 497 | std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); | |
| 498 | 498 | std.process.exit(1); |
| 499 | 499 | } |
| 500 | 500 | } |
| ... | ... | @@ -518,12 +518,12 @@ pub const TestContext = struct { |
| 518 | 518 | defer test_node.end(); |
| 519 | 519 | |
| 520 | 520 | if (expected_output.len != out_zir.items.len) { |
| 521 | 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 }); | |
| 521 | 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 }); | |
| 522 | 522 | std.process.exit(1); |
| 523 | 523 | } |
| 524 | 524 | for (expected_output) |e, i| { |
| 525 | 525 | if (out_zir.items[i] != e) { |
| 526 | 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 }); | |
| 526 | 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 }); | |
| 527 | 527 | std.process.exit(1); |
| 528 | 528 | } |
| 529 | 529 | } |
| ... | ... | @@ -561,7 +561,7 @@ pub const TestContext = struct { |
| 561 | 561 | } |
| 562 | 562 | }, |
| 563 | 563 | .Execution => |expected_stdout| { |
| 564 | std.debug.assert(case.c_standard == null); | |
| 564 | std.debug.assert(!case.cbe); | |
| 565 | 565 | |
| 566 | 566 | update_node.estimated_total_items = 4; |
| 567 | 567 | var exec_result = x: { |
test/stage2/cbe.zig+10-10| ... | ... | @@ -9,30 +9,30 @@ const linux_x64 = std.zig.CrossTarget{ |
| 9 | 9 | }; |
| 10 | 10 | |
| 11 | 11 | pub fn addCases(ctx: *TestContext) !void { |
| 12 | ctx.c11("empty start function", linux_x64, | |
| 12 | ctx.c("empty start function", linux_x64, | |
| 13 | 13 | \\export fn _start() noreturn {} |
| 14 | 14 | , |
| 15 | \\_Noreturn void _start(void) {} | |
| 15 | \\noreturn void _start(void) {} | |
| 16 | 16 | \\ |
| 17 | 17 | ); |
| 18 | ctx.c11("less empty start function", linux_x64, | |
| 18 | ctx.c("less empty start function", linux_x64, | |
| 19 | 19 | \\fn main() noreturn {} |
| 20 | 20 | \\ |
| 21 | 21 | \\export fn _start() noreturn { |
| 22 | 22 | \\	main(); |
| 23 | 23 | \\} |
| 24 | 24 | , |
| 25 | \\_Noreturn void main(void); | |
| 25 | \\noreturn void main(void); | |
| 26 | 26 | \\ |
| 27 | \\_Noreturn void _start(void) { | |
| 27 | \\noreturn void _start(void) { | |
| 28 | 28 | \\	main(); |
| 29 | 29 | \\} |
| 30 | 30 | \\ |
| 31 | \\_Noreturn void main(void) {} | |
| 31 | \\noreturn void main(void) {} | |
| 32 | 32 | \\ |
| 33 | 33 | ); |
| 34 | 34 | // TODO: implement return values |
| 35 | ctx.c11("inline asm", linux_x64, | |
| 35 | ctx.c("inline asm", linux_x64, | |
| 36 | 36 | \\fn exitGood() void { |
| 37 | 37 | \\	asm volatile ("syscall" |
| 38 | 38 | \\ 		: |
| ... | ... | @@ -49,7 +49,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 49 | 49 | \\ |
| 50 | 50 | \\void exitGood(void); |
| 51 | 51 | \\ |
| 52 | \\_Noreturn void _start(void) { | |
| 52 | \\noreturn void _start(void) { | |
| 53 | 53 | \\	exitGood(); |
| 54 | 54 | \\} |
| 55 | 55 | \\ |
| ... | ... | @@ -60,7 +60,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 60 | 60 | \\} |
| 61 | 61 | \\ |
| 62 | 62 | ); |
| 63 | //ctx.c11("basic return", linux_x64, | |
| 63 | //ctx.c("basic return", linux_x64, | |
| 64 | 64 | // \\fn main() u8 { |
| 65 | 65 | // \\	return 103; |
| 66 | 66 | // \\} |
| ... | ... | @@ -73,7 +73,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 73 | 73 | // \\ |
| 74 | 74 | // \\uint8_t main(void); |
| 75 | 75 | // \\ |
| 76 | // \\_Noreturn void _start(void) { | |
| 76 | // \\noreturn void _start(void) { | |
| 77 | 77 | // \\	(void)main(); |
| 78 | 78 | // \\} |
| 79 | 79 | // \\ |