self-hosted: create tmp dir for .o files and emit .o file for fn
parent
0fa24b6b75
commit
97bfeac13f
|
@ -479,6 +479,7 @@ set(ZIG_STD_FILES
|
|||
"index.zig"
|
||||
"io.zig"
|
||||
"json.zig"
|
||||
"lazy_init.zig"
|
||||
"linked_list.zig"
|
||||
"macho.zig"
|
||||
"math/acos.zig"
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Compilation = @import("compilation.zig").Compilation;
|
||||
// we go through llvm instead of c for 2 reasons:
|
||||
// 1. to avoid accidentally calling the non-thread-safe functions
|
||||
// 2. patch up some of the types to remove nullability
|
||||
const llvm = @import("llvm.zig");
|
||||
const c = @import("c.zig");
|
||||
const ir = @import("ir.zig");
|
||||
const Value = @import("value.zig").Value;
|
||||
const Type = @import("type.zig").Type;
|
||||
const event = std.event;
|
||||
const assert = std.debug.assert;
|
||||
const DW = std.dwarf;
|
||||
|
||||
pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {
|
||||
fn_val.base.ref();
|
||||
defer fn_val.base.deref(comp);
|
||||
defer code.destroy(comp.a());
|
||||
defer code.destroy(comp.gpa());
|
||||
|
||||
var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable);
|
||||
errdefer output_path.deinit();
|
||||
|
||||
const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
|
||||
defer llvm_handle.release(comp.event_loop_local);
|
||||
|
@ -23,13 +26,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
|||
const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
|
||||
defer llvm.DisposeModule(module);
|
||||
|
||||
llvm.SetTarget(module, comp.llvm_triple.ptr());
|
||||
llvm.SetDataLayout(module, comp.target_layout_str);
|
||||
|
||||
if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) {
|
||||
llvm.AddModuleCodeViewFlag(module);
|
||||
} else {
|
||||
llvm.AddModuleDebugInfoFlag(module);
|
||||
}
|
||||
|
||||
const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
|
||||
defer llvm.DisposeBuilder(builder);
|
||||
|
||||
const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;
|
||||
defer llvm.DisposeDIBuilder(dibuilder);
|
||||
|
||||
// Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
|
||||
// the git revision.
|
||||
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),
|
||||
);
|
||||
const flags = c"";
|
||||
const runtime_version = 0;
|
||||
const compile_unit_file = llvm.CreateFile(
|
||||
dibuilder,
|
||||
comp.name.ptr(),
|
||||
comp.root_package.root_src_dir.ptr(),
|
||||
) orelse return error.OutOfMemory;
|
||||
const is_optimized = comp.build_mode != builtin.Mode.Debug;
|
||||
const compile_unit = llvm.CreateCompileUnit(
|
||||
dibuilder,
|
||||
DW.LANG_C99,
|
||||
compile_unit_file,
|
||||
producer.ptr(),
|
||||
is_optimized,
|
||||
flags,
|
||||
runtime_version,
|
||||
c"",
|
||||
0,
|
||||
!comp.strip,
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
var ofile = ObjectFile{
|
||||
.comp = comp,
|
||||
.module = module,
|
||||
.builder = builder,
|
||||
.dibuilder = dibuilder,
|
||||
.context = context,
|
||||
.lock = event.Lock.init(comp.loop),
|
||||
};
|
||||
|
@ -41,8 +87,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
|||
// LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
|
||||
//}
|
||||
|
||||
// TODO
|
||||
//ZigLLVMDIBuilderFinalize(g->dbuilder);
|
||||
llvm.DIBuilderFinalize(dibuilder);
|
||||
|
||||
if (comp.verbose_llvm_ir) {
|
||||
llvm.DumpModule(ofile.module);
|
||||
|
@ -53,17 +98,42 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
|||
var error_ptr: ?[*]u8 = null;
|
||||
_ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
|
||||
}
|
||||
|
||||
assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types
|
||||
|
||||
const is_small = comp.build_mode == builtin.Mode.ReleaseSmall;
|
||||
const is_debug = comp.build_mode == builtin.Mode.Debug;
|
||||
|
||||
var err_msg: [*]u8 = undefined;
|
||||
// TODO integrate this with evented I/O
|
||||
if (llvm.TargetMachineEmitToFile(
|
||||
comp.target_machine,
|
||||
module,
|
||||
output_path.ptr(),
|
||||
llvm.EmitBinary,
|
||||
&err_msg,
|
||||
is_debug,
|
||||
is_small,
|
||||
)) {
|
||||
if (std.debug.runtime_safety) {
|
||||
std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg);
|
||||
}
|
||||
return error.WritingObjectFileFailed;
|
||||
}
|
||||
//validate_inline_fns(g); TODO
|
||||
fn_val.containing_object = output_path;
|
||||
}
|
||||
|
||||
pub const ObjectFile = struct {
|
||||
comp: *Compilation,
|
||||
module: llvm.ModuleRef,
|
||||
builder: llvm.BuilderRef,
|
||||
dibuilder: *llvm.DIBuilder,
|
||||
context: llvm.ContextRef,
|
||||
lock: event.Lock,
|
||||
|
||||
fn a(self: *ObjectFile) *std.mem.Allocator {
|
||||
return self.comp.a();
|
||||
fn gpa(self: *ObjectFile) *std.mem.Allocator {
|
||||
return self.comp.gpa();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -26,16 +26,31 @@ const Value = @import("value.zig").Value;
|
|||
const Type = Value.Type;
|
||||
const Span = errmsg.Span;
|
||||
const codegen = @import("codegen.zig");
|
||||
const Package = @import("package.zig").Package;
|
||||
|
||||
/// Data that is local to the event loop.
|
||||
pub const EventLoopLocal = struct {
|
||||
loop: *event.Loop,
|
||||
llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
|
||||
|
||||
fn init(loop: *event.Loop) EventLoopLocal {
|
||||
/// TODO pool these so that it doesn't have to lock
|
||||
prng: event.Locked(std.rand.DefaultPrng),
|
||||
|
||||
var lazy_init_targets = std.lazyInit(void);
|
||||
|
||||
fn init(loop: *event.Loop) !EventLoopLocal {
|
||||
lazy_init_targets.get() orelse {
|
||||
Target.initializeAll();
|
||||
lazy_init_targets.resolve();
|
||||
};
|
||||
|
||||
var seed_bytes: [@sizeOf(u64)]u8 = undefined;
|
||||
try std.os.getRandomBytes(seed_bytes[0..]);
|
||||
const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
|
||||
return EventLoopLocal{
|
||||
.loop = loop,
|
||||
.llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
|
||||
.prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -76,10 +91,16 @@ pub const Compilation = struct {
|
|||
event_loop_local: *EventLoopLocal,
|
||||
loop: *event.Loop,
|
||||
name: Buffer,
|
||||
llvm_triple: Buffer,
|
||||
root_src_path: ?[]const u8,
|
||||
target: Target,
|
||||
llvm_target: llvm.TargetRef,
|
||||
build_mode: builtin.Mode,
|
||||
zig_lib_dir: []const u8,
|
||||
zig_std_dir: []const u8,
|
||||
|
||||
/// lazily created when we need it
|
||||
tmp_dir: event.Future(BuildError![]u8),
|
||||
|
||||
version_major: u32,
|
||||
version_minor: u32,
|
||||
|
@ -106,8 +127,16 @@ pub const Compilation = struct {
|
|||
lib_dirs: []const []const u8,
|
||||
rpath_list: []const []const u8,
|
||||
assembly_files: []const []const u8,
|
||||
|
||||
/// paths that are explicitly provided by the user to link against
|
||||
link_objects: []const []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),
|
||||
|
||||
pub const FnLinkSet = std.LinkedList(?*Value.Fn);
|
||||
|
||||
windows_subsystem_windows: bool,
|
||||
windows_subsystem_console: bool,
|
||||
|
||||
|
@ -141,7 +170,7 @@ pub const Compilation = struct {
|
|||
|
||||
/// Before code generation starts, must wait on this group to make sure
|
||||
/// the build is complete.
|
||||
build_group: event.Group(BuildError!void),
|
||||
prelink_group: event.Group(BuildError!void),
|
||||
|
||||
compile_errors: event.Locked(CompileErrList),
|
||||
|
||||
|
@ -155,6 +184,16 @@ pub const Compilation = struct {
|
|||
false_value: *Value.Bool,
|
||||
noreturn_value: *Value.NoReturn,
|
||||
|
||||
target_machine: llvm.TargetMachineRef,
|
||||
target_data_ref: llvm.TargetDataRef,
|
||||
target_layout_str: [*]u8,
|
||||
|
||||
/// for allocating things which have the same lifetime as this Compilation
|
||||
arena_allocator: std.heap.ArenaAllocator,
|
||||
|
||||
root_package: *Package,
|
||||
std_package: *Package,
|
||||
|
||||
const CompileErrList = std.ArrayList(*errmsg.Msg);
|
||||
|
||||
// TODO handle some of these earlier and report them in a way other than error codes
|
||||
|
@ -195,6 +234,9 @@ pub const Compilation = struct {
|
|||
BufferTooSmall,
|
||||
Unimplemented, // TODO remove this one
|
||||
SemanticAnalysisFailed, // TODO remove this one
|
||||
ReadOnlyFileSystem,
|
||||
LinkQuotaExceeded,
|
||||
EnvironmentVariableNotFound,
|
||||
};
|
||||
|
||||
pub const Event = union(enum) {
|
||||
|
@ -234,31 +276,31 @@ pub const Compilation = struct {
|
|||
event_loop_local: *EventLoopLocal,
|
||||
name: []const u8,
|
||||
root_src_path: ?[]const u8,
|
||||
target: *const Target,
|
||||
target: Target,
|
||||
kind: Kind,
|
||||
build_mode: builtin.Mode,
|
||||
is_static: bool,
|
||||
zig_lib_dir: []const u8,
|
||||
cache_dir: []const u8,
|
||||
) !*Compilation {
|
||||
const loop = event_loop_local.loop;
|
||||
|
||||
var name_buffer = try Buffer.init(loop.allocator, name);
|
||||
errdefer name_buffer.deinit();
|
||||
|
||||
const events = try event.Channel(Event).create(loop, 0);
|
||||
errdefer events.destroy();
|
||||
|
||||
const comp = try loop.allocator.create(Compilation{
|
||||
const comp = try event_loop_local.loop.allocator.create(Compilation{
|
||||
.loop = loop,
|
||||
.arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
|
||||
.event_loop_local = event_loop_local,
|
||||
.events = events,
|
||||
.name = name_buffer,
|
||||
.events = undefined,
|
||||
.root_src_path = root_src_path,
|
||||
.target = target.*,
|
||||
.target = target,
|
||||
.llvm_target = undefined,
|
||||
.kind = kind,
|
||||
.build_mode = build_mode,
|
||||
.zig_lib_dir = zig_lib_dir,
|
||||
.zig_std_dir = undefined,
|
||||
.cache_dir = cache_dir,
|
||||
.tmp_dir = event.Future(BuildError![]u8).init(loop),
|
||||
|
||||
.name = undefined,
|
||||
.llvm_triple = undefined,
|
||||
|
||||
.version_major = 0,
|
||||
.version_minor = 0,
|
||||
|
@ -283,7 +325,7 @@ pub const Compilation = struct {
|
|||
.is_test = false,
|
||||
.each_lib_rpath = false,
|
||||
.strip = false,
|
||||
.is_static = false,
|
||||
.is_static = is_static,
|
||||
.linker_rdynamic = false,
|
||||
.clang_argv = [][]const u8{},
|
||||
.llvm_argv = [][]const u8{},
|
||||
|
@ -291,9 +333,10 @@ pub const Compilation = struct {
|
|||
.rpath_list = [][]const u8{},
|
||||
.assembly_files = [][]const u8{},
|
||||
.link_objects = [][]const u8{},
|
||||
.fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
|
||||
.windows_subsystem_windows = false,
|
||||
.windows_subsystem_console = false,
|
||||
.link_libs_list = ArrayList(*LinkLib).init(loop.allocator),
|
||||
.link_libs_list = undefined,
|
||||
.libc_link_lib = null,
|
||||
.err_color = errmsg.Color.Auto,
|
||||
.darwin_frameworks = [][]const u8{},
|
||||
|
@ -303,7 +346,7 @@ pub const Compilation = struct {
|
|||
.emit_file_type = Emit.Binary,
|
||||
.link_out_file = null,
|
||||
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
|
||||
.build_group = event.Group(BuildError!void).init(loop),
|
||||
.prelink_group = event.Group(BuildError!void).init(loop),
|
||||
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
|
||||
|
||||
.meta_type = undefined,
|
||||
|
@ -314,13 +357,82 @@ pub const Compilation = struct {
|
|||
.false_value = undefined,
|
||||
.noreturn_type = undefined,
|
||||
.noreturn_value = undefined,
|
||||
|
||||
.target_machine = undefined,
|
||||
.target_data_ref = undefined,
|
||||
.target_layout_str = undefined,
|
||||
|
||||
.root_package = undefined,
|
||||
.std_package = undefined,
|
||||
});
|
||||
errdefer {
|
||||
comp.arena_allocator.deinit();
|
||||
comp.loop.allocator.destroy(comp);
|
||||
}
|
||||
|
||||
comp.name = try Buffer.init(comp.arena(), name);
|
||||
comp.llvm_triple = try target.getTriple(comp.arena());
|
||||
comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
|
||||
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
|
||||
comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
|
||||
|
||||
const opt_level = switch (build_mode) {
|
||||
builtin.Mode.Debug => llvm.CodeGenLevelNone,
|
||||
else => llvm.CodeGenLevelAggressive,
|
||||
};
|
||||
|
||||
const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
|
||||
|
||||
// LLVM creates invalid binaries on Windows sometimes.
|
||||
// See https://github.com/ziglang/zig/issues/508
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
|
||||
comp.target_machine = llvm.CreateTargetMachine(
|
||||
comp.llvm_target,
|
||||
comp.llvm_triple.ptr(),
|
||||
target_specific_cpu_args orelse c"",
|
||||
target_specific_cpu_features orelse c"",
|
||||
opt_level,
|
||||
reloc_mode,
|
||||
llvm.CodeModelDefault,
|
||||
) orelse return error.OutOfMemory;
|
||||
errdefer 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);
|
||||
|
||||
comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
|
||||
errdefer llvm.DisposeMessage(comp.target_layout_str);
|
||||
|
||||
comp.events = try event.Channel(Event).create(comp.loop, 0);
|
||||
errdefer comp.events.destroy();
|
||||
|
||||
if (root_src_path) |root_src| {
|
||||
const dirname = std.os.path.dirname(root_src) orelse ".";
|
||||
const basename = std.os.path.basename(root_src);
|
||||
|
||||
comp.root_package = try Package.create(comp.arena(), dirname, basename);
|
||||
comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig");
|
||||
try comp.root_package.add("std", comp.std_package);
|
||||
} else {
|
||||
comp.root_package = try Package.create(comp.arena(), ".", "");
|
||||
}
|
||||
|
||||
try comp.initTypes();
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
fn initTypes(comp: *Compilation) !void {
|
||||
comp.meta_type = try comp.a().create(Type.MetaType{
|
||||
comp.meta_type = try comp.gpa().create(Type.MetaType{
|
||||
.base = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
|
@ -333,9 +445,9 @@ pub const Compilation = struct {
|
|||
});
|
||||
comp.meta_type.value = &comp.meta_type.base;
|
||||
comp.meta_type.base.base.typeof = &comp.meta_type.base;
|
||||
errdefer comp.a().destroy(comp.meta_type);
|
||||
errdefer comp.gpa().destroy(comp.meta_type);
|
||||
|
||||
comp.void_type = try comp.a().create(Type.Void{
|
||||
comp.void_type = try comp.gpa().create(Type.Void{
|
||||
.base = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
|
@ -345,9 +457,9 @@ pub const Compilation = struct {
|
|||
.id = builtin.TypeId.Void,
|
||||
},
|
||||
});
|
||||
errdefer comp.a().destroy(comp.void_type);
|
||||
errdefer comp.gpa().destroy(comp.void_type);
|
||||
|
||||
comp.noreturn_type = try comp.a().create(Type.NoReturn{
|
||||
comp.noreturn_type = try comp.gpa().create(Type.NoReturn{
|
||||
.base = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
|
@ -357,9 +469,9 @@ pub const Compilation = struct {
|
|||
.id = builtin.TypeId.NoReturn,
|
||||
},
|
||||
});
|
||||
errdefer comp.a().destroy(comp.noreturn_type);
|
||||
errdefer comp.gpa().destroy(comp.noreturn_type);
|
||||
|
||||
comp.bool_type = try comp.a().create(Type.Bool{
|
||||
comp.bool_type = try comp.gpa().create(Type.Bool{
|
||||
.base = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
|
@ -369,18 +481,18 @@ pub const Compilation = struct {
|
|||
.id = builtin.TypeId.Bool,
|
||||
},
|
||||
});
|
||||
errdefer comp.a().destroy(comp.bool_type);
|
||||
errdefer comp.gpa().destroy(comp.bool_type);
|
||||
|
||||
comp.void_value = try comp.a().create(Value.Void{
|
||||
comp.void_value = try comp.gpa().create(Value.Void{
|
||||
.base = Value{
|
||||
.id = Value.Id.Void,
|
||||
.typeof = &Type.Void.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
errdefer comp.a().destroy(comp.void_value);
|
||||
errdefer comp.gpa().destroy(comp.void_value);
|
||||
|
||||
comp.true_value = try comp.a().create(Value.Bool{
|
||||
comp.true_value = try comp.gpa().create(Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typeof = &Type.Bool.get(comp).base,
|
||||
|
@ -388,9 +500,9 @@ pub const Compilation = struct {
|
|||
},
|
||||
.x = true,
|
||||
});
|
||||
errdefer comp.a().destroy(comp.true_value);
|
||||
errdefer comp.gpa().destroy(comp.true_value);
|
||||
|
||||
comp.false_value = try comp.a().create(Value.Bool{
|
||||
comp.false_value = try comp.gpa().create(Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typeof = &Type.Bool.get(comp).base,
|
||||
|
@ -398,19 +510,23 @@ pub const Compilation = struct {
|
|||
},
|
||||
.x = false,
|
||||
});
|
||||
errdefer comp.a().destroy(comp.false_value);
|
||||
errdefer comp.gpa().destroy(comp.false_value);
|
||||
|
||||
comp.noreturn_value = try comp.a().create(Value.NoReturn{
|
||||
comp.noreturn_value = try comp.gpa().create(Value.NoReturn{
|
||||
.base = Value{
|
||||
.id = Value.Id.NoReturn,
|
||||
.typeof = &Type.NoReturn.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
errdefer comp.a().destroy(comp.noreturn_value);
|
||||
errdefer comp.gpa().destroy(comp.noreturn_value);
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Compilation) void {
|
||||
if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
|
||||
os.deleteTree(self.arena(), tmp_dir) catch {};
|
||||
} else |_| {};
|
||||
|
||||
self.noreturn_value.base.deref(self);
|
||||
self.void_value.base.deref(self);
|
||||
self.false_value.base.deref(self);
|
||||
|
@ -420,14 +536,18 @@ pub const Compilation = struct {
|
|||
self.meta_type.base.base.deref(self);
|
||||
|
||||
self.events.destroy();
|
||||
self.name.deinit();
|
||||
|
||||
self.a().destroy(self);
|
||||
llvm.DisposeMessage(self.target_layout_str);
|
||||
llvm.DisposeTargetData(self.target_data_ref);
|
||||
llvm.DisposeTargetMachine(self.target_machine);
|
||||
|
||||
self.arena_allocator.deinit();
|
||||
self.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn build(self: *Compilation) !void {
|
||||
if (self.llvm_argv.len != 0) {
|
||||
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{
|
||||
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
|
||||
[][]const u8{"zig (LLVM option parsing)"},
|
||||
self.llvm_argv,
|
||||
});
|
||||
|
@ -436,7 +556,7 @@ pub const Compilation = struct {
|
|||
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
|
||||
}
|
||||
|
||||
_ = try async<self.a()> self.buildAsync();
|
||||
_ = try async<self.gpa()> self.buildAsync();
|
||||
}
|
||||
|
||||
async fn buildAsync(self: *Compilation) void {
|
||||
|
@ -464,7 +584,7 @@ pub const Compilation = struct {
|
|||
}
|
||||
} else |err| {
|
||||
// if there's an error then the compile errors have dangling references
|
||||
self.a().free(compile_errors);
|
||||
self.gpa().free(compile_errors);
|
||||
|
||||
await (async self.events.put(Event{ .Error = err }) catch unreachable);
|
||||
}
|
||||
|
@ -477,26 +597,26 @@ pub const Compilation = struct {
|
|||
async fn addRootSrc(self: *Compilation) !void {
|
||||
const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
|
||||
// TODO async/await os.path.real
|
||||
const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
|
||||
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
|
||||
try printError("unable to get real path '{}': {}", root_src_path, err);
|
||||
return err;
|
||||
};
|
||||
errdefer self.a().free(root_src_real_path);
|
||||
errdefer self.gpa().free(root_src_real_path);
|
||||
|
||||
// TODO async/await readFileAlloc()
|
||||
const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
|
||||
const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
|
||||
try printError("unable to open '{}': {}", root_src_real_path, err);
|
||||
return err;
|
||||
};
|
||||
errdefer self.a().free(source_code);
|
||||
errdefer self.gpa().free(source_code);
|
||||
|
||||
const parsed_file = try self.a().create(ParsedFile{
|
||||
const parsed_file = try self.gpa().create(ParsedFile{
|
||||
.tree = undefined,
|
||||
.realpath = root_src_real_path,
|
||||
});
|
||||
errdefer self.a().destroy(parsed_file);
|
||||
errdefer self.gpa().destroy(parsed_file);
|
||||
|
||||
parsed_file.tree = try std.zig.parse(self.a(), source_code);
|
||||
parsed_file.tree = try std.zig.parse(self.gpa(), source_code);
|
||||
errdefer parsed_file.tree.deinit();
|
||||
|
||||
const tree = &parsed_file.tree;
|
||||
|
@ -525,7 +645,7 @@ pub const Compilation = struct {
|
|||
continue;
|
||||
};
|
||||
|
||||
const fn_decl = try self.a().create(Decl.Fn{
|
||||
const fn_decl = try self.gpa().create(Decl.Fn{
|
||||
.base = Decl{
|
||||
.id = Decl.Id.Fn,
|
||||
.name = name,
|
||||
|
@ -538,7 +658,7 @@ pub const Compilation = struct {
|
|||
.value = Decl.Fn.Val{ .Unresolved = {} },
|
||||
.fn_proto = fn_proto,
|
||||
});
|
||||
errdefer self.a().destroy(fn_decl);
|
||||
errdefer self.gpa().destroy(fn_decl);
|
||||
|
||||
try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
|
||||
},
|
||||
|
@ -547,15 +667,15 @@ pub const Compilation = struct {
|
|||
}
|
||||
}
|
||||
try await (async decl_group.wait() catch unreachable);
|
||||
try await (async self.build_group.wait() catch unreachable);
|
||||
try await (async self.prelink_group.wait() catch unreachable);
|
||||
}
|
||||
|
||||
async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
|
||||
const is_export = decl.isExported(&decl.parsed_file.tree);
|
||||
|
||||
if (is_export) {
|
||||
try self.build_group.call(verifyUniqueSymbol, self, decl);
|
||||
try self.build_group.call(resolveDecl, self, decl);
|
||||
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
|
||||
try self.prelink_group.call(resolveDecl, self, decl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,7 +683,7 @@ pub const Compilation = struct {
|
|||
const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
|
||||
errdefer self.loop.allocator.free(text);
|
||||
|
||||
try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text);
|
||||
try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text);
|
||||
}
|
||||
|
||||
async fn addCompileErrorAsync(
|
||||
|
@ -625,11 +745,11 @@ pub const Compilation = struct {
|
|||
}
|
||||
}
|
||||
|
||||
const link_lib = try self.a().create(LinkLib{
|
||||
const link_lib = try self.gpa().create(LinkLib{
|
||||
.name = name,
|
||||
.path = null,
|
||||
.provided_explicitly = provided_explicitly,
|
||||
.symbols = ArrayList([]u8).init(self.a()),
|
||||
.symbols = ArrayList([]u8).init(self.gpa()),
|
||||
});
|
||||
try self.link_libs_list.append(link_lib);
|
||||
if (is_libc) {
|
||||
|
@ -638,9 +758,71 @@ pub const Compilation = struct {
|
|||
return link_lib;
|
||||
}
|
||||
|
||||
fn a(self: Compilation) *mem.Allocator {
|
||||
/// General Purpose Allocator. Must free when done.
|
||||
fn gpa(self: Compilation) *mem.Allocator {
|
||||
return self.loop.allocator;
|
||||
}
|
||||
|
||||
/// Arena Allocator. Automatically freed when the Compilation is destroyed.
|
||||
fn arena(self: *Compilation) *mem.Allocator {
|
||||
return &self.arena_allocator.allocator;
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
const tmp_dir = try await (async self.getTmpDir() catch unreachable);
|
||||
const file_prefix = await (async self.getRandomFileName() catch unreachable);
|
||||
|
||||
const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
|
||||
defer self.gpa().free(file_name);
|
||||
|
||||
const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]);
|
||||
errdefer self.gpa().free(full_path);
|
||||
|
||||
return Buffer.fromOwnedSlice(self.gpa(), full_path);
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*;
|
||||
self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable);
|
||||
self.tmp_dir.resolve();
|
||||
return self.tmp_dir.data;
|
||||
}
|
||||
|
||||
async fn getTmpDirImpl(self: *Compilation) ![]u8 {
|
||||
const comp_dir_name = await (async self.getRandomFileName() catch unreachable);
|
||||
const zig_dir_path = try getZigDir(self.gpa());
|
||||
defer self.gpa().free(zig_dir_path);
|
||||
|
||||
const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]);
|
||||
try os.makePath(self.gpa(), tmp_dir);
|
||||
return tmp_dir;
|
||||
}
|
||||
|
||||
async 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-_",
|
||||
std.base64.standard_pad_char,
|
||||
);
|
||||
|
||||
var rand_bytes: [9]u8 = undefined;
|
||||
|
||||
{
|
||||
const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
held.value.random.bytes(rand_bytes[0..]);
|
||||
}
|
||||
|
||||
var result: [12]u8 = undefined;
|
||||
b64_fs_encoder.encode(result[0..], rand_bytes);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
fn printError(comptime format: []const u8, args: ...) !void {
|
||||
|
@ -662,13 +844,11 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
|
|||
|
||||
/// This declaration has been blessed as going into the final code generation.
|
||||
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
|
||||
decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
} else {
|
||||
return (await (async decl.resolution.get() catch unreachable)).*;
|
||||
}
|
||||
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
}
|
||||
|
||||
/// The function that actually does the generation.
|
||||
|
@ -698,7 +878,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
|||
const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
|
||||
defer fn_type.base.base.deref(comp);
|
||||
|
||||
var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name);
|
||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||
errdefer symbol_name.deinit();
|
||||
|
||||
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
|
||||
|
@ -719,7 +899,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
|||
error.SemanticAnalysisFailed => return {},
|
||||
else => return err,
|
||||
};
|
||||
defer unanalyzed_code.destroy(comp.a());
|
||||
defer unanalyzed_code.destroy(comp.gpa());
|
||||
|
||||
if (comp.verbose_ir) {
|
||||
std.debug.warn("unanalyzed:\n");
|
||||
|
@ -738,7 +918,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
|||
error.SemanticAnalysisFailed => return {},
|
||||
else => return err,
|
||||
};
|
||||
errdefer analyzed_code.destroy(comp.a());
|
||||
errdefer analyzed_code.destroy(comp.gpa());
|
||||
|
||||
if (comp.verbose_ir) {
|
||||
std.debug.warn("analyzed:\n");
|
||||
|
@ -747,5 +927,30 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
|||
|
||||
// Kick off rendering to LLVM module, but it doesn't block the fn decl
|
||||
// analysis from being complete.
|
||||
try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
|
||||
try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
|
||||
try comp.prelink_group.call(addFnToLinkSet, comp, fn_val);
|
||||
}
|
||||
|
||||
async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
|
||||
fn_val.base.ref();
|
||||
defer fn_val.base.deref(comp);
|
||||
|
||||
fn_val.link_set_node.data = fn_val;
|
||||
|
||||
const held = await (async comp.fn_link_set.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
held.value.append(fn_val.link_set_node);
|
||||
}
|
||||
|
||||
fn getZigDir(allocator: *mem.Allocator) ![]u8 {
|
||||
const home_dir = try getHomeDir(allocator);
|
||||
defer allocator.free(home_dir);
|
||||
|
||||
return os.path.join(allocator, home_dir, ".zig");
|
||||
}
|
||||
|
||||
/// TODO move to zig std lib, and make it work for other OSes
|
||||
fn getHomeDir(allocator: *mem.Allocator) ![]u8 {
|
||||
return os.getEnvVarOwned(allocator, "HOME");
|
||||
}
|
||||
|
|
|
@ -453,7 +453,7 @@ pub const Code = struct {
|
|||
arena: std.heap.ArenaAllocator,
|
||||
return_type: ?*Type,
|
||||
|
||||
/// allocator is comp.a()
|
||||
/// allocator is comp.gpa()
|
||||
pub fn destroy(self: *Code, allocator: *Allocator) void {
|
||||
self.arena.deinit();
|
||||
allocator.destroy(self);
|
||||
|
@ -483,13 +483,13 @@ pub const Builder = struct {
|
|||
pub const Error = Analyze.Error;
|
||||
|
||||
pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder {
|
||||
const code = try comp.a().create(Code{
|
||||
const code = try comp.gpa().create(Code{
|
||||
.basic_block_list = undefined,
|
||||
.arena = std.heap.ArenaAllocator.init(comp.a()),
|
||||
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
|
||||
.return_type = null,
|
||||
});
|
||||
code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
|
||||
errdefer code.destroy(comp.a());
|
||||
errdefer code.destroy(comp.gpa());
|
||||
|
||||
return Builder{
|
||||
.comp = comp,
|
||||
|
@ -502,7 +502,7 @@ pub const Builder = struct {
|
|||
}
|
||||
|
||||
pub fn abort(self: *Builder) void {
|
||||
self.code.destroy(self.comp.a());
|
||||
self.code.destroy(self.comp.gpa());
|
||||
}
|
||||
|
||||
/// Call code.destroy() when done
|
||||
|
|
|
@ -2,6 +2,12 @@ const builtin = @import("builtin");
|
|||
const c = @import("c.zig");
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
// we wrap the c module for 3 reasons:
|
||||
// 1. to avoid accidentally calling the non-thread-safe functions
|
||||
// 2. patch up some of the types to remove nullability
|
||||
// 3. some functions have been augmented by zig_llvm.cpp to be more powerful,
|
||||
// such as ZigLLVMTargetMachineEmitToFile
|
||||
|
||||
pub const AttributeIndex = c_uint;
|
||||
pub const Bool = c_int;
|
||||
|
||||
|
@ -12,25 +18,51 @@ pub const ValueRef = removeNullability(c.LLVMValueRef);
|
|||
pub const TypeRef = removeNullability(c.LLVMTypeRef);
|
||||
pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
|
||||
pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
|
||||
pub const TargetRef = removeNullability(c.LLVMTargetRef);
|
||||
pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
|
||||
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
|
||||
pub const DIBuilder = c.ZigLLVMDIBuilder;
|
||||
|
||||
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
|
||||
pub const AddFunction = c.LLVMAddFunction;
|
||||
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
|
||||
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
|
||||
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
|
||||
pub const ConstAllOnes = c.LLVMConstAllOnes;
|
||||
pub const ConstInt = c.LLVMConstInt;
|
||||
pub const ConstNull = c.LLVMConstNull;
|
||||
pub const ConstStringInContext = c.LLVMConstStringInContext;
|
||||
pub const ConstStructInContext = c.LLVMConstStructInContext;
|
||||
pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
|
||||
pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
|
||||
pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
|
||||
pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
|
||||
pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
|
||||
pub const CreateFile = c.ZigLLVMCreateFile;
|
||||
pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
|
||||
pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
|
||||
pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
|
||||
pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
|
||||
pub const DisposeBuilder = c.LLVMDisposeBuilder;
|
||||
pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
|
||||
pub const DisposeMessage = c.LLVMDisposeMessage;
|
||||
pub const DisposeModule = c.LLVMDisposeModule;
|
||||
pub const DisposeTargetData = c.LLVMDisposeTargetData;
|
||||
pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine;
|
||||
pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;
|
||||
pub const DumpModule = c.LLVMDumpModule;
|
||||
pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
|
||||
pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
|
||||
pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
|
||||
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
|
||||
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
|
||||
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
|
||||
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
|
||||
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
|
||||
pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
|
||||
pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos;
|
||||
pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs;
|
||||
pub const InitializeAllTargets = c.LLVMInitializeAllTargets;
|
||||
pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;
|
||||
pub const Int128TypeInContext = c.LLVMInt128TypeInContext;
|
||||
pub const Int16TypeInContext = c.LLVMInt16TypeInContext;
|
||||
|
@ -47,13 +79,16 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
|
|||
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
|
||||
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
|
||||
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
|
||||
pub const SetDataLayout = c.LLVMSetDataLayout;
|
||||
pub const SetTarget = c.LLVMSetTarget;
|
||||
pub const StructTypeInContext = c.LLVMStructTypeInContext;
|
||||
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
|
||||
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
|
||||
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
|
||||
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
|
||||
pub const ConstAllOnes = c.LLVMConstAllOnes;
|
||||
pub const ConstNull = c.LLVMConstNull;
|
||||
|
||||
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
|
||||
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
|
||||
|
||||
pub const VerifyModule = LLVMVerifyModule;
|
||||
extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
|
||||
|
@ -83,6 +118,31 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
|
|||
pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;
|
||||
pub const VerifierFailureAction = c.LLVMVerifierFailureAction;
|
||||
|
||||
pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone;
|
||||
pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess;
|
||||
pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault;
|
||||
pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive;
|
||||
pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel;
|
||||
|
||||
pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault;
|
||||
pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic;
|
||||
pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC;
|
||||
pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic;
|
||||
pub const RelocMode = c.LLVMRelocMode;
|
||||
|
||||
pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault;
|
||||
pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault;
|
||||
pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall;
|
||||
pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel;
|
||||
pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium;
|
||||
pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge;
|
||||
pub const CodeModel = c.LLVMCodeModel;
|
||||
|
||||
pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly;
|
||||
pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
|
||||
pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
|
||||
pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
|
||||
|
||||
fn removeNullability(comptime T: type) type {
|
||||
comptime assert(@typeId(T) == builtin.TypeId.Optional);
|
||||
return T.Child;
|
||||
|
@ -90,3 +150,14 @@ fn removeNullability(comptime T: type) type {
|
|||
|
||||
pub const BuildRet = LLVMBuildRet;
|
||||
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;
|
||||
|
||||
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
|
||||
extern fn ZigLLVMTargetMachineEmitToFile(
|
||||
targ_machine_ref: TargetMachineRef,
|
||||
module_ref: ModuleRef,
|
||||
filename: [*]const u8,
|
||||
output_type: EmitOutputType,
|
||||
error_message: *[*]u8,
|
||||
is_debug: bool,
|
||||
is_small: bool,
|
||||
) bool;
|
||||
|
|
|
@ -363,6 +363,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
}
|
||||
};
|
||||
|
||||
const is_static = flags.present("static");
|
||||
|
||||
const assembly_files = flags.many("assembly");
|
||||
const link_objects = flags.many("object");
|
||||
if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {
|
||||
|
@ -389,7 +391,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
try loop.initMultiThreaded(allocator);
|
||||
defer loop.deinit();
|
||||
|
||||
var event_loop_local = EventLoopLocal.init(&loop);
|
||||
var event_loop_local = try EventLoopLocal.init(&loop);
|
||||
defer event_loop_local.deinit();
|
||||
|
||||
var comp = try Compilation.create(
|
||||
|
@ -399,6 +401,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
Target.Native,
|
||||
out_type,
|
||||
build_mode,
|
||||
is_static,
|
||||
zig_lib_dir,
|
||||
full_cache_dir,
|
||||
);
|
||||
|
@ -426,7 +429,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
comp.clang_argv = clang_argv_buf.toSliceConst();
|
||||
|
||||
comp.strip = flags.present("strip");
|
||||
comp.is_static = flags.present("static");
|
||||
|
||||
if (flags.single("libc-lib-dir")) |libc_lib_dir| {
|
||||
comp.libc_lib_dir = libc_lib_dir;
|
||||
|
@ -481,9 +483,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
}
|
||||
|
||||
comp.emit_file_type = emit_type;
|
||||
comp.link_objects = link_objects;
|
||||
comp.assembly_files = assembly_files;
|
||||
comp.link_out_file = flags.single("out-file");
|
||||
comp.link_objects = link_objects;
|
||||
|
||||
try comp.build();
|
||||
const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
const Buffer = std.Buffer;
|
||||
|
||||
pub const Package = struct {
|
||||
root_src_dir: Buffer,
|
||||
root_src_path: Buffer,
|
||||
|
||||
/// relative to root_src_dir
|
||||
table: Table,
|
||||
|
||||
pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||
|
||||
/// makes internal copies of root_src_dir and root_src_path
|
||||
/// allocator should be an arena allocator because Package never frees anything
|
||||
pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
|
||||
return allocator.create(Package{
|
||||
.root_src_dir = try Buffer.init(allocator, root_src_dir),
|
||||
.root_src_path = try Buffer.init(allocator, root_src_path),
|
||||
.table = Table.init(allocator),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add(self: *Package, name: []const u8, package: *Package) !void {
|
||||
const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package);
|
||||
assert(entry == null);
|
||||
}
|
||||
};
|
|
@ -64,7 +64,7 @@ pub const Scope = struct {
|
|||
|
||||
/// Creates a Decls scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls {
|
||||
const self = try comp.a().create(Decls{
|
||||
const self = try comp.gpa().create(Decls{
|
||||
.base = Scope{
|
||||
.id = Id.Decls,
|
||||
.parent = parent,
|
||||
|
@ -72,9 +72,9 @@ pub const Scope = struct {
|
|||
},
|
||||
.table = undefined,
|
||||
});
|
||||
errdefer comp.a().destroy(self);
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
self.table = Decl.Table.init(comp.a());
|
||||
self.table = Decl.Table.init(comp.gpa());
|
||||
errdefer self.table.deinit();
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
|
@ -126,7 +126,7 @@ pub const Scope = struct {
|
|||
|
||||
/// Creates a Block scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*Block {
|
||||
const self = try comp.a().create(Block{
|
||||
const self = try comp.gpa().create(Block{
|
||||
.base = Scope{
|
||||
.id = Id.Block,
|
||||
.parent = parent,
|
||||
|
@ -138,14 +138,14 @@ pub const Scope = struct {
|
|||
.is_comptime = undefined,
|
||||
.safety = Safety.Auto,
|
||||
});
|
||||
errdefer comp.a().destroy(self);
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Block, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -158,7 +158,7 @@ pub const Scope = struct {
|
|||
/// Creates a FnDef scope with 1 reference
|
||||
/// Must set the fn_val later
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {
|
||||
const self = try comp.a().create(FnDef{
|
||||
const self = try comp.gpa().create(FnDef{
|
||||
.base = Scope{
|
||||
.id = Id.FnDef,
|
||||
.parent = parent,
|
||||
|
@ -173,7 +173,7 @@ pub const Scope = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *FnDef, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -182,7 +182,7 @@ pub const Scope = struct {
|
|||
|
||||
/// Creates a CompTime scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime {
|
||||
const self = try comp.a().create(CompTime{
|
||||
const self = try comp.gpa().create(CompTime{
|
||||
.base = Scope{
|
||||
.id = Id.CompTime,
|
||||
.parent = parent,
|
||||
|
@ -195,7 +195,7 @@ pub const Scope = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *CompTime, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -216,7 +216,7 @@ pub const Scope = struct {
|
|||
kind: Kind,
|
||||
defer_expr_scope: *DeferExpr,
|
||||
) !*Defer {
|
||||
const self = try comp.a().create(Defer{
|
||||
const self = try comp.gpa().create(Defer{
|
||||
.base = Scope{
|
||||
.id = Id.Defer,
|
||||
.parent = parent,
|
||||
|
@ -225,7 +225,7 @@ pub const Scope = struct {
|
|||
.defer_expr_scope = defer_expr_scope,
|
||||
.kind = kind,
|
||||
});
|
||||
errdefer comp.a().destroy(self);
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
defer_expr_scope.base.ref();
|
||||
|
||||
|
@ -235,7 +235,7 @@ pub const Scope = struct {
|
|||
|
||||
pub fn destroy(self: *Defer, comp: *Compilation) void {
|
||||
self.defer_expr_scope.base.deref(comp);
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -245,7 +245,7 @@ pub const Scope = struct {
|
|||
|
||||
/// Creates a DeferExpr scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
|
||||
const self = try comp.a().create(DeferExpr{
|
||||
const self = try comp.gpa().create(DeferExpr{
|
||||
.base = Scope{
|
||||
.id = Id.DeferExpr,
|
||||
.parent = parent,
|
||||
|
@ -253,14 +253,14 @@ pub const Scope = struct {
|
|||
},
|
||||
.expr_node = expr_node,
|
||||
});
|
||||
errdefer comp.a().destroy(self);
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,60 +1,118 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const c = @import("c.zig");
|
||||
|
||||
pub const CrossTarget = struct {
|
||||
arch: builtin.Arch,
|
||||
os: builtin.Os,
|
||||
environ: builtin.Environ,
|
||||
};
|
||||
const llvm = @import("llvm.zig");
|
||||
|
||||
pub const Target = union(enum) {
|
||||
Native,
|
||||
Cross: CrossTarget,
|
||||
Cross: Cross,
|
||||
|
||||
pub fn oFileExt(self: *const Target) []const u8 {
|
||||
const environ = switch (self.*) {
|
||||
Target.Native => builtin.environ,
|
||||
Target.Cross => |t| t.environ,
|
||||
};
|
||||
return switch (environ) {
|
||||
builtin.Environ.msvc => ".obj",
|
||||
pub const Cross = struct {
|
||||
arch: builtin.Arch,
|
||||
os: builtin.Os,
|
||||
environ: builtin.Environ,
|
||||
object_format: builtin.ObjectFormat,
|
||||
};
|
||||
|
||||
pub fn oFileExt(self: Target) []const u8 {
|
||||
return switch (self.getObjectFormat()) {
|
||||
builtin.ObjectFormat.coff => ".obj",
|
||||
else => ".o",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn exeFileExt(self: *const Target) []const u8 {
|
||||
pub fn exeFileExt(self: Target) []const u8 {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.windows => ".exe",
|
||||
else => "",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getOs(self: *const Target) builtin.Os {
|
||||
return switch (self.*) {
|
||||
pub fn getOs(self: Target) builtin.Os {
|
||||
return switch (self) {
|
||||
Target.Native => builtin.os,
|
||||
Target.Cross => |t| t.os,
|
||||
@TagType(Target).Cross => |t| t.os,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isDarwin(self: *const Target) bool {
|
||||
pub fn getArch(self: Target) builtin.Arch {
|
||||
return switch (self) {
|
||||
Target.Native => builtin.arch,
|
||||
@TagType(Target).Cross => |t| t.arch,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getEnviron(self: Target) builtin.Environ {
|
||||
return switch (self) {
|
||||
Target.Native => builtin.environ,
|
||||
@TagType(Target).Cross => |t| t.environ,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getObjectFormat(self: Target) builtin.ObjectFormat {
|
||||
return switch (self) {
|
||||
Target.Native => builtin.object_format,
|
||||
@TagType(Target).Cross => |t| t.object_format,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isWasm(self: Target) bool {
|
||||
return switch (self.getArch()) {
|
||||
builtin.Arch.wasm32, builtin.Arch.wasm64 => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isDarwin(self: Target) bool {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.ios, builtin.Os.macosx => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isWindows(self: *const Target) bool {
|
||||
pub fn isWindows(self: Target) bool {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.windows => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn initializeAll() void {
|
||||
c.LLVMInitializeAllTargets();
|
||||
c.LLVMInitializeAllTargetInfos();
|
||||
c.LLVMInitializeAllTargetMCs();
|
||||
c.LLVMInitializeAllAsmPrinters();
|
||||
c.LLVMInitializeAllAsmParsers();
|
||||
}
|
||||
pub fn initializeAll() void {
|
||||
llvm.InitializeAllTargets();
|
||||
llvm.InitializeAllTargetInfos();
|
||||
llvm.InitializeAllTargetMCs();
|
||||
llvm.InitializeAllAsmPrinters();
|
||||
llvm.InitializeAllAsmParsers();
|
||||
}
|
||||
|
||||
pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer {
|
||||
var result = try std.Buffer.initSize(allocator, 0);
|
||||
errdefer result.deinit();
|
||||
|
||||
// LLVM WebAssembly output support requires the target to be activated at
|
||||
// build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
|
||||
//
|
||||
// LLVM determines the output format based on the environment suffix,
|
||||
// defaulting to an object based on the architecture. The default format in
|
||||
// LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
|
||||
// explicitly set this ourself in order for it to work.
|
||||
//
|
||||
// This is fixed in LLVM 7 and you will be able to get wasm output by
|
||||
// using the target triple `wasm32-unknown-unknown-unknown`.
|
||||
const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron());
|
||||
|
||||
var out = &std.io.BufferOutStream.init(&result).stream;
|
||||
try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
|
||||
var result: llvm.TargetRef = undefined;
|
||||
var err_msg: [*]u8 = undefined;
|
||||
if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
|
||||
std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);
|
||||
return error.UnsupportedTarget;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@ pub const TestContext = struct {
|
|||
try self.loop.initMultiThreaded(allocator);
|
||||
errdefer self.loop.deinit();
|
||||
|
||||
self.event_loop_local = EventLoopLocal.init(&self.loop);
|
||||
self.event_loop_local = try EventLoopLocal.init(&self.loop);
|
||||
errdefer self.event_loop_local.deinit();
|
||||
|
||||
self.group = std.event.Group(error!void).init(&self.loop);
|
||||
|
@ -107,6 +107,7 @@ pub const TestContext = struct {
|
|||
Target.Native,
|
||||
Compilation.Kind.Obj,
|
||||
builtin.Mode.Debug,
|
||||
true, // is_static
|
||||
self.zig_lib_dir,
|
||||
self.zig_cache_dir,
|
||||
);
|
||||
|
|
|
@ -160,7 +160,7 @@ pub const Type = struct {
|
|||
decls: *Scope.Decls,
|
||||
|
||||
pub fn destroy(self: *Struct, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -180,7 +180,7 @@ pub const Type = struct {
|
|||
};
|
||||
|
||||
pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
|
||||
const result = try comp.a().create(Fn{
|
||||
const result = try comp.gpa().create(Fn{
|
||||
.base = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
|
@ -193,7 +193,7 @@ pub const Type = struct {
|
|||
.params = params,
|
||||
.is_var_args = is_var_args,
|
||||
});
|
||||
errdefer comp.a().destroy(result);
|
||||
errdefer comp.gpa().destroy(result);
|
||||
|
||||
result.return_type.base.ref();
|
||||
for (result.params) |param| {
|
||||
|
@ -207,7 +207,7 @@ pub const Type = struct {
|
|||
for (self.params) |param| {
|
||||
param.typeof.base.deref(comp);
|
||||
}
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {
|
||||
|
@ -215,8 +215,8 @@ pub const Type = struct {
|
|||
Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
|
||||
else => try self.return_type.getLlvmType(ofile),
|
||||
};
|
||||
const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len);
|
||||
defer ofile.a().free(llvm_param_types);
|
||||
const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len);
|
||||
defer ofile.gpa().free(llvm_param_types);
|
||||
for (llvm_param_types) |*llvm_param_type, i| {
|
||||
llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ pub const Type = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *MetaType, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -255,7 +255,7 @@ pub const Type = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *Void, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -269,7 +269,7 @@ pub const Type = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *Bool, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -287,7 +287,7 @@ pub const Type = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *NoReturn, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -295,7 +295,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Int, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -307,7 +307,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Float, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -332,7 +332,7 @@ pub const Type = struct {
|
|||
pub const Size = builtin.TypeInfo.Pointer.Size;
|
||||
|
||||
pub fn destroy(self: *Pointer, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
|
@ -355,7 +355,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Array, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -367,7 +367,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -375,7 +375,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -383,7 +383,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Undefined, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -391,7 +391,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Null, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -399,7 +399,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Optional, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -411,7 +411,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -423,7 +423,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -435,7 +435,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Enum, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -447,7 +447,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Union, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -459,7 +459,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Namespace, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -467,7 +467,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Block, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -475,7 +475,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *BoundFn, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -487,7 +487,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *ArgTuple, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -495,7 +495,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Opaque, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
@ -507,7 +507,7 @@ pub const Type = struct {
|
|||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Promise, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {
|
||||
|
|
|
@ -4,6 +4,7 @@ const Scope = @import("scope.zig").Scope;
|
|||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const ObjectFile = @import("codegen.zig").ObjectFile;
|
||||
const llvm = @import("llvm.zig");
|
||||
const Buffer = std.Buffer;
|
||||
|
||||
/// Values are ref-counted, heap-allocated, and copy-on-write
|
||||
/// If there is only 1 ref then write need not copy
|
||||
|
@ -68,7 +69,7 @@ pub const Value = struct {
|
|||
|
||||
/// The main external name that is used in the .o file.
|
||||
/// TODO https://github.com/ziglang/zig/issues/265
|
||||
symbol_name: std.Buffer,
|
||||
symbol_name: Buffer,
|
||||
|
||||
/// parent should be the top level decls or container decls
|
||||
fndef_scope: *Scope.FnDef,
|
||||
|
@ -79,10 +80,22 @@ pub const Value = struct {
|
|||
/// parent is child_scope
|
||||
block_scope: *Scope.Block,
|
||||
|
||||
/// Path to the object file that contains this function
|
||||
containing_object: Buffer,
|
||||
|
||||
link_set_node: *std.LinkedList(?*Value.Fn).Node,
|
||||
|
||||
/// Creates a Fn value with 1 ref
|
||||
/// Takes ownership of symbol_name
|
||||
pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn {
|
||||
const self = try comp.a().create(Fn{
|
||||
pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
|
||||
const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
|
||||
.data = null,
|
||||
.next = undefined,
|
||||
.prev = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(link_set_node);
|
||||
|
||||
const self = try comp.gpa().create(Fn{
|
||||
.base = Value{
|
||||
.id = Value.Id.Fn,
|
||||
.typeof = &fn_type.base,
|
||||
|
@ -92,6 +105,8 @@ pub const Value = struct {
|
|||
.child_scope = &fndef_scope.base,
|
||||
.block_scope = undefined,
|
||||
.symbol_name = symbol_name,
|
||||
.containing_object = Buffer.initNull(comp.gpa()),
|
||||
.link_set_node = link_set_node,
|
||||
});
|
||||
fn_type.base.base.ref();
|
||||
fndef_scope.fn_val = self;
|
||||
|
@ -100,9 +115,19 @@ pub const Value = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *Fn, comp: *Compilation) void {
|
||||
// remove with a tombstone so that we do not have to grab a lock
|
||||
if (self.link_set_node.data != null) {
|
||||
// it's now the job of the link step to find this tombstone and
|
||||
// deallocate it.
|
||||
self.link_set_node.data = null;
|
||||
} else {
|
||||
comp.gpa().destroy(self.link_set_node);
|
||||
}
|
||||
|
||||
self.containing_object.deinit();
|
||||
self.fndef_scope.base.deref(comp);
|
||||
self.symbol_name.deinit();
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -115,7 +140,7 @@ pub const Value = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *Void, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -134,7 +159,7 @@ pub const Value = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *Bool, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
|
||||
|
@ -156,7 +181,7 @@ pub const Value = struct {
|
|||
}
|
||||
|
||||
pub fn destroy(self: *NoReturn, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -170,7 +195,7 @@ pub const Value = struct {
|
|||
};
|
||||
|
||||
pub fn destroy(self: *Ptr, comp: *Compilation) void {
|
||||
comp.a().destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -440,6 +440,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso
|
|||
return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
|
||||
}
|
||||
|
||||
void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) {
|
||||
DIBuilder *di_builder = reinterpret_cast<DIBuilder *>(dbuilder);
|
||||
delete di_builder;
|
||||
}
|
||||
|
||||
void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {
|
||||
unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get(
|
||||
line, column, reinterpret_cast<DIScope*>(scope)));
|
||||
|
|
|
@ -39,7 +39,7 @@ struct ZigLLVMInsertionPoint;
|
|||
ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
|
||||
ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
|
||||
|
||||
/// Caller must free memory.
|
||||
/// Caller must free memory with LLVMDisposeMessage
|
||||
ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);
|
||||
ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
|
||||
|
||||
|
@ -139,6 +139,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
|
|||
ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
|
||||
|
||||
ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
|
||||
ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder);
|
||||
ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
|
||||
ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module);
|
||||
|
||||
|
|
|
@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type {
|
|||
pub fn get(self: *Self) T {
|
||||
return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
pub fn xchg(self: *Self, new_value: T) T {
|
||||
return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -54,6 +54,19 @@ pub const Buffer = struct {
|
|||
return result;
|
||||
}
|
||||
|
||||
pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer {
|
||||
const countSize = struct {
|
||||
fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
|
||||
size.* += bytes.len;
|
||||
}
|
||||
}.countSize;
|
||||
var size: usize = 0;
|
||||
std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {};
|
||||
var self = try Buffer.initSize(allocator, size);
|
||||
assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Buffer) void {
|
||||
self.list.deinit();
|
||||
}
|
||||
|
|
|
@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03;
|
|||
pub const LNE_set_discriminator = 0x04;
|
||||
pub const LNE_lo_user = 0x80;
|
||||
pub const LNE_hi_user = 0xff;
|
||||
|
||||
pub const LANG_C89 = 0x0001;
|
||||
pub const LANG_C = 0x0002;
|
||||
pub const LANG_Ada83 = 0x0003;
|
||||
pub const LANG_C_plus_plus = 0x0004;
|
||||
pub const LANG_Cobol74 = 0x0005;
|
||||
pub const LANG_Cobol85 = 0x0006;
|
||||
pub const LANG_Fortran77 = 0x0007;
|
||||
pub const LANG_Fortran90 = 0x0008;
|
||||
pub const LANG_Pascal83 = 0x0009;
|
||||
pub const LANG_Modula2 = 0x000a;
|
||||
pub const LANG_Java = 0x000b;
|
||||
pub const LANG_C99 = 0x000c;
|
||||
pub const LANG_Ada95 = 0x000d;
|
||||
pub const LANG_Fortran95 = 0x000e;
|
||||
pub const LANG_PLI = 0x000f;
|
||||
pub const LANG_ObjC = 0x0010;
|
||||
pub const LANG_ObjC_plus_plus = 0x0011;
|
||||
pub const LANG_UPC = 0x0012;
|
||||
pub const LANG_D = 0x0013;
|
||||
pub const LANG_Python = 0x0014;
|
||||
pub const LANG_Go = 0x0016;
|
||||
pub const LANG_C_plus_plus_11 = 0x001a;
|
||||
pub const LANG_Rust = 0x001c;
|
||||
pub const LANG_C11 = 0x001d;
|
||||
pub const LANG_C_plus_plus_14 = 0x0021;
|
||||
pub const LANG_Fortran03 = 0x0022;
|
||||
pub const LANG_Fortran08 = 0x0023;
|
||||
pub const LANG_lo_user = 0x8000;
|
||||
pub const LANG_hi_user = 0xffff;
|
||||
pub const LANG_Mips_Assembler = 0x8001;
|
||||
pub const LANG_Upc = 0x8765;
|
||||
pub const LANG_HP_Bliss = 0x8003;
|
||||
pub const LANG_HP_Basic91 = 0x8004;
|
||||
pub const LANG_HP_Pascal91 = 0x8005;
|
||||
pub const LANG_HP_IMacro = 0x8006;
|
||||
pub const LANG_HP_Assembler = 0x8007;
|
||||
|
|
|
@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder;
|
|||
const Lock = std.event.Lock;
|
||||
const Loop = std.event.Loop;
|
||||
|
||||
/// This is a value that starts out unavailable, until a value is put().
|
||||
/// This is a value that starts out unavailable, until resolve() is called
|
||||
/// While it is unavailable, coroutines suspend when they try to get() it,
|
||||
/// and then are resumed when the value is put().
|
||||
/// At this point the value remains forever available, and another put() is not allowed.
|
||||
/// and then are resumed when resolve() is called.
|
||||
/// At this point the value remains forever available, and another resolve() is not allowed.
|
||||
pub fn Future(comptime T: type) type {
|
||||
return struct {
|
||||
lock: Lock,
|
||||
data: T,
|
||||
available: u8, // TODO make this a bool
|
||||
|
||||
/// TODO make this an enum
|
||||
/// 0 - not started
|
||||
/// 1 - started
|
||||
/// 2 - finished
|
||||
available: u8,
|
||||
|
||||
const Self = this;
|
||||
const Queue = std.atomic.Queue(promise);
|
||||
|
@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type {
|
|||
/// available.
|
||||
/// Thread-safe.
|
||||
pub async fn get(self: *Self) *T {
|
||||
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {
|
||||
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
|
||||
return &self.data;
|
||||
}
|
||||
const held = await (async self.lock.acquire() catch unreachable);
|
||||
|
@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type {
|
|||
/// Gets the data without waiting for it. If it's available, a pointer is
|
||||
/// returned. Otherwise, null is returned.
|
||||
pub fn getOrNull(self: *Self) ?*T {
|
||||
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {
|
||||
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
|
||||
return &self.data;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// If someone else has started working on the data, wait for them to complete
|
||||
/// and return a pointer to the data. Otherwise, return null, and the caller
|
||||
/// should start working on the data.
|
||||
/// It's not required to call start() before resolve() but it can be useful since
|
||||
/// this method is thread-safe.
|
||||
pub async fn start(self: *Self) ?*T {
|
||||
const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
|
||||
switch (state) {
|
||||
1 => {
|
||||
const held = await (async self.lock.acquire() catch unreachable);
|
||||
held.release();
|
||||
return &self.data;
|
||||
},
|
||||
2 => return &self.data,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the data become available. May be called only once.
|
||||
/// Before calling this, modify the `data` property.
|
||||
pub fn resolve(self: *Self) void {
|
||||
const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
assert(prev == 0); // put() called twice
|
||||
const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
|
||||
assert(prev == 0 or prev == 1); // resolve() called twice
|
||||
Lock.Held.release(Lock.Held{ .lock = &self.lock });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -36,6 +36,8 @@ pub const sort = @import("sort.zig");
|
|||
pub const unicode = @import("unicode.zig");
|
||||
pub const zig = @import("zig/index.zig");
|
||||
|
||||
pub const lazyInit = @import("lazy_init.zig").lazyInit;
|
||||
|
||||
test "std" {
|
||||
// run tests from these
|
||||
_ = @import("atomic/index.zig");
|
||||
|
@ -71,4 +73,5 @@ test "std" {
|
|||
_ = @import("sort.zig");
|
||||
_ = @import("unicode.zig");
|
||||
_ = @import("zig/index.zig");
|
||||
_ = @import("lazy_init.zig");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
/// Thread-safe initialization of global data.
|
||||
/// TODO use a mutex instead of a spinlock
|
||||
pub fn lazyInit(comptime T: type) LazyInit(T) {
|
||||
return LazyInit(T){
|
||||
.data = undefined,
|
||||
.state = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn LazyInit(comptime T: type) type {
|
||||
return struct {
|
||||
state: u8, // TODO make this an enum
|
||||
data: Data,
|
||||
|
||||
const Self = this;
|
||||
|
||||
// TODO this isn't working for void, investigate and then remove this special case
|
||||
const Data = if (@sizeOf(T) == 0) u8 else T;
|
||||
const Ptr = if (T == void) void else *T;
|
||||
|
||||
/// Returns a usable pointer to the initialized data,
|
||||
/// or returns null, indicating that the caller should
|
||||
/// perform the initialization and then call resolve().
|
||||
pub fn get(self: *Self) ?Ptr {
|
||||
while (true) {
|
||||
var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
|
||||
switch (state) {
|
||||
0 => continue,
|
||||
1 => {
|
||||
// TODO mutex instead of a spinlock
|
||||
continue;
|
||||
},
|
||||
2 => {
|
||||
if (@sizeOf(T) == 0) {
|
||||
return T(undefined);
|
||||
} else {
|
||||
return &self.data;
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(self: *Self) void {
|
||||
const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
|
||||
assert(prev == 1); // resolve() called twice
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var global_number = lazyInit(i32);
|
||||
|
||||
test "std.lazyInit" {
|
||||
if (global_number.get()) |_| @panic("bad") else {
|
||||
global_number.data = 1234;
|
||||
global_number.resolve();
|
||||
}
|
||||
if (global_number.get()) |x| {
|
||||
assert(x.* == 1234);
|
||||
} else {
|
||||
@panic("bad");
|
||||
}
|
||||
if (global_number.get()) |x| {
|
||||
assert(x.* == 1234);
|
||||
} else {
|
||||
@panic("bad");
|
||||
}
|
||||
}
|
||||
|
||||
var global_void = lazyInit(void);
|
||||
|
||||
test "std.lazyInit(void)" {
|
||||
if (global_void.get()) |_| @panic("bad") else {
|
||||
global_void.resolve();
|
||||
}
|
||||
assert(global_void.get() != null);
|
||||
assert(global_void.get() != null);
|
||||
}
|
Loading…
Reference in New Issue