mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
CBE: Move standards determination to generated code
This commit is contained in:
parent
5461c482d0
commit
b91cf15972
@ -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();
|
||||
|
||||
|
||||
8
src-self-hosted/cbe.h
Normal file
8
src-self-hosted/cbe.h
Normal file
@ -0,0 +1,8 @@
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define noreturn _Noreturn
|
||||
#elif !__STRICT_ANSI__
|
||||
#define noreturn __attribute__ ((noreturn))
|
||||
#else
|
||||
#define noreturn
|
||||
#endif
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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();
|
||||
// \\}
|
||||
// \\
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user