mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 00:08:56 +00:00
stage2: add support for start.zig
This adds a simplified start2.zig that the current stage2 compiler is able to generate code for.
This commit is contained in:
parent
e85cd616ef
commit
ac14b52e85
58
lib/std/start2.zig
Normal file
58
lib/std/start2.zig
Normal file
@ -0,0 +1,58 @@
|
||||
const root = @import("root");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
comptime {
|
||||
if (builtin.output_mode == 0) { // OutputMode.Exe
|
||||
if (builtin.link_libc or builtin.object_format == 5) { // ObjectFormat.c
|
||||
if (!@hasDecl(root, "main")) {
|
||||
@export(otherMain, "main");
|
||||
}
|
||||
} else {
|
||||
if (!@hasDecl(root, "_start")) {
|
||||
@export(otherStart, "_start");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Cannot call this function `main`, because `fully qualified names`
|
||||
// have not been implemented yet.
|
||||
fn otherMain() callconv(.C) c_int {
|
||||
root.zigMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME: Cannot call this function `_start`, because `fully qualified names`
|
||||
// have not been implemented yet.
|
||||
fn otherStart() callconv(.Naked) noreturn {
|
||||
root.zigMain();
|
||||
otherExit();
|
||||
}
|
||||
|
||||
// FIXME: Cannot call this function `exit`, because `fully qualified names`
|
||||
// have not been implemented yet.
|
||||
fn otherExit() noreturn {
|
||||
if (builtin.arch == 31) { // x86_64
|
||||
asm volatile ("syscall"
|
||||
:
|
||||
: [number] "{rax}" (231),
|
||||
[arg1] "{rdi}" (0)
|
||||
: "rcx", "r11", "memory"
|
||||
);
|
||||
} else if (builtin.arch == 0) { // arm
|
||||
asm volatile ("svc #0"
|
||||
:
|
||||
: [number] "{r7}" (1),
|
||||
[arg1] "{r0}" (0)
|
||||
: "memory"
|
||||
);
|
||||
} else if (builtin.arch == 2) { // aarch64
|
||||
asm volatile ("svc #0"
|
||||
:
|
||||
: [number] "{x8}" (93),
|
||||
[arg1] "{x0}" (0)
|
||||
: "memory", "cc"
|
||||
);
|
||||
} else @compileError("not yet supported!");
|
||||
unreachable;
|
||||
}
|
||||
@ -908,41 +908,45 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
};
|
||||
|
||||
const builtin_pkg = try Package.create(gpa, zig_cache_artifact_directory.path.?, "builtin2.zig");
|
||||
|
||||
const std_dir_path = try options.zig_lib_directory.join(gpa, &[_][]const u8{"std"});
|
||||
defer gpa.free(std_dir_path);
|
||||
const start_pkg = try Package.create(gpa, std_dir_path, "start2.zig");
|
||||
|
||||
try root_pkg.add(gpa, "builtin", builtin_pkg);
|
||||
try root_pkg.add(gpa, "root", root_pkg);
|
||||
|
||||
try start_pkg.add(gpa, "builtin", builtin_pkg);
|
||||
try start_pkg.add(gpa, "root", root_pkg);
|
||||
|
||||
// TODO when we implement serialization and deserialization of incremental compilation metadata,
|
||||
// this is where we would load it. We have open a handle to the directory where
|
||||
// the output either already is, or will be.
|
||||
// However we currently do not have serialization of such metadata, so for now
|
||||
// we set up an empty Module that does the entire compilation fresh.
|
||||
|
||||
const root_scope = rs: {
|
||||
if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
|
||||
const root_scope = try gpa.create(Module.Scope.File);
|
||||
const struct_ty = try Type.Tag.empty_struct.create(
|
||||
gpa,
|
||||
&root_scope.root_container,
|
||||
);
|
||||
root_scope.* = .{
|
||||
// TODO this is duped so it can be freed in Container.deinit
|
||||
.sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
|
||||
.source = .{ .unloaded = {} },
|
||||
.tree = undefined,
|
||||
.status = .never_loaded,
|
||||
.pkg = root_pkg,
|
||||
.root_container = .{
|
||||
.file_scope = root_scope,
|
||||
.decls = .{},
|
||||
.ty = struct_ty,
|
||||
},
|
||||
};
|
||||
break :rs root_scope;
|
||||
} else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
|
||||
return error.ZirFilesUnsupported;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) return error.ZirFilesUnsupported;
|
||||
|
||||
const start_scope = ss: {
|
||||
const start_scope = try gpa.create(Module.Scope.File);
|
||||
const struct_ty = try Type.Tag.empty_struct.create(
|
||||
gpa,
|
||||
&start_scope.root_container,
|
||||
);
|
||||
start_scope.* = .{
|
||||
// TODO this is duped so it can be freed in Container.deinit
|
||||
.sub_file_path = try gpa.dupe(u8, start_pkg.root_src_path),
|
||||
.source = .{ .unloaded = {} },
|
||||
.tree = undefined,
|
||||
.status = .never_loaded,
|
||||
.pkg = start_pkg,
|
||||
.root_container = .{
|
||||
.file_scope = start_scope,
|
||||
.decls = .{},
|
||||
.ty = struct_ty,
|
||||
},
|
||||
};
|
||||
break :ss start_scope;
|
||||
};
|
||||
|
||||
const module = try arena.create(Module);
|
||||
@ -951,7 +955,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.gpa = gpa,
|
||||
.comp = comp,
|
||||
.root_pkg = root_pkg,
|
||||
.root_scope = root_scope,
|
||||
.root_scope = null,
|
||||
.start_pkg = start_pkg,
|
||||
.start_scope = start_scope,
|
||||
.zig_cache_artifact_directory = zig_cache_artifact_directory,
|
||||
.emit_h = options.emit_h,
|
||||
.error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
|
||||
@ -1353,9 +1359,9 @@ pub fn update(self: *Compilation) !void {
|
||||
// TODO Detect which source files changed.
|
||||
// Until then we simulate a full cache miss. Source files could have been loaded
|
||||
// for any reason; to force a refresh we unload now.
|
||||
module.unloadFile(module.root_scope);
|
||||
module.unloadFile(module.start_scope);
|
||||
module.failed_root_src_file = null;
|
||||
module.analyzeContainer(&module.root_scope.root_container) catch |err| switch (err) {
|
||||
module.analyzeContainer(&module.start_scope.root_container) catch |err| switch (err) {
|
||||
error.AnalysisFail => {
|
||||
assert(self.totalErrorCount() != 0);
|
||||
},
|
||||
@ -1416,7 +1422,7 @@ pub fn update(self: *Compilation) !void {
|
||||
// to report error messages. Otherwise we unload all source files to save memory.
|
||||
if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
|
||||
if (self.bin_file.options.module) |module| {
|
||||
module.root_scope.unload(self.gpa);
|
||||
module.start_scope.unload(self.gpa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2851,11 +2857,15 @@ fn generateBuiltin2ZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 {
|
||||
\\pub const link_libc = {};
|
||||
\\pub const arch = {};
|
||||
\\pub const os = {};
|
||||
\\pub const output_mode = {};
|
||||
\\pub const object_format = {};
|
||||
\\
|
||||
, .{
|
||||
comp.bin_file.options.link_libc,
|
||||
@enumToInt(target.cpu.arch),
|
||||
@enumToInt(target.os.tag),
|
||||
@enumToInt(comp.bin_file.options.output_mode),
|
||||
@enumToInt(comp.bin_file.options.object_format),
|
||||
});
|
||||
|
||||
return buffer.toOwnedSlice();
|
||||
|
||||
@ -35,8 +35,11 @@ comp: *Compilation,
|
||||
zig_cache_artifact_directory: Compilation.Directory,
|
||||
/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
|
||||
root_pkg: *Package,
|
||||
/// This is populated when `@import("root")` is analysed.
|
||||
root_scope: ?*Scope.File,
|
||||
start_pkg: *Package,
|
||||
/// Module owns this resource.
|
||||
root_scope: *Scope.File,
|
||||
start_scope: *Scope.File,
|
||||
/// It's rare for a decl to be exported, so we save memory by having a sparse map of
|
||||
/// Decl pointers to details about them being exported.
|
||||
/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
|
||||
@ -2341,7 +2344,9 @@ pub fn deinit(mod: *Module) void {
|
||||
mod.export_owners.deinit(gpa);
|
||||
|
||||
mod.symbol_exports.deinit(gpa);
|
||||
mod.root_scope.destroy(gpa);
|
||||
|
||||
mod.start_scope.destroy(gpa);
|
||||
mod.start_pkg.destroy(gpa);
|
||||
|
||||
var it = mod.global_error_set.iterator();
|
||||
while (it.next()) |entry| {
|
||||
|
||||
@ -15,6 +15,9 @@ root_src_path: []const u8,
|
||||
table: Table = .{},
|
||||
parent: ?*Package = null,
|
||||
|
||||
// Used when freeing packages
|
||||
seen: bool = false,
|
||||
|
||||
/// Allocate a Package. No references to the slices passed are kept.
|
||||
pub fn create(
|
||||
gpa: *Allocator,
|
||||
@ -55,6 +58,14 @@ pub fn destroy(pkg: *Package, gpa: *Allocator) void {
|
||||
pkg.root_src_directory.handle.close();
|
||||
}
|
||||
|
||||
// First we recurse into all the packages and remove packages from the tables
|
||||
// once we have seen it before. We do this to make sure that that
|
||||
// a package can only be found once in the whole tree.
|
||||
if (!pkg.seen) {
|
||||
pkg.seen = true;
|
||||
pkg.markSeen(gpa);
|
||||
}
|
||||
|
||||
{
|
||||
var it = pkg.table.iterator();
|
||||
while (it.next()) |kv| {
|
||||
@ -69,6 +80,20 @@ pub fn destroy(pkg: *Package, gpa: *Allocator) void {
|
||||
gpa.destroy(pkg);
|
||||
}
|
||||
|
||||
fn markSeen(pkg: *Package, gpa: *Allocator) void {
|
||||
var it = pkg.table.iterator();
|
||||
while (it.next()) |kv| {
|
||||
if (pkg != kv.value) {
|
||||
if (kv.value.seen) {
|
||||
pkg.table.removeAssertDiscard(kv.key);
|
||||
} else {
|
||||
kv.value.seen = true;
|
||||
kv.value.markSeen(gpa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void {
|
||||
try pkg.table.ensureCapacity(gpa, pkg.table.count() + 1);
|
||||
const name_dupe = try mem.dupe(gpa, u8, name);
|
||||
|
||||
@ -4699,7 +4699,7 @@ fn namedFieldPtr(
|
||||
}
|
||||
|
||||
// TODO this will give false positives for structs inside the root file
|
||||
if (container_scope.file_scope == mod.root_scope) {
|
||||
if (container_scope.file_scope == mod.root_scope.?) {
|
||||
return mod.fail(
|
||||
&block.base,
|
||||
src,
|
||||
@ -5338,6 +5338,9 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin
|
||||
.ty = struct_ty,
|
||||
},
|
||||
};
|
||||
if (mem.eql(u8, target_string, "root")) {
|
||||
sema.mod.root_scope = file_scope;
|
||||
}
|
||||
sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
|
||||
error.AnalysisFail => {
|
||||
assert(sema.mod.comp.totalErrorCount() != 0);
|
||||
|
||||
@ -1732,6 +1732,8 @@ fn buildOutputType(
|
||||
},
|
||||
}
|
||||
|
||||
// This gets cleaned up, because root_pkg becomes part of the
|
||||
// package table of the start_pkg.
|
||||
const root_pkg: ?*Package = if (root_src_file) |src_path| blk: {
|
||||
if (main_pkg_path) |p| {
|
||||
const rel_src_path = try fs.path.relative(gpa, p, src_path);
|
||||
@ -1741,7 +1743,6 @@ fn buildOutputType(
|
||||
break :blk try Package.create(gpa, fs.path.dirname(src_path), fs.path.basename(src_path));
|
||||
}
|
||||
} else null;
|
||||
defer if (root_pkg) |p| p.destroy(gpa);
|
||||
|
||||
// Transfer packages added with --pkg-begin/--pkg-end to the root package
|
||||
if (root_pkg) |pkg| {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user