Merge pull request #3780 from Vexu/stage2-async-review

Update use of async functions in self hosted compiler
This commit is contained in:
Andrew Kelley 2019-11-27 13:38:49 -05:00 committed by GitHub
commit 83c664eaa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 345 additions and 367 deletions

View File

@ -735,24 +735,26 @@ pub fn Watch(comptime V: type) type {
allocator: *Allocator,
const OsData = switch (builtin.os) {
.macosx, .freebsd, .netbsd, .dragonfly => struct {
file_table: FileTable,
table_lock: event.Lock,
const FileTable = std.StringHashMap(*Put);
const Put = struct {
putter_frame: @Frame(kqPutEvents),
cancelled: bool = false,
value: V,
};
},
// TODO https://github.com/ziglang/zig/issues/3778
.macosx, .freebsd, .netbsd, .dragonfly => KqOsData,
.linux => LinuxOsData,
.windows => WindowsOsData,
else => @compileError("Unsupported OS"),
};
const KqOsData = struct {
file_table: FileTable,
table_lock: event.Lock,
const FileTable = std.StringHashMap(*Put);
const Put = struct {
putter_frame: @Frame(kqPutEvents),
cancelled: bool = false,
value: V,
};
};
const WindowsOsData = struct {
table_lock: event.Lock,
dir_table: DirTable,
@ -1291,7 +1293,7 @@ pub fn Watch(comptime V: type) type {
os.linux.EINVAL => unreachable,
os.linux.EFAULT => unreachable,
os.linux.EAGAIN => {
global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN);
global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN | os.EPOLLONESHOT);
},
else => unreachable,
}

View File

@ -25,10 +25,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const context = llvm_handle.node.data;
const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
const module = llvm.ModuleCreateWithNameInContext(comp.name.toSliceConst(), context) orelse return error.OutOfMemory;
defer llvm.DisposeModule(module);
llvm.SetTarget(module, comp.llvm_triple.ptr());
llvm.SetTarget(module, comp.llvm_triple.toSliceConst());
llvm.SetDataLayout(module, comp.target_layout_str);
if (util.getObjectFormat(comp.target) == .coff) {
@ -48,23 +48,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const producer = try std.Buffer.allocPrint(
&code.arena.allocator,
"zig {}.{}.{}",
u32(c.ZIG_VERSION_MAJOR),
u32(c.ZIG_VERSION_MINOR),
u32(c.ZIG_VERSION_PATCH),
@as(u32, c.ZIG_VERSION_MAJOR),
@as(u32, c.ZIG_VERSION_MINOR),
@as(u32, c.ZIG_VERSION_PATCH),
);
const flags = "";
const runtime_version = 0;
const compile_unit_file = llvm.CreateFile(
dibuilder,
comp.name.ptr(),
comp.root_package.root_src_dir.ptr(),
comp.name.toSliceConst(),
comp.root_package.root_src_dir.toSliceConst(),
) orelse return error.OutOfMemory;
const is_optimized = comp.build_mode != .Debug;
const compile_unit = llvm.CreateCompileUnit(
dibuilder,
DW.LANG_C99,
compile_unit_file,
producer.ptr(),
producer.toSliceConst(),
is_optimized,
flags,
runtime_version,
@ -99,7 +99,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
// verify the llvm module when safety is on
if (std.debug.runtime_safety) {
var error_ptr: ?[*]u8 = null;
var error_ptr: ?[*:0]u8 = null;
_ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
}
@ -108,12 +108,12 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const is_small = comp.build_mode == .ReleaseSmall;
const is_debug = comp.build_mode == .Debug;
var err_msg: [*]u8 = undefined;
var err_msg: [*:0]u8 = undefined;
// TODO integrate this with evented I/O
if (llvm.TargetMachineEmitToFile(
comp.target_machine,
module,
output_path.ptr(),
output_path.toSliceConst(),
llvm.EmitBinary,
&err_msg,
is_debug,
@ -154,7 +154,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
fn_val.symbol_name.ptr(),
fn_val.symbol_name.toSliceConst(),
llvm_fn_type,
) orelse return error.OutOfMemory;
@ -379,7 +379,7 @@ fn renderLoadUntyped(
ptr: *llvm.Value,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
name: [*]const u8,
name: [*:0]const u8,
) !*llvm.Value {
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
switch (vol) {
@ -390,7 +390,7 @@ fn renderLoadUntyped(
return result;
}
fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value {
fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value {
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
}
@ -438,7 +438,7 @@ pub fn renderAlloca(
) !*llvm.Value {
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory;
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
return result;
}

View File

@ -93,7 +93,7 @@ pub const ZigCompiler = struct {
return LlvmHandle{ .node = node };
}
pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
if (self.native_libc.start()) |ptr| return ptr;
try self.native_libc.data.findNative(self.allocator);
self.native_libc.resolve();
@ -133,62 +133,62 @@ pub const Compilation = struct {
zig_std_dir: []const u8,
/// lazily created when we need it
tmp_dir: event.Future(BuildError![]u8),
tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(),
version_major: u32,
version_minor: u32,
version_patch: u32,
version_major: u32 = 0,
version_minor: u32 = 0,
version_patch: u32 = 0,
linker_script: ?[]const u8,
out_h_path: ?[]const u8,
linker_script: ?[]const u8 = null,
out_h_path: ?[]const u8 = null,
is_test: bool,
each_lib_rpath: bool,
strip: bool,
is_test: bool = false,
each_lib_rpath: bool = false,
strip: bool = false,
is_static: bool,
linker_rdynamic: bool,
linker_rdynamic: bool = false,
clang_argv: []const []const u8,
lib_dirs: []const []const u8,
rpath_list: []const []const u8,
assembly_files: []const []const u8,
clang_argv: []const []const u8 = [_][]const u8{},
lib_dirs: []const []const u8 = [_][]const u8{},
rpath_list: []const []const u8 = [_][]const u8{},
assembly_files: []const []const u8 = [_][]const u8{},
/// paths that are explicitly provided by the user to link against
link_objects: []const []const u8,
link_objects: []const []const u8 = [_][]const u8{},
/// functions that have their own objects that we need to link
/// it uses an optional pointer so that tombstone removals are possible
fn_link_set: event.Locked(FnLinkSet),
fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
pub const FnLinkSet = std.TailQueue(?*Value.Fn);
windows_subsystem_windows: bool,
windows_subsystem_console: bool,
windows_subsystem_windows: bool = false,
windows_subsystem_console: bool = false,
link_libs_list: ArrayList(*LinkLib),
libc_link_lib: ?*LinkLib,
libc_link_lib: ?*LinkLib = null,
err_color: errmsg.Color,
err_color: errmsg.Color = .Auto,
verbose_tokenize: bool,
verbose_ast_tree: bool,
verbose_ast_fmt: bool,
verbose_cimport: bool,
verbose_ir: bool,
verbose_llvm_ir: bool,
verbose_link: bool,
verbose_tokenize: bool = false,
verbose_ast_tree: bool = false,
verbose_ast_fmt: bool = false,
verbose_cimport: bool = false,
verbose_ir: bool = false,
verbose_llvm_ir: bool = false,
verbose_link: bool = false,
darwin_frameworks: []const []const u8,
darwin_version_min: DarwinVersionMin,
darwin_frameworks: []const []const u8 = [_][]const u8{},
darwin_version_min: DarwinVersionMin = .None,
test_filters: []const []const u8,
test_name_prefix: ?[]const u8,
test_filters: []const []const u8 = [_][]const u8{},
test_name_prefix: ?[]const u8 = null,
emit_file_type: Emit,
emit_file_type: Emit = .Binary,
kind: Kind,
link_out_file: ?[]const u8,
link_out_file: ?[]const u8 = null,
events: *event.Channel(Event),
exported_symbol_names: event.Locked(Decl.Table),
@ -213,7 +213,7 @@ pub const Compilation = struct {
target_machine: *llvm.TargetMachine,
target_data_ref: *llvm.TargetData,
target_layout_str: [*]u8,
target_layout_str: [*:0]u8,
target_ptr_bits: u32,
/// for allocating things which have the same lifetime as this Compilation
@ -222,16 +222,16 @@ pub const Compilation = struct {
root_package: *Package,
std_package: *Package,
override_libc: ?*LibCInstallation,
override_libc: ?*LibCInstallation = null,
/// need to wait on this group before deinitializing
deinit_group: event.Group(void),
// destroy_frame: @Frame(createAsync),
// main_loop_frame: @Frame(Compilation.mainLoop),
main_loop_future: event.Future(void),
destroy_frame: *@Frame(createAsync),
main_loop_frame: *@Frame(Compilation.mainLoop),
main_loop_future: event.Future(void) = event.Future(void).init(),
have_err_ret_tracing: bool,
have_err_ret_tracing: bool = false,
/// not locked because it is read-only
primitive_type_table: TypeTable,
@ -243,7 +243,9 @@ pub const Compilation = struct {
c_int_types: [CInt.list.len]*Type.Int,
// fs_watch: *fs.Watch(*Scope.Root),
fs_watch: *fs.Watch(*Scope.Root),
cancelled: bool = false,
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
@ -348,7 +350,9 @@ pub const Compilation = struct {
zig_lib_dir: []const u8,
) !*Compilation {
var optional_comp: ?*Compilation = null;
var frame = async createAsync(
var frame = try zig_compiler.allocator.create(@Frame(createAsync));
errdefer zig_compiler.allocator.destroy(frame);
frame.* = async createAsync(
&optional_comp,
zig_compiler,
name,
@ -359,7 +363,11 @@ pub const Compilation = struct {
is_static,
zig_lib_dir,
);
return optional_comp orelse if (await frame) |_| unreachable else |err| err;
// TODO causes segfault
// return optional_comp orelse if (await frame) |_| unreachable else |err| err;
if (optional_comp) |comp| {
return comp;
} else if (await frame) |_| unreachable else |err| return err;
}
async fn createAsync(
@ -385,50 +393,14 @@ pub const Compilation = struct {
.build_mode = build_mode,
.zig_lib_dir = zig_lib_dir,
.zig_std_dir = undefined,
.tmp_dir = event.Future(BuildError![]u8).init(),
// .destroy_frame = @frame(),
// .main_loop_frame = undefined,
.main_loop_future = event.Future(void).init(),
.destroy_frame = @frame(),
.main_loop_frame = undefined,
.name = undefined,
.llvm_triple = undefined,
.version_major = 0,
.version_minor = 0,
.version_patch = 0,
.verbose_tokenize = false,
.verbose_ast_tree = false,
.verbose_ast_fmt = false,
.verbose_cimport = false,
.verbose_ir = false,
.verbose_llvm_ir = false,
.verbose_link = false,
.linker_script = null,
.out_h_path = null,
.is_test = false,
.each_lib_rpath = false,
.strip = false,
.is_static = is_static,
.linker_rdynamic = false,
.clang_argv = [_][]const u8{},
.lib_dirs = [_][]const u8{},
.rpath_list = [_][]const u8{},
.assembly_files = [_][]const u8{},
.link_objects = [_][]const u8{},
.fn_link_set = event.Locked(FnLinkSet).init(FnLinkSet.init()),
.windows_subsystem_windows = false,
.windows_subsystem_console = false,
.link_libs_list = undefined,
.libc_link_lib = null,
.err_color = errmsg.Color.Auto,
.darwin_frameworks = [_][]const u8{},
.darwin_version_min = DarwinVersionMin.None,
.test_filters = [_][]const u8{},
.test_name_prefix = null,
.emit_file_type = Emit.Binary,
.link_out_file = null,
.exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)),
.prelink_group = event.Group(BuildError!void).init(allocator),
.deinit_group = event.Group(void).init(allocator),
@ -458,11 +430,9 @@ pub const Compilation = struct {
.root_package = undefined,
.std_package = undefined,
.override_libc = null,
.have_err_ret_tracing = false,
.primitive_type_table = undefined,
// .fs_watch = undefined,
.fs_watch = undefined,
};
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
comp.primitive_type_table = TypeTable.init(comp.arena());
@ -534,13 +504,16 @@ pub const Compilation = struct {
comp.root_package = try Package.create(comp.arena(), ".", "");
}
// comp.fs_watch = try fs.Watch(*Scope.Root).create(16);
// defer comp.fs_watch.destroy();
comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16);
defer comp.fs_watch.deinit();
try comp.initTypes();
defer comp.primitive_type_table.deinit();
// comp.main_loop_frame = async comp.mainLoop();
comp.main_loop_frame = try allocator.create(@Frame(mainLoop));
defer allocator.destroy(comp.main_loop_frame);
comp.main_loop_frame.* = async comp.mainLoop();
// 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.
@ -559,7 +532,7 @@ pub const Compilation = struct {
}
/// it does ref the result because it could be an arbitrary integer size
pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
if (name.len >= 2) {
switch (name[0]) {
'i', 'u' => blk: {
@ -753,8 +726,11 @@ pub const Compilation = struct {
}
pub fn destroy(self: *Compilation) void {
// await self.main_loop_frame;
// resume self.destroy_frame;
const allocator = self.gpa();
self.cancelled = true;
await self.main_loop_frame;
resume self.destroy_frame;
allocator.destroy(self.destroy_frame);
}
fn start(self: *Compilation) void {
@ -767,7 +743,7 @@ pub const Compilation = struct {
var build_result = self.initialCompile();
while (true) {
while (!self.cancelled) {
const link_result = if (build_result) blk: {
break :blk self.maybeLink();
} else |err| err;
@ -795,47 +771,47 @@ pub const Compilation = struct {
self.events.put(Event{ .Error = err });
}
// // First, get an item from the watch channel, waiting on the channel.
// var group = event.Group(BuildError!void).init(self.gpa());
// {
// const ev = (self.fs_watch.channel.get()) catch |err| {
// build_result = err;
// continue;
// };
// const root_scope = ev.data;
// group.call(rebuildFile, self, root_scope) catch |err| {
// build_result = err;
// continue;
// };
// }
// // Next, get all the items from the channel that are buffered up.
// while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
// if (ev_or_err) |ev| {
// const root_scope = ev.data;
// group.call(rebuildFile, self, root_scope) catch |err| {
// build_result = err;
// continue;
// };
// } else |err| {
// build_result = err;
// continue;
// }
// }
// build_result = group.wait();
// First, get an item from the watch channel, waiting on the channel.
var group = event.Group(BuildError!void).init(self.gpa());
{
const ev = (self.fs_watch.channel.get()) catch |err| {
build_result = err;
continue;
};
const root_scope = ev.data;
group.call(rebuildFile, self, root_scope) catch |err| {
build_result = err;
continue;
};
}
// Next, get all the items from the channel that are buffered up.
while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
if (ev_or_err) |ev| {
const root_scope = ev.data;
group.call(rebuildFile, self, root_scope) catch |err| {
build_result = err;
continue;
};
} else |err| {
build_result = err;
continue;
}
}
build_result = group.wait();
}
}
async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void {
const tree_scope = blk: {
const source_code = "";
// const source_code = fs.readFile(
// root_scope.realpath,
// max_src_size,
// ) catch |err| {
// try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
// return;
// };
// errdefer self.gpa().free(source_code);
const source_code = fs.readFile(
self.gpa(),
root_scope.realpath,
max_src_size,
) catch |err| {
try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
return;
};
errdefer self.gpa().free(source_code);
const tree = try std.zig.parse(self.gpa(), source_code);
errdefer {
@ -873,7 +849,7 @@ pub const Compilation = struct {
try decl_group.wait();
}
async fn rebuildChangedDecls(
fn rebuildChangedDecls(
self: *Compilation,
group: *event.Group(BuildError!void),
locked_table: *Decl.Table,
@ -962,7 +938,7 @@ pub const Compilation = struct {
}
}
async fn initialCompile(self: *Compilation) !void {
fn initialCompile(self: *Compilation) !void {
if (self.root_src_path) |root_src_path| {
const root_scope = blk: {
// TODO async/await std.fs.realpath
@ -981,7 +957,7 @@ pub const Compilation = struct {
}
}
async fn maybeLink(self: *Compilation) !void {
fn maybeLink(self: *Compilation) !void {
(self.prelink_group.wait()) catch |err| switch (err) {
error.SemanticAnalysisFailed => {},
else => return err,
@ -1165,11 +1141,10 @@ pub const Compilation = struct {
return link_lib;
}
/// cancels itself so no need to await or cancel the promise.
async fn startFindingNativeLibC(self: *Compilation) void {
std.event.Loop.instance.?.yield();
event.Loop.startCpuBoundOperation();
// we don't care if it fails, we're just trying to kick off the future resolution
_ = (self.zig_compiler.getNativeLibC()) catch return;
_ = self.zig_compiler.getNativeLibC() catch return;
}
/// General Purpose Allocator. Must free when done.
@ -1184,7 +1159,7 @@ pub const Compilation = struct {
/// If the temporary directory for this compilation has not been created, it creates it.
/// Then it creates a random file name in that dir and returns it.
pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
const tmp_dir = try self.getTmpDir();
const file_prefix = self.getRandomFileName();
@ -1200,14 +1175,14 @@ pub const Compilation = struct {
/// If the temporary directory for this Compilation has not been created, creates it.
/// Then returns it. The directory is unique to this Compilation and cleaned up when
/// the Compilation deinitializes.
async fn getTmpDir(self: *Compilation) ![]const u8 {
fn getTmpDir(self: *Compilation) ![]const u8 {
if (self.tmp_dir.start()) |ptr| return ptr.*;
self.tmp_dir.data = self.getTmpDirImpl();
self.tmp_dir.resolve();
return self.tmp_dir.data;
}
async fn getTmpDirImpl(self: *Compilation) ![]u8 {
fn getTmpDirImpl(self: *Compilation) ![]u8 {
const comp_dir_name = self.getRandomFileName();
const zig_dir_path = try getZigDir(self.gpa());
defer self.gpa().free(zig_dir_path);
@ -1217,7 +1192,7 @@ pub const Compilation = struct {
return tmp_dir;
}
async fn getRandomFileName(self: *Compilation) [12]u8 {
fn getRandomFileName(self: *Compilation) [12]u8 {
// here we replace the standard +/ with -_ so that it can be used in a file name
const b64_fs_encoder = std.base64.Base64Encoder.init(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
@ -1243,20 +1218,23 @@ pub const Compilation = struct {
}
/// Returns a value which has been ref()'d once
async fn analyzeConstValue(
fn analyzeConstValue(
comp: *Compilation,
tree_scope: *Scope.AstTree,
scope: *Scope,
node: *ast.Node,
expected_type: *Type,
) !*Value {
const analyzed_code = try comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode));
defer comp.gpa().destroy(frame);
frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
const analyzed_code = try await frame;
defer analyzed_code.destroy(comp.gpa());
return analyzed_code.getCompTimeResult(comp);
}
async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
const meta_type = &Type.MetaType.get(comp).base;
defer meta_type.base.deref(comp);
@ -1287,7 +1265,7 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
}
/// The function that actually does the generation.
async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
fn generateDecl(comp: *Compilation, decl: *Decl) !void {
switch (decl.id) {
.Var => @panic("TODO"),
.Fn => {
@ -1298,7 +1276,7 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
}
}
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const tree_scope = fn_decl.base.tree_scope;
const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl);
@ -1315,7 +1293,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
// The Decl.Fn owns the initial 1 reference count
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
fn_decl.value = .{ .Fn = fn_val };
symbol_name_consumed = true;
// Define local parameter variables
@ -1350,12 +1328,15 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
try fn_type.non_key.Normal.variable_list.append(var_scope);
}
const analyzed_code = try comp.genAndAnalyzeCode(
var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode));
defer comp.gpa().destroy(frame);
frame.* = async comp.genAndAnalyzeCode(
tree_scope,
fn_val.child_scope,
body_node,
fn_type.key.data.Normal.return_type,
);
const analyzed_code = try await frame;
errdefer analyzed_code.destroy(comp.gpa());
assert(fn_val.block_scope != null);
@ -1382,7 +1363,7 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
return std.fs.getAppDataDir(allocator, "zig");
}
async fn analyzeFnType(
fn analyzeFnType(
comp: *Compilation,
tree_scope: *Scope.AstTree,
scope: *Scope,
@ -1444,7 +1425,7 @@ async fn analyzeFnType(
return fn_type;
}
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const fn_type = try analyzeFnType(
comp,
fn_decl.base.tree_scope,
@ -1459,6 +1440,6 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
// The Decl.Fn owns the initial 1 reference count
const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
fn_decl.value = .{ .FnProto = fn_proto_val };
symbol_name_consumed = true;
}

View File

@ -69,15 +69,12 @@ pub const Decl = struct {
pub const Fn = struct {
base: Decl,
value: Val,
fn_proto: *ast.Node.FnProto,
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
pub const Val = union(enum) {
value: union(enum) {
Unresolved,
Fn: *Value.Fn,
FnProto: *Value.FnProto,
};
},
fn_proto: *ast.Node.FnProto,
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {

View File

@ -110,7 +110,7 @@ pub const Inst = struct {
unreachable;
}
pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
switch (base.id) {
.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
@ -422,7 +422,7 @@ pub const Inst = struct {
return false;
}
pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
const target = try self.params.target.getAsParam();
if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
@ -472,7 +472,7 @@ pub const Inst = struct {
return false;
}
pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
(ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.SemanticAnalysisFailed,
@ -516,7 +516,7 @@ pub const Inst = struct {
return false;
}
pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
switch (self.params.var_scope.data) {
.Const => @panic("TODO"),
.Param => |param| {
@ -563,7 +563,7 @@ pub const Inst = struct {
return false;
}
pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
const target = try self.params.target.getAsParam();
const target_type = target.getKnownType();
if (target_type.id != .Pointer) {
@ -645,7 +645,7 @@ pub const Inst = struct {
return false;
}
pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
const child_type = try self.params.child_type.getAsConstType(ira);
// if (child_type->id == TypeTableEntryIdUnreachable) {
// ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
@ -658,7 +658,7 @@ pub const Inst = struct {
const amt = try align_inst.getAsConstAlign(ira);
break :blk Type.Pointer.Align{ .Override = amt };
} else blk: {
break :blk Type.Pointer.Align{ .Abi = {} };
break :blk .Abi;
};
const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
.child_type = child_type,
@ -927,7 +927,7 @@ pub const Variable = struct {
pub const BasicBlock = struct {
ref_count: usize,
name_hint: [*]const u8, // must be a C string literal
name_hint: [*:0]const u8,
debug_id: usize,
scope: *Scope,
instruction_list: std.ArrayList(*Inst),
@ -1051,7 +1051,7 @@ pub const Builder = struct {
}
/// No need to clean up resources thanks to the arena allocator.
pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock {
pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock {
const basic_block = try self.arena().create(BasicBlock);
basic_block.* = BasicBlock{
.ref_count = 0,
@ -1078,6 +1078,14 @@ pub const Builder = struct {
self.current_basic_block = basic_block;
}
pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
const alloc = irb.comp.gpa();
var frame = try alloc.create(@Frame(genNode));
defer alloc.destroy(frame);
frame.* = async irb.genNode(node, scope, lval);
return await frame;
}
pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
switch (node.id) {
.Root => unreachable,
@ -1157,7 +1165,7 @@ pub const Builder = struct {
},
.GroupedExpression => {
const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
return irb.genNode(grouped_expr.expr, scope, lval);
return irb.genNodeRecursive(grouped_expr.expr, scope, lval);
},
.BuiltinCall => return error.Unimplemented,
.ErrorSetDecl => return error.Unimplemented,
@ -1186,14 +1194,14 @@ pub const Builder = struct {
}
}
async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
const fn_ref = try irb.genNode(suffix_op.lhs, scope, .None);
fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None);
const args = try irb.arena().alloc(*Inst, call.params.len);
var it = call.params.iterator(0);
var i: usize = 0;
while (it.next()) |arg_node_ptr| : (i += 1) {
args[i] = try irb.genNode(arg_node_ptr.*, scope, .None);
args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None);
}
//bool is_async = node->data.fn_call_expr.is_async;
@ -1214,7 +1222,7 @@ pub const Builder = struct {
//return ir_lval_wrap(irb, scope, fn_call, lval);
}
async fn genPtrType(
fn genPtrType(
irb: *Builder,
prefix_op: *ast.Node.PrefixOp,
ptr_info: ast.Node.PrefixOp.PtrInfo,
@ -1238,7 +1246,7 @@ pub const Builder = struct {
//} else {
// align_value = nullptr;
//}
const child_type = try irb.genNode(prefix_op.rhs, scope, .None);
const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None);
//uint32_t bit_offset_start = 0;
//if (node->data.pointer_type.bit_offset_start != nullptr) {
@ -1307,9 +1315,9 @@ pub const Builder = struct {
var rest: []const u8 = undefined;
if (int_token.len >= 3 and int_token[0] == '0') {
base = switch (int_token[1]) {
'b' => u8(2),
'o' => u8(8),
'x' => u8(16),
'b' => 2,
'o' => 8,
'x' => 16,
else => unreachable,
};
rest = int_token[2..];
@ -1339,7 +1347,7 @@ pub const Builder = struct {
return inst;
}
pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
const src_span = Span.token(str_lit.token);
@ -1389,7 +1397,7 @@ pub const Builder = struct {
}
}
pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
const block_scope = try Scope.Block.create(irb.comp, parent_scope);
const outer_block_scope = &block_scope.base;
@ -1437,7 +1445,7 @@ pub const Builder = struct {
child_scope = &defer_child_scope.base;
continue;
}
const statement_value = try irb.genNode(statement_node, child_scope, .None);
const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None);
is_continuation_unreachable = statement_value.isNoReturn();
if (is_continuation_unreachable) {
@ -1499,7 +1507,7 @@ pub const Builder = struct {
return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
}
pub async fn genControlFlowExpr(
pub fn genControlFlowExpr(
irb: *Builder,
control_flow_expr: *ast.Node.ControlFlowExpression,
scope: *Scope,
@ -1533,7 +1541,7 @@ pub const Builder = struct {
const outer_scope = irb.begin_scope.?;
const return_value = if (control_flow_expr.rhs) |rhs| blk: {
break :blk try irb.genNode(rhs, scope, .None);
break :blk try irb.genNodeRecursive(rhs, scope, .None);
} else blk: {
break :blk try irb.buildConstVoid(scope, src_span, true);
};
@ -1596,7 +1604,7 @@ pub const Builder = struct {
}
}
pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
const src_span = Span.token(identifier.token);
const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
@ -1694,7 +1702,7 @@ pub const Builder = struct {
return result;
}
async fn genDefersForBlock(
fn genDefersForBlock(
irb: *Builder,
inner_scope: *Scope,
outer_scope: *Scope,
@ -1712,7 +1720,7 @@ pub const Builder = struct {
};
if (generate) {
const defer_expr_scope = defer_scope.defer_expr_scope;
const instruction = try irb.genNode(
const instruction = try irb.genNodeRecursive(
defer_expr_scope.expr_node,
&defer_expr_scope.base,
.None,
@ -1797,7 +1805,7 @@ pub const Builder = struct {
// Look at the params and ref() other instructions
comptime var i = 0;
inline while (i < @memberCount(I.Params)) : (i += 1) {
const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
const FieldType = comptime @typeOf(@field(@as(I.Params, undefined), @memberName(I.Params, i)));
switch (FieldType) {
*Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
*BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
@ -1909,7 +1917,7 @@ pub const Builder = struct {
VarScope: *Scope.Var,
};
async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
var s = scope;
while (true) {
switch (s.id) {
@ -2519,7 +2527,7 @@ const Analyze = struct {
}
};
pub async fn gen(
pub fn gen(
comp: *Compilation,
body_node: *ast.Node,
tree_scope: *Scope.AstTree,
@ -2541,7 +2549,7 @@ pub async fn gen(
return irb.finish();
}
pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
const old_entry_bb = old_code.basic_block_list.at(0);
var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);

View File

@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
}
/// Finds the default, native libc.
pub async fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
self.initEmpty();
var group = event.Group(FindError!void).init(allocator);
errdefer group.wait() catch {};
@ -393,14 +393,14 @@ pub const LibCInstallation = struct {
};
/// caller owns returned memory
async fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
const cc_exe = std.os.getenv("CC") orelse "cc";
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", o_file);
defer allocator.free(arg1);
const argv = [_][]const u8{ cc_exe, arg1 };
// TODO This simulates evented I/O for the child process exec
std.event.Loop.instance.?.yield();
event.Loop.startCpuBoundOperation();
const errorable_result = std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;

View File

@ -11,7 +11,7 @@ const util = @import("util.zig");
const Context = struct {
comp: *Compilation,
arena: std.heap.ArenaAllocator,
args: std.ArrayList([*]const u8),
args: std.ArrayList([*:0]const u8),
link_in_crt: bool,
link_err: error{OutOfMemory}!void,
@ -21,7 +21,7 @@ const Context = struct {
out_file_path: std.Buffer,
};
pub async fn link(comp: *Compilation) !void {
pub fn link(comp: *Compilation) !void {
var ctx = Context{
.comp = comp,
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
@ -33,7 +33,7 @@ pub async fn link(comp: *Compilation) !void {
.out_file_path = undefined,
};
defer ctx.arena.deinit();
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
if (comp.link_out_file) |out_file| {
@ -58,7 +58,8 @@ pub async fn link(comp: *Compilation) !void {
try ctx.args.append("lld");
if (comp.haveLibC()) {
ctx.libc = ctx.comp.override_libc orelse blk: {
// TODO https://github.com/ziglang/zig/issues/3190
var libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
@ -66,6 +67,7 @@ pub async fn link(comp: *Compilation) !void {
else => return error.LibCRequiredButNotProvidedOrFound,
}
};
ctx.libc = libc;
}
try constructLinkerArgs(&ctx);
@ -171,7 +173,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
//}
try ctx.args.append("-o");
try ctx.args.append(ctx.out_file_path.ptr());
try ctx.args.append(ctx.out_file_path.toSliceConst());
if (ctx.link_in_crt) {
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
@ -214,10 +216,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
if (ctx.comp.haveLibC()) {
try ctx.args.append("-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
// TODO addNullByte should probably return [:0]u8
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr));
try ctx.args.append("-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr));
if (!ctx.comp.is_static) {
const dl = blk: {
@ -226,7 +229,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
return error.LibCMissingDynamicLinker;
};
try ctx.args.append("-dynamic-linker");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
}
}
@ -238,7 +241,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// .o files
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
}
try addFnObjects(ctx);
@ -313,7 +316,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.fs.path.join(&ctx.arena.allocator, [_][]const u8{ dirname, basename });
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
try ctx.args.append(full_path_with_null.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));
}
fn constructLinkerArgsCoff(ctx: *Context) !void {
@ -339,12 +342,12 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
const is_library = ctx.comp.kind == .Lib;
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
try ctx.args.append(out_arg.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));
if (ctx.comp.haveLibC()) {
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr));
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr));
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr));
}
if (ctx.link_in_crt) {
@ -353,17 +356,17 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
if (ctx.comp.is_static) {
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
try ctx.args.append(cmt_lib_name.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));
} else {
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
try ctx.args.append(msvcrt_lib_name.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));
}
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
try ctx.args.append(vcruntime_lib_name.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
try ctx.args.append(crt_lib_name.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));
// Visual C++ 2015 Conformance Changes
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
@ -395,7 +398,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
}
try addFnObjects(ctx);
@ -504,11 +507,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
//}
try ctx.args.append("-arch");
const darwin_arch_str = try std.cstr.addNullByte(
&ctx.arena.allocator,
ctx.comp.target.getDarwinArchString(),
);
try ctx.args.append(darwin_arch_str.ptr);
try ctx.args.append(util.getDarwinArchString(ctx.comp.target));
const platform = try DarwinPlatform.get(ctx.comp);
switch (platform.kind) {
@ -517,7 +516,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
.IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"),
}
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
try ctx.args.append(ver_str.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr));
if (ctx.comp.kind == .Exe) {
if (ctx.comp.is_static) {
@ -528,7 +527,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
}
try ctx.args.append("-o");
try ctx.args.append(ctx.out_file_path.ptr());
try ctx.args.append(ctx.out_file_path.toSliceConst());
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
@ -572,7 +571,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
}
try addFnObjects(ctx);
@ -593,10 +592,10 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
} else {
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
try ctx.args.append(arg.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
} else {
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
try ctx.args.append(arg.ptr);
try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
}
}
}
@ -626,20 +625,19 @@ fn constructLinkerArgsWasm(ctx: *Context) void {
}
fn addFnObjects(ctx: *Context) !void {
// at this point it's guaranteed nobody else has this lock, so we circumvent it
// and avoid having to be an async function
const fn_link_set = &ctx.comp.fn_link_set.private_data;
const held = ctx.comp.fn_link_set.acquire();
defer held.release();
var it = fn_link_set.first;
var it = held.value.first;
while (it) |node| {
const fn_val = node.data orelse {
// handle the tombstone. See Value.Fn.destroy.
it = node.next;
fn_link_set.remove(node);
held.value.remove(node);
ctx.comp.gpa().destroy(node);
continue;
};
try ctx.args.append(fn_val.containing_object.ptr());
try ctx.args.append(fn_val.containing_object.toSliceConst());
it = node.next;
}
}

View File

@ -86,7 +86,7 @@ pub const AddGlobal = LLVMAddGlobal;
extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value;
pub const ConstStringInContext = LLVMConstStringInContext;
extern fn LLVMConstStringInContext(C: *Context, Str: [*:0]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
pub const ConstInt = LLVMConstInt;
extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value;

View File

@ -49,14 +49,15 @@ const usage =
const Command = struct {
name: []const u8,
exec: fn (*Allocator, []const []const u8) anyerror!void,
exec: async fn (*Allocator, []const []const u8) anyerror!void,
};
pub fn main() !void {
// This allocator needs to be thread-safe because we use it for the event.Loop
// which multiplexes async functions onto kernel threads.
// libc allocator is guaranteed to have this property.
const allocator = std.heap.c_allocator;
// TODO https://github.com/ziglang/zig/issues/3783
const allocator = std.heap.page_allocator;
stdout = &std.io.getStdOut().outStream().stream;
@ -118,14 +119,18 @@ pub fn main() !void {
},
};
for (commands) |command| {
inline for (commands) |command| {
if (mem.eql(u8, command.name, args[1])) {
return command.exec(allocator, args[2..]);
var frame = try allocator.create(@Frame(command.exec));
defer allocator.destroy(frame);
frame.* = async command.exec(allocator, args[2..]);
return await frame;
}
}
try stderr.print("unknown command: {}\n\n", args[1]);
try stderr.write(usage);
process.argsFree(allocator, args);
process.exit(1);
}
@ -461,13 +466,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.link_objects = link_objects;
comp.start();
const frame = async processBuildEvents(comp, color);
processBuildEvents(comp, color);
}
async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
var count: usize = 0;
while (true) {
// TODO directly awaiting async should guarantee memory allocation elision
while (!comp.cancelled) {
const build_event = comp.events.get();
count += 1;
@ -545,7 +549,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
"Try running `zig libc` to see an example for the native target.\n",
libc_paths_file,
@errorName(err),
) catch process.exit(1);
) catch {};
process.exit(1);
};
}
@ -567,12 +571,8 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
var zig_compiler = try ZigCompiler.init(allocator);
defer zig_compiler.deinit();
const frame = async findLibCAsync(&zig_compiler);
}
async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
const libc = zig_compiler.getNativeLibC() catch |err| {
stderr.print("unable to find libc: {}\n", @errorName(err)) catch process.exit(1);
stderr.print("unable to find libc: {}\n", @errorName(err)) catch {};
process.exit(1);
};
libc.render(stdout) catch process.exit(1);
@ -644,11 +644,23 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
process.exit(1);
}
return asyncFmtMain(
allocator,
&flags,
color,
);
var fmt = Fmt{
.allocator = allocator,
.seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
.any_error = false,
.color = color,
};
const check_mode = flags.present("check");
var group = event.Group(FmtError!void).init(allocator);
for (flags.positionals.toSliceConst()) |file_path| {
try group.call(fmtPath, &fmt, file_path, check_mode);
}
try group.wait();
if (fmt.any_error) {
process.exit(1);
}
}
const FmtError = error{
@ -673,30 +685,6 @@ const FmtError = error{
CurrentWorkingDirectoryUnlinked,
} || fs.File.OpenError;
async fn asyncFmtMain(
allocator: *Allocator,
flags: *const Args,
color: errmsg.Color,
) FmtError!void {
var fmt = Fmt{
.allocator = allocator,
.seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
.any_error = false,
.color = color,
};
const check_mode = flags.present("check");
var group = event.Group(FmtError!void).init(allocator);
for (flags.positionals.toSliceConst()) |file_path| {
try group.call(fmtPath, &fmt, file_path, check_mode);
}
try group.wait();
if (fmt.any_error) {
process.exit(1);
}
}
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
defer fmt.allocator.free(file_path);
@ -708,33 +696,34 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
if (try held.value.put(file_path, {})) |_| return;
}
const source_code = "";
// const source_code = event.fs.readFile(
// file_path,
// max_src_size,
// ) catch |err| switch (err) {
// error.IsDir, error.AccessDenied => {
// // TODO make event based (and dir.next())
// var dir = try fs.Dir.cwd().openDirList(file_path);
// defer dir.close();
const source_code = event.fs.readFile(
fmt.allocator,
file_path,
max_src_size,
) catch |err| switch (err) {
error.IsDir, error.AccessDenied => {
var dir = try fs.Dir.cwd().openDirList(file_path);
defer dir.close();
// var group = event.Group(FmtError!void).init(fmt.allocator);
// while (try dir.next()) |entry| {
// if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
// const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
// try group.call(fmtPath, fmt, full_path, check_mode);
// }
// }
// return group.wait();
// },
// else => {
// // TODO lock stderr printing
// try stderr.print("unable to open '{}': {}\n", file_path, err);
// fmt.any_error = true;
// return;
// },
// };
// defer fmt.allocator.free(source_code);
var group = event.Group(FmtError!void).init(fmt.allocator);
var it = dir.iterate();
while (try it.next()) |entry| {
if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
@panic("TODO https://github.com/ziglang/zig/issues/3777");
// try group.call(fmtPath, fmt, full_path, check_mode);
}
}
return group.wait();
},
else => {
// TODO lock stderr printing
try stderr.print("unable to open '{}': {}\n", file_path, err);
fmt.any_error = true;
return;
},
};
defer fmt.allocator.free(source_code);
const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
@ -867,10 +856,12 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
.exec = cmdInternalBuildInfo,
}};
for (sub_commands) |sub_command| {
inline for (sub_commands) |sub_command| {
if (mem.eql(u8, sub_command.name, args[0])) {
try sub_command.exec(allocator, args[1..]);
return;
var frame = try allocator.create(@Frame(sub_command.exec));
defer allocator.destroy(frame);
frame.* = async sub_command.exec(allocator, args[1..]);
return await frame;
}
}

View File

@ -26,7 +26,8 @@ test "stage2" {
}
const file1 = "1.zig";
const allocator = std.heap.c_allocator;
// TODO https://github.com/ziglang/zig/issues/3783
const allocator = std.heap.page_allocator;
pub const TestContext = struct {
zig_compiler: ZigCompiler,
@ -94,8 +95,8 @@ pub const TestContext = struct {
&self.zig_compiler,
"test",
file1_path,
Target.Native,
Compilation.Kind.Obj,
.Native,
.Obj,
.Debug,
true, // is_static
self.zig_lib_dir,
@ -116,7 +117,7 @@ pub const TestContext = struct {
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{.Native = {}}).exeFileExt());
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{ .Native = {} }).exeFileExt());
if (std.fs.path.dirname(file1_path)) |dirname| {
try std.fs.makePath(allocator, dirname);
}
@ -128,8 +129,8 @@ pub const TestContext = struct {
&self.zig_compiler,
"test",
file1_path,
Target.Native,
Compilation.Kind.Exe,
.Native,
.Exe,
.Debug,
false,
self.zig_lib_dir,
@ -148,15 +149,12 @@ pub const TestContext = struct {
exe_file: []const u8,
expected_output: []const u8,
) anyerror!void {
// TODO this should not be necessary
const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
defer comp.destroy();
const build_event = comp.events.get();
switch (build_event) {
.Ok => {
const argv = [_][]const u8{exe_file_2};
const argv = [_][]const u8{exe_file};
// TODO use event loop
const child = try std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
switch (child.term) {
@ -173,13 +171,13 @@ pub const TestContext = struct {
return error.OutputMismatch;
}
},
Compilation.Event.Error => |err| return err,
Compilation.Event.Fail => |msgs| {
.Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err,
.Fail => |msgs| {
const stderr = std.io.getStdErr();
try stderr.write("build incorrectly failed:\n");
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(stderr, errmsg.Color.Auto);
try msg.printToFile(stderr, .Auto);
}
},
}

View File

@ -53,7 +53,7 @@ pub const Type = struct {
base: *Type,
allocator: *Allocator,
llvm_context: *llvm.Context,
) (error{OutOfMemory}!*llvm.Type) {
) error{OutOfMemory}!*llvm.Type {
switch (base.id) {
.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
@ -184,7 +184,7 @@ pub const Type = struct {
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
/// Otherwise, this one will grab one from the pool and then release it.
pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
if (base.abi_alignment.start()) |ptr| return ptr.*;
{
@ -200,7 +200,7 @@ pub const Type = struct {
}
/// If you have an llvm conext handy, you can use it here.
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
if (base.abi_alignment.start()) |ptr| return ptr.*;
base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
@ -209,7 +209,7 @@ pub const Type = struct {
}
/// Lower level function that does the work. See getAbiAlignment.
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
}
@ -367,7 +367,7 @@ pub const Type = struct {
}
/// takes ownership of key.Normal.params on success
pub async fn get(comp: *Compilation, key: Key) !*Fn {
pub fn get(comp: *Compilation, key: Key) !*Fn {
{
const held = comp.fn_type_table.acquire();
defer held.release();
@ -564,7 +564,7 @@ pub const Type = struct {
return comp.u8_type;
}
pub async fn get(comp: *Compilation, key: Key) !*Int {
pub fn get(comp: *Compilation, key: Key) !*Int {
{
const held = comp.int_type_table.acquire();
defer held.release();
@ -606,7 +606,7 @@ pub const Type = struct {
comp.registerGarbage(Int, &self.garbage_node);
}
pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
pub fn gcDestroy(self: *Int, comp: *Compilation) void {
{
const held = comp.int_type_table.acquire();
defer held.release();
@ -700,7 +700,7 @@ pub const Type = struct {
comp.registerGarbage(Pointer, &self.garbage_node);
}
pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
pub fn gcDestroy(self: *Pointer, comp: *Compilation) void {
{
const held = comp.ptr_type_table.acquire();
defer held.release();
@ -711,14 +711,14 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
switch (self.key.alignment) {
.Abi => return self.key.child_type.getAbiAlignment(comp),
.Override => |alignment| return alignment,
}
}
pub async fn get(
pub fn get(
comp: *Compilation,
key: Key,
) !*Pointer {
@ -726,8 +726,10 @@ pub const Type = struct {
switch (key.alignment) {
.Abi => {},
.Override => |alignment| {
// TODO https://github.com/ziglang/zig/issues/3190
var align_spill = alignment;
const abi_align = try key.child_type.getAbiAlignment(comp);
if (abi_align == alignment) {
if (abi_align == align_spill) {
normal_key.alignment = .Abi;
}
},
@ -828,7 +830,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub async fn get(comp: *Compilation, key: Key) !*Array {
pub fn get(comp: *Compilation, key: Key) !*Array {
key.elem_type.base.ref();
errdefer key.elem_type.base.deref(comp);

View File

@ -32,21 +32,21 @@ pub fn getFloatAbi(self: Target) FloatAbi {
};
}
pub fn getObjectFormat(self: Target) Target.ObjectFormat {
return switch (self) {
.Native => @import("builtin").object_format,
.Cross => {
pub fn getObjectFormat(target: Target) Target.ObjectFormat {
switch (target) {
.Native => return @import("builtin").object_format,
.Cross => blk: {
if (target.isWindows() or target.isUefi()) {
break .coff;
return .coff;
} else if (target.isDarwin()) {
break .macho;
return .macho;
}
if (target.isWasm()) {
break .wasm;
return .wasm;
}
break .elf;
return .elf;
},
};
}
}
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
@ -156,7 +156,7 @@ pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
}
}
pub fn getDarwinArchString(self: Target) []const u8 {
pub fn getDarwinArchString(self: Target) [:0]const u8 {
const arch = self.getArch();
switch (arch) {
.aarch64 => return "arm64",
@ -166,7 +166,8 @@ pub fn getDarwinArchString(self: Target) []const u8 {
.powerpc => return "ppc",
.powerpc64 => return "ppc64",
.powerpc64le => return "ppc64le",
else => return @tagName(arch),
// @tagName should be able to return sentinel terminated slice
else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch),
}
}

View File

@ -156,7 +156,7 @@ pub const Value = struct {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
self.symbol_name.toSliceConst(),
llvm_fn_type,
) orelse return error.OutOfMemory;
@ -241,7 +241,7 @@ pub const Value = struct {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
self.symbol_name.toSliceConst(),
llvm_fn_type,
) orelse return error.OutOfMemory;
@ -334,7 +334,7 @@ pub const Value = struct {
field_index: usize,
};
pub async fn createArrayElemPtr(
pub fn createArrayElemPtr(
comp: *Compilation,
array_val: *Array,
mut: Type.Pointer.Mut,
@ -350,7 +350,7 @@ pub const Value = struct {
.mut = mut,
.vol = Type.Pointer.Vol.Non,
.size = size,
.alignment = Type.Pointer.Align.Abi,
.alignment = .Abi,
});
var ptr_type_consumed = false;
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
@ -390,13 +390,13 @@ pub const Value = struct {
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
const indices = [_]*llvm.Value{
var indices = [_]*llvm.Value{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
return llvm.ConstInBoundsGEP(
array_llvm_value,
&indices,
@ptrCast([*]*llvm.Value, &indices),
@intCast(c_uint, indices.len),
) orelse return error.OutOfMemory;
},
@ -423,7 +423,7 @@ pub const Value = struct {
};
/// Takes ownership of buffer
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
const u8_type = Type.Int.get_u8(comp);
defer u8_type.base.base.deref(comp);