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