Commit 0df485d4dc
Changed files (6)
src-self-hosted
src-self-hosted/codegen.zig
@@ -19,8 +19,8 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
errdefer output_path.deinit();
- const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
- defer llvm_handle.release(comp.event_loop_local);
+ const llvm_handle = try comp.zig_compiler.getAnyLlvmContext();
+ defer llvm_handle.release(comp.zig_compiler);
const context = llvm_handle.node.data;
src-self-hosted/compilation.zig
@@ -35,7 +35,7 @@ const fs = event.fs;
const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
/// Data that is local to the event loop.
-pub const EventLoopLocal = struct {
+pub const ZigCompiler = struct {
loop: *event.Loop,
llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
lld_lock: event.Lock,
@@ -47,7 +47,7 @@ pub const EventLoopLocal = struct {
var lazy_init_targets = std.lazyInit(void);
- fn init(loop: *event.Loop) !EventLoopLocal {
+ fn init(loop: *event.Loop) !ZigCompiler {
lazy_init_targets.get() orelse {
Target.initializeAll();
lazy_init_targets.resolve();
@@ -57,7 +57,7 @@ pub const EventLoopLocal = struct {
try std.os.getRandomBytes(seed_bytes[0..]);
const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
- return EventLoopLocal{
+ return ZigCompiler{
.loop = loop,
.lld_lock = event.Lock.init(loop),
.llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
@@ -67,7 +67,7 @@ pub const EventLoopLocal = struct {
}
/// Must be called only after EventLoop.run completes.
- fn deinit(self: *EventLoopLocal) void {
+ fn deinit(self: *ZigCompiler) void {
self.lld_lock.deinit();
while (self.llvm_handle_pool.pop()) |node| {
c.LLVMContextDispose(node.data);
@@ -77,7 +77,7 @@ pub const EventLoopLocal = struct {
/// Gets an exclusive handle on any LlvmContext.
/// Caller must release the handle when done.
- pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {
+ pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
@@ -92,24 +92,36 @@ pub const EventLoopLocal = struct {
return LlvmHandle{ .node = node };
}
- pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
+ pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
self.native_libc.resolve();
return &self.native_libc.data;
}
+
+ /// Must be called only once, ever. Sets global state.
+ pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void {
+ if (llvm_argv.len != 0) {
+ var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, [][]const []const u8{
+ [][]const u8{"zig (LLVM option parsing)"},
+ llvm_argv,
+ });
+ defer c_compatible_args.deinit();
+ c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr);
+ }
+ }
};
pub const LlvmHandle = struct {
node: *std.atomic.Stack(llvm.ContextRef).Node,
- pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {
- event_loop_local.llvm_handle_pool.push(self.node);
+ pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
+ zig_compiler.llvm_handle_pool.push(self.node);
}
};
pub const Compilation = struct {
- event_loop_local: *EventLoopLocal,
+ zig_compiler: *ZigCompiler,
loop: *event.Loop,
name: Buffer,
llvm_triple: Buffer,
@@ -137,7 +149,6 @@ pub const Compilation = struct {
linker_rdynamic: bool,
clang_argv: []const []const u8,
- llvm_argv: []const []const u8,
lib_dirs: []const []const u8,
rpath_list: []const []const u8,
assembly_files: []const []const u8,
@@ -217,6 +228,8 @@ pub const Compilation = struct {
deinit_group: event.Group(void),
destroy_handle: promise,
+ main_loop_handle: promise,
+ main_loop_future: event.Future(void),
have_err_ret_tracing: bool,
@@ -325,7 +338,7 @@ pub const Compilation = struct {
};
pub fn create(
- event_loop_local: *EventLoopLocal,
+ zig_compiler: *ZigCompiler,
name: []const u8,
root_src_path: ?[]const u8,
target: Target,
@@ -334,12 +347,45 @@ pub const Compilation = struct {
is_static: bool,
zig_lib_dir: []const u8,
) !*Compilation {
- const loop = event_loop_local.loop;
- const comp = try event_loop_local.loop.allocator.createOne(Compilation);
- comp.* = Compilation{
+ var optional_comp: ?*Compilation = null;
+ const handle = try async<zig_compiler.loop.allocator> createAsync(
+ &optional_comp,
+ zig_compiler,
+ name,
+ root_src_path,
+ target,
+ kind,
+ build_mode,
+ is_static,
+ zig_lib_dir,
+ );
+ return optional_comp orelse if (getAwaitResult(
+ zig_compiler.loop.allocator,
+ handle,
+ )) |_| unreachable else |err| err;
+ }
+
+ async fn createAsync(
+ out_comp: *?*Compilation,
+ zig_compiler: *ZigCompiler,
+ name: []const u8,
+ root_src_path: ?[]const u8,
+ target: Target,
+ kind: Kind,
+ build_mode: builtin.Mode,
+ is_static: bool,
+ zig_lib_dir: []const u8,
+ ) !void {
+ // workaround for https://github.com/ziglang/zig/issues/1194
+ suspend {
+ resume @handle();
+ }
+
+ const loop = zig_compiler.loop;
+ var comp = Compilation{
.loop = loop,
.arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
- .event_loop_local = event_loop_local,
+ .zig_compiler = zig_compiler,
.events = undefined,
.root_src_path = root_src_path,
.target = target,
@@ -349,6 +395,9 @@ pub const Compilation = struct {
.zig_lib_dir = zig_lib_dir,
.zig_std_dir = undefined,
.tmp_dir = event.Future(BuildError![]u8).init(loop),
+ .destroy_handle = @handle(),
+ .main_loop_handle = undefined,
+ .main_loop_future = event.Future(void).init(loop),
.name = undefined,
.llvm_triple = undefined,
@@ -373,7 +422,6 @@ pub const Compilation = struct {
.is_static = is_static,
.linker_rdynamic = false,
.clang_argv = [][]const u8{},
- .llvm_argv = [][]const u8{},
.lib_dirs = [][]const u8{},
.rpath_list = [][]const u8{},
.assembly_files = [][]const u8{},
@@ -381,7 +429,7 @@ pub const Compilation = struct {
.fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
.windows_subsystem_windows = false,
.windows_subsystem_console = false,
- .link_libs_list = ArrayList(*LinkLib).init(comp.arena()),
+ .link_libs_list = undefined,
.libc_link_lib = null,
.err_color = errmsg.Color.Auto,
.darwin_frameworks = [][]const u8{},
@@ -420,19 +468,20 @@ pub const Compilation = struct {
.std_package = undefined,
.override_libc = null,
- .destroy_handle = undefined,
.have_err_ret_tracing = false,
- .primitive_type_table = TypeTable.init(comp.arena()),
+ .primitive_type_table = undefined,
.fs_watch = undefined,
};
- errdefer {
+ comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
+ comp.primitive_type_table = TypeTable.init(comp.arena());
+
+ defer {
comp.int_type_table.private_data.deinit();
comp.array_type_table.private_data.deinit();
comp.ptr_type_table.private_data.deinit();
comp.fn_type_table.private_data.deinit();
comp.arena_allocator.deinit();
- comp.loop.allocator.destroy(comp);
}
comp.name = try Buffer.init(comp.arena(), name);
@@ -452,8 +501,8 @@ pub const Compilation = struct {
// As a workaround we do not use target native features on Windows.
var target_specific_cpu_args: ?[*]u8 = null;
var target_specific_cpu_features: ?[*]u8 = null;
- errdefer llvm.DisposeMessage(target_specific_cpu_args);
- errdefer llvm.DisposeMessage(target_specific_cpu_features);
+ defer llvm.DisposeMessage(target_specific_cpu_args);
+ defer llvm.DisposeMessage(target_specific_cpu_features);
if (target == Target.Native and !target.isWindows()) {
target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
@@ -468,16 +517,16 @@ pub const Compilation = struct {
reloc_mode,
llvm.CodeModelDefault,
) orelse return error.OutOfMemory;
- errdefer llvm.DisposeTargetMachine(comp.target_machine);
+ defer llvm.DisposeTargetMachine(comp.target_machine);
comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
- errdefer llvm.DisposeTargetData(comp.target_data_ref);
+ defer llvm.DisposeTargetData(comp.target_data_ref);
comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
- errdefer llvm.DisposeMessage(comp.target_layout_str);
+ defer llvm.DisposeMessage(comp.target_layout_str);
comp.events = try event.Channel(Event).create(comp.loop, 0);
- errdefer comp.events.destroy();
+ defer comp.events.destroy();
if (root_src_path) |root_src| {
const dirname = std.os.path.dirname(root_src) orelse ".";
@@ -491,13 +540,25 @@ pub const Compilation = struct {
}
comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
- errdefer comp.fs_watch.destroy();
+ defer comp.fs_watch.destroy();
try comp.initTypes();
+ defer comp.primitive_type_table.deinit();
+
+ // Set this to indicate that initialization completed successfully.
+ // from here on out we must not return an error.
+ // This must occur before the first suspend/await.
+ comp.main_loop_handle = async comp.mainLoop() catch unreachable;
+ out_comp.* = ∁
+ suspend;
- comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
+ // From here on is cleanup.
+ await (async comp.deinit_group.wait() catch unreachable);
- return comp;
+ if (comp.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
+ // TODO evented I/O?
+ os.deleteTree(comp.arena(), tmp_dir) catch {};
+ } else |_| {};
}
/// it does ref the result because it could be an arbitrary integer size
@@ -683,49 +744,19 @@ pub const Compilation = struct {
assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
}
- /// This function can safely use async/await, because it manages Compilation's lifetime,
- /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
- async fn internalDeinit(self: *Compilation) void {
- suspend;
-
- await (async self.deinit_group.wait() catch unreachable);
- if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
- // TODO evented I/O?
- os.deleteTree(self.arena(), tmp_dir) catch {};
- } else |_| {};
-
- self.fs_watch.destroy();
- self.events.destroy();
-
- llvm.DisposeMessage(self.target_layout_str);
- llvm.DisposeTargetData(self.target_data_ref);
- llvm.DisposeTargetMachine(self.target_machine);
-
- self.primitive_type_table.deinit();
-
- self.arena_allocator.deinit();
- self.gpa().destroy(self);
- }
-
pub fn destroy(self: *Compilation) void {
+ cancel self.main_loop_handle;
resume self.destroy_handle;
}
- pub fn build(self: *Compilation) !void {
- if (self.llvm_argv.len != 0) {
- var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
- [][]const u8{"zig (LLVM option parsing)"},
- self.llvm_argv,
- });
- defer c_compatible_args.deinit();
- // TODO this sets global state
- c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
- }
-
- _ = try async<self.gpa()> self.buildAsync();
+ fn start(self: *Compilation) void {
+ self.main_loop_future.resolve();
}
- async fn buildAsync(self: *Compilation) void {
+ async fn mainLoop(self: *Compilation) void {
+ // wait until start() is called
+ _ = await (async self.main_loop_future.get() catch unreachable);
+
var build_result = await (async self.initialCompile() catch unreachable);
while (true) {
@@ -1131,7 +1162,7 @@ pub const Compilation = struct {
async fn startFindingNativeLibC(self: *Compilation) void {
await (async self.loop.yield() catch unreachable);
// we don't care if it fails, we're just trying to kick off the future resolution
- _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
+ _ = (await (async self.zig_compiler.getNativeLibC() catch unreachable)) catch return;
}
/// General Purpose Allocator. Must free when done.
@@ -1189,7 +1220,7 @@ pub const Compilation = struct {
var rand_bytes: [9]u8 = undefined;
{
- const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
+ const held = await (async self.zig_compiler.prng.acquire() catch unreachable);
defer held.release();
held.value.random.bytes(rand_bytes[0..]);
@@ -1424,3 +1455,14 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
symbol_name_consumed = true;
}
+
+// TODO these are hacks which should probably be solved by the language
+fn getAwaitResult(allocator: *Allocator, handle: var) @typeInfo(@typeOf(handle)).Promise.child.? {
+ var result: ?@typeInfo(@typeOf(handle)).Promise.child.? = null;
+ cancel (async<allocator> getAwaitResultAsync(handle, &result) catch unreachable);
+ return result.?;
+}
+
+async fn getAwaitResultAsync(handle: var, out: *?@typeInfo(@typeOf(handle)).Promise.child.?) void {
+ out.* = await handle;
+}
src-self-hosted/link.zig
@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {
ctx.libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
- break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
+ break :blk (await (async comp.zig_compiler.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
},
else => return error.LibCRequiredButNotProvidedOrFound,
}
@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {
{
// LLD is not thread-safe, so we grab a global lock.
- const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable);
+ const held = await (async comp.zig_compiler.lld_lock.acquire() catch unreachable);
defer held.release();
// Not evented I/O. LLD does its own multithreading internally.
src-self-hosted/main.zig
@@ -14,7 +14,7 @@ const c = @import("c.zig");
const introspect = @import("introspect.zig");
const Args = arg.Args;
const Flag = arg.Flag;
-const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
+const ZigCompiler = @import("compilation.zig").ZigCompiler;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const errmsg = @import("errmsg.zig");
@@ -373,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
os.exit(1);
}
+ var clang_argv_buf = ArrayList([]const u8).init(allocator);
+ defer clang_argv_buf.deinit();
+
+ const mllvm_flags = flags.many("mllvm");
+ for (mllvm_flags) |mllvm| {
+ try clang_argv_buf.append("-mllvm");
+ try clang_argv_buf.append(mllvm);
+ }
+ try ZigCompiler.setLlvmArgv(allocator, mllvm_flags);
+
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir);
@@ -382,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
try loop.initMultiThreaded(allocator);
defer loop.deinit();
- var event_loop_local = try EventLoopLocal.init(&loop);
- defer event_loop_local.deinit();
+ var zig_compiler = try ZigCompiler.init(&loop);
+ defer zig_compiler.deinit();
var comp = try Compilation.create(
- &event_loop_local,
+ &zig_compiler,
root_name,
root_source_file,
Target.Native,
@@ -415,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.linker_script = flags.single("linker-script");
comp.each_lib_rpath = flags.present("each-lib-rpath");
- var clang_argv_buf = ArrayList([]const u8).init(allocator);
- defer clang_argv_buf.deinit();
-
- const mllvm_flags = flags.many("mllvm");
- for (mllvm_flags) |mllvm| {
- try clang_argv_buf.append("-mllvm");
- try clang_argv_buf.append(mllvm);
- }
-
- comp.llvm_argv = mllvm_flags;
comp.clang_argv = clang_argv_buf.toSliceConst();
comp.strip = flags.present("strip");
@@ -467,7 +467,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.link_out_file = flags.single("output");
comp.link_objects = link_objects;
- try comp.build();
+ comp.start();
const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
defer cancel process_build_events_handle;
loop.run();
@@ -572,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
try loop.initMultiThreaded(allocator);
defer loop.deinit();
- var event_loop_local = try EventLoopLocal.init(&loop);
- defer event_loop_local.deinit();
+ var zig_compiler = try ZigCompiler.init(&loop);
+ defer zig_compiler.deinit();
- const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
+ const handle = try async<loop.allocator> findLibCAsync(&zig_compiler);
defer cancel handle;
loop.run();
}
-async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
- const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
+async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
+ const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| {
stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
os.exit(1);
};
src-self-hosted/test.zig
@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;
const introspect = @import("introspect.zig");
const assertOrPanic = std.debug.assertOrPanic;
const errmsg = @import("errmsg.zig");
-const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
+const ZigCompiler = @import("compilation.zig").ZigCompiler;
var ctx: TestContext = undefined;
@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;
pub const TestContext = struct {
loop: std.event.Loop,
- event_loop_local: EventLoopLocal,
+ zig_compiler: ZigCompiler,
zig_lib_dir: []u8,
file_index: std.atomic.Int(usize),
group: std.event.Group(error!void),
@@ -37,7 +37,7 @@ pub const TestContext = struct {
self.* = TestContext{
.any_err = {},
.loop = undefined,
- .event_loop_local = undefined,
+ .zig_compiler = undefined,
.zig_lib_dir = undefined,
.group = undefined,
.file_index = std.atomic.Int(usize).init(0),
@@ -46,8 +46,8 @@ pub const TestContext = struct {
try self.loop.initMultiThreaded(allocator);
errdefer self.loop.deinit();
- self.event_loop_local = try EventLoopLocal.init(&self.loop);
- errdefer self.event_loop_local.deinit();
+ self.zig_compiler = try ZigCompiler.init(&self.loop);
+ errdefer self.zig_compiler.deinit();
self.group = std.event.Group(error!void).init(&self.loop);
errdefer self.group.deinit();
@@ -62,7 +62,7 @@ pub const TestContext = struct {
fn deinit(self: *TestContext) void {
std.os.deleteTree(allocator, tmp_dir_name) catch {};
allocator.free(self.zig_lib_dir);
- self.event_loop_local.deinit();
+ self.zig_compiler.deinit();
self.loop.deinit();
}
@@ -97,7 +97,7 @@ pub const TestContext = struct {
try std.io.writeFile(allocator, file1_path, source);
var comp = try Compilation.create(
- &self.event_loop_local,
+ &self.zig_compiler,
"test",
file1_path,
Target.Native,
@@ -108,7 +108,7 @@ pub const TestContext = struct {
);
errdefer comp.destroy();
- try comp.build();
+ comp.start();
try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
}
@@ -131,7 +131,7 @@ pub const TestContext = struct {
try std.io.writeFile(allocator, file1_path, source);
var comp = try Compilation.create(
- &self.event_loop_local,
+ &self.zig_compiler,
"test",
file1_path,
Target.Native,
@@ -144,7 +144,7 @@ pub const TestContext = struct {
_ = try comp.addLinkLib("c", true);
comp.link_out_file = output_file;
- try comp.build();
+ comp.start();
try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
}
src-self-hosted/type.zig
@@ -184,8 +184,8 @@ pub const Type = struct {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
{
- const held = try comp.event_loop_local.getAnyLlvmContext();
- defer held.release(comp.event_loop_local);
+ const held = try comp.zig_compiler.getAnyLlvmContext();
+ defer held.release(comp.zig_compiler);
const llvm_context = held.node.data;