Stage2: Refactor in preparation for C backend

This commit is contained in:
Noam Preil 2020-07-07 14:55:44 -04:00
parent 0db0258fb2
commit b4c571301b
No known key found for this signature in database
GPG Key ID: FC347E7C85BE8238
6 changed files with 1356 additions and 1219 deletions

View File

@ -26,7 +26,7 @@ root_pkg: *Package,
/// Module owns this resource.
/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
root_scope: *Scope,
bin_file: link.ElfFile,
bin_file: *link.File,
bin_file_dir: std.fs.Dir,
bin_file_path: []const u8,
/// It's rare for a decl to be exported, so we save memory by having a sparse map of
@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export),
decl_table: DeclTable,
optimize_mode: std.builtin.Mode,
link_error_flags: link.ElfFile.ErrorFlags = .{},
link_error_flags: link.File.ErrorFlags = .{},
work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
@ -91,7 +91,7 @@ pub const Export = struct {
/// Byte offset into the file that contains the export directive.
src: usize,
/// Represents the position of the export, if any, in the output file.
link: link.ElfFile.Export,
link: link.File.Elf.Export,
/// The Decl that performs the export. Note that this is *not* the Decl being exported.
owner_decl: *Decl,
/// The Decl being exported. Note this is *not* the Decl performing the export.
@ -169,7 +169,7 @@ pub const Decl = struct {
/// Represents the position of the code in the output file.
/// This is populated regardless of semantic analysis and code generation.
link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
contents_hash: std.zig.SrcHash,
@ -722,6 +722,12 @@ pub const AllErrors = struct {
}
};
pub const CStandard = enum {
C99,
GNU99,
C11,
};
pub const InitOptions = struct {
target: std.Target,
root_pkg: *Package,
@ -732,17 +738,19 @@ 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,
};
pub fn init(gpa: *Allocator, options: InitOptions) !Module {
const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
.target = options.target,
.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,
});
errdefer bin_file.deinit();
errdefer bin_file.*.deinit();
const root_scope = blk: {
if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
@ -793,6 +801,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
pub fn deinit(self: *Module) void {
self.bin_file.deinit();
const allocator = self.allocator;
allocator.destroy(self.bin_file);
self.deletion_set.deinit(allocator);
self.work_queue.deinit();
@ -840,7 +849,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
}
pub fn target(self: Module) std.Target {
return self.bin_file.options.target;
return self.bin_file.options().target;
}
/// Detect changes to source files, perform semantic analysis, and update the output files.
@ -882,7 +891,7 @@ pub fn update(self: *Module) !void {
try self.deleteDecl(decl);
}
self.link_error_flags = self.bin_file.error_flags;
self.link_error_flags = self.bin_file.errorFlags();
// If there are any errors, we anticipate the source files being loaded
// to report error messages. Otherwise we unload all source files to save memory.
@ -1898,8 +1907,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
self.decl_exports.removeAssertDiscard(exp.exported_decl);
}
}
self.bin_file.deleteExport(exp.link);
if (self.bin_file.cast(link.File.Elf)) |elf| {
elf.deleteExport(exp.link);
}
if (self.failed_exports.remove(exp)) |entry| {
entry.value.destroy(self.allocator);
}
@ -1961,7 +1971,7 @@ fn allocateNewDecl(
.analysis = .unreferenced,
.deletion_flag = false,
.contents_hash = contents_hash,
.link = link.ElfFile.TextBlock.empty,
.link = link.File.Elf.TextBlock.empty,
.generation = 0,
};
return new_decl;
@ -2189,19 +2199,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
}
try self.symbol_exports.putNoClobber(symbol_name, new_export);
self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
self.allocator,
src,
"unable to export: {}",
.{@errorName(err)},
));
new_export.status = .failed_retryable;
},
};
if (self.bin_file.cast(link.File.Elf)) |elf| {
elf.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
self.allocator,
src,
"unable to export: {}",
.{@errorName(err)},
));
new_export.status = .failed_retryable;
},
};
}
}
fn addNewInstArgs(

View File

@ -21,7 +21,7 @@ pub const Result = union(enum) {
};
pub fn generateSymbol(
bin_file: *link.ElfFile,
bin_file: *link.File.Elf,
src: usize,
typed_value: TypedValue,
code: *std.ArrayList(u8),
@ -211,7 +211,7 @@ pub fn generateSymbol(
}
const Function = struct {
bin_file: *link.ElfFile,
bin_file: *link.File.Elf,
target: *const std.Target,
mod_fn: *const Module.Fn,
code: *std.ArrayList(u8),

File diff suppressed because it is too large Load Diff

View File

@ -64,10 +64,11 @@ pub const TestContext = struct {
/// such as QEMU is required for tests to complete.
target: std.zig.CrossTarget,
/// In order to be able to run e.g. Execution updates, this must be set
/// to Executable.
/// to Executable. This is ignored when generating C output.
output_mode: std.builtin.OutputMode,
updates: std.ArrayList(Update),
extension: TestType,
c_standard: ?Module.CStandard = null,
/// Adds a subcase in which the module is updated with `src`, and the
/// resulting ZIR is validated against `result`.
@ -187,6 +188,22 @@ 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 {
ctx.cases.append(Case{
.name = name,
.target = target,
.updates = std.ArrayList(Update).init(ctx.cases.allocator),
.output_mode = .Obj,
.extension = T,
.c_standard = standard,
}) 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 addCompareOutput(
ctx: *TestContext,
name: []const u8,
@ -425,6 +442,7 @@ pub const TestContext = struct {
.bin_file_path = bin_name,
.root_pkg = root_pkg,
.keep_source_files_loaded = true,
.c_standard = case.c_standard,
});
defer module.deinit();
@ -463,14 +481,15 @@ pub const TestContext = struct {
var test_node = update_node.start("assert", null);
test_node.activate();
defer test_node.end();
const label = if (case.c_standard) |_| "C" else "ZIR";
if (expected_output.len != out_zir.items.len) {
std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
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.process.exit(1);
}
for (expected_output) |e, i| {
if (out_zir.items[i] != e) {
if (expected_output.len != out_zir.items.len) {
std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
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.process.exit(1);
}
}

18
test/stage2/cbe.zig Normal file
View File

@ -0,0 +1,18 @@
const std = @import("std");
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
// These tests should work with all platforms, but we're using linux_x64 for
// now for consistency. Will be expanded eventually.
const linux_x64 = std.zig.CrossTarget{
.cpu_arch = .x86_64,
.os_tag = .linux,
};
pub fn addCases(ctx: *TestContext) !void {
// // These tests should work on every platform
// ctx.c11("empty start function", linux_x64,
// \\export fn start() void {}
// ,
// \\void start(void) {}
// );
}

View File

@ -4,4 +4,5 @@ pub fn addCases(ctx: *TestContext) !void {
try @import("compile_errors.zig").addCases(ctx);
try @import("compare_output.zig").addCases(ctx);
try @import("zir.zig").addCases(ctx);
try @import("cbe.zig").addCases(ctx);
}