stage2: compiling C objects with clang
* add target_util.zig which has ported code from src/target.cpp * Module gains an arena that owns memory used during initialization that has the same lifetime as the Module. Useful for constructing file paths and lists of strings that have mixed lifetimes. - The Module memory itself is allocated in this arena. init/deinit are modified to be create/destroy. - root_name moves to the arena and no longer needs manual free * implement the ability to invoke `zig clang` as a subprocess - there are lots of TODOs that should be solved before merging * Module now requires a Random object and zig_lib_dir * Module now requires a path to its own executable or any zig executable that can do `zig clang`. * Wire up more CLI options. * Module creates "zig-cache" directory and "tmp" and "o" subdirectories ("h" is created by the cache_hash) * stubbed out some of the things linker code needs to do with TODO prints * delete dead code for computing compiler id. the previous commit eliminated the need for it. * add `zig translate-c` CLI option but it's not fully hooked up yet. It should be possible for this to be fully wired up before merging this branch. * `zig targets` now uses canonical data for available_libcs
This commit is contained in:
parent
c99e34a00e
commit
193ad413f0
@ -10,6 +10,7 @@ const log = std.log.scoped(.module);
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const Target = std.Target;
|
||||
const target_util = @import("target.zig");
|
||||
const Package = @import("Package.zig");
|
||||
const link = @import("link.zig");
|
||||
const ir = @import("ir.zig");
|
||||
@ -26,6 +27,8 @@ const build_options = @import("build_options");
|
||||
|
||||
/// General-purpose allocator. Used for both temporary and long-term storage.
|
||||
gpa: *Allocator,
|
||||
/// Arena-allocated memory used during initialization. Should be untouched until deinit.
|
||||
arena_state: std.heap.ArenaAllocator.State,
|
||||
/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
|
||||
root_pkg: ?*Package,
|
||||
/// Module owns this resource.
|
||||
@ -85,6 +88,12 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
|
||||
root_name: []u8,
|
||||
keep_source_files_loaded: bool,
|
||||
use_clang: bool,
|
||||
sanitize_c: bool,
|
||||
/// When this is `true` it means invoking clang as a sub-process is expected to inherit
|
||||
/// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
|
||||
/// Otherwise we attempt to parse the error messages and expose them via the Module API.
|
||||
/// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`.
|
||||
clang_passthrough_mode: bool,
|
||||
|
||||
/// Error tags and their values, tag names are duped with mod.gpa.
|
||||
global_error_set: std.StringHashMapUnmanaged(u16) = .{},
|
||||
@ -92,6 +101,12 @@ global_error_set: std.StringHashMapUnmanaged(u16) = .{},
|
||||
c_source_files: []const []const u8,
|
||||
clang_argv: []const []const u8,
|
||||
cache: std.cache_hash.CacheHash,
|
||||
/// Path to own executable for invoking `zig clang`.
|
||||
self_exe_path: ?[]const u8,
|
||||
zig_lib_dir: []const u8,
|
||||
zig_cache_dir_path: []const u8,
|
||||
libc_include_dir_list: []const []const u8,
|
||||
rand: *std.rand.Random,
|
||||
|
||||
pub const InnerError = error{ OutOfMemory, AnalysisFail };
|
||||
|
||||
@ -913,10 +928,12 @@ pub const AllErrors = struct {
|
||||
};
|
||||
|
||||
pub const InitOptions = struct {
|
||||
target: std.Target,
|
||||
zig_lib_dir: []const u8,
|
||||
target: Target,
|
||||
root_name: []const u8,
|
||||
root_pkg: ?*Package,
|
||||
output_mode: std.builtin.OutputMode,
|
||||
rand: *std.rand.Random,
|
||||
bin_file_dir: ?std.fs.Dir = null,
|
||||
bin_file_path: []const u8,
|
||||
emit_h: ?[]const u8 = null,
|
||||
@ -932,8 +949,8 @@ pub const InitOptions = struct {
|
||||
framework_dirs: []const []const u8 = &[0][]const u8{},
|
||||
frameworks: []const []const u8 = &[0][]const u8{},
|
||||
system_libs: []const []const u8 = &[0][]const u8{},
|
||||
have_libc: bool = false,
|
||||
have_libcpp: bool = false,
|
||||
link_libc: bool = false,
|
||||
link_libcpp: bool = false,
|
||||
want_pic: ?bool = null,
|
||||
want_sanitize_c: ?bool = null,
|
||||
use_llvm: ?bool = null,
|
||||
@ -943,20 +960,32 @@ pub const InitOptions = struct {
|
||||
strip: bool = false,
|
||||
linker_script: ?[]const u8 = null,
|
||||
version_script: ?[]const u8 = null,
|
||||
disable_c_depfile: bool = false,
|
||||
override_soname: ?[]const u8 = null,
|
||||
linker_optimization: ?[]const u8 = null,
|
||||
linker_gc_sections: ?bool = null,
|
||||
function_sections: ?bool = null,
|
||||
linker_allow_shlib_undefined: ?bool = null,
|
||||
linker_bind_global_refs_locally: ?bool = null,
|
||||
disable_c_depfile: bool = false,
|
||||
linker_z_nodelete: bool = false,
|
||||
linker_z_defs: bool = false,
|
||||
clang_passthrough_mode: bool = false,
|
||||
stack_size_override: u64 = 0,
|
||||
self_exe_path: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
const root_name = try gpa.dupe(u8, options.root_name);
|
||||
errdefer gpa.free(root_name);
|
||||
pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
|
||||
const mod: *Module = mod: {
|
||||
// For allocations that have the same lifetime as Module. This arena is used only during this
|
||||
// initialization and then is freed in deinit().
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||
errdefer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
// We put the `Module` itself in the arena. Freeing the arena will free the module.
|
||||
// It's initialized later after we prepare the initialization options.
|
||||
const mod = try arena.create(Module);
|
||||
const root_name = try arena.dupe(u8, options.root_name);
|
||||
|
||||
const ofmt = options.object_format orelse options.target.getObjectFormat();
|
||||
|
||||
@ -973,7 +1002,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
options.c_source_files.len != 0 or
|
||||
options.frameworks.len != 0 or
|
||||
options.system_libs.len != 0 or
|
||||
options.have_libc or options.have_libcpp or
|
||||
options.link_libc or options.link_libcpp or
|
||||
options.linker_script != null or options.version_script != null)
|
||||
{
|
||||
break :blk true;
|
||||
@ -1000,6 +1029,8 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
.optimize_mode = options.optimize_mode,
|
||||
.use_lld = use_lld,
|
||||
.use_llvm = use_llvm,
|
||||
.link_libc = options.link_libc,
|
||||
.link_libcpp = options.link_libcpp,
|
||||
.objects = options.link_objects,
|
||||
.frameworks = options.frameworks,
|
||||
.framework_dirs = options.framework_dirs,
|
||||
@ -1007,9 +1038,11 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
.lib_dirs = options.lib_dirs,
|
||||
.rpath_list = options.rpath_list,
|
||||
.strip = options.strip,
|
||||
.function_sections = options.function_sections orelse false,
|
||||
});
|
||||
errdefer bin_file.destroy();
|
||||
|
||||
// We arena-allocate the root scope so there is no free needed.
|
||||
const root_scope = blk: {
|
||||
if (options.root_pkg) |root_pkg| {
|
||||
if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
|
||||
@ -1049,8 +1082,19 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
// find the same binary and incrementally update it even if the file contents changed.
|
||||
// TODO Look into storing this information in memory rather than on disk and solving
|
||||
// serialization/deserialization of *all* incremental compilation state in a more generic way.
|
||||
const cache_dir = if (options.root_pkg) |root_pkg| root_pkg.root_src_dir else std.fs.cwd();
|
||||
var cache = try std.cache_hash.CacheHash.init(gpa, cache_dir, "zig-cache");
|
||||
const cache_parent_dir = if (options.root_pkg) |root_pkg| root_pkg.root_src_dir else std.fs.cwd();
|
||||
var cache_dir = try cache_parent_dir.makeOpenPath("zig-cache", .{});
|
||||
defer cache_dir.close();
|
||||
|
||||
try cache_dir.makePath("tmp");
|
||||
try cache_dir.makePath("o");
|
||||
// We need this string because of sending paths to clang as a child process.
|
||||
const zig_cache_dir_path = if (options.root_pkg) |root_pkg|
|
||||
try std.fmt.allocPrint(arena, "{}" ++ std.fs.path.sep_str ++ "zig-cache", .{root_pkg.root_src_dir_path})
|
||||
else
|
||||
"zig-cache";
|
||||
|
||||
var cache = try std.cache_hash.CacheHash.init(gpa, cache_dir, "h");
|
||||
errdefer cache.release();
|
||||
|
||||
// Now we will prepare hash state initializations to avoid redundantly computing hashes.
|
||||
@ -1100,13 +1144,48 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
// It's not planned to do our own translate-c or C compilation.
|
||||
break :blk true;
|
||||
};
|
||||
var c_object_table = std.AutoArrayHashMapUnmanaged(*CObject, void){};
|
||||
errdefer {
|
||||
for (c_object_table.items()) |entry| entry.key.destroy(gpa);
|
||||
c_object_table.deinit(gpa);
|
||||
}
|
||||
|
||||
const libc_include_dir_list = try detectLibCIncludeDirs(
|
||||
arena,
|
||||
options.zig_lib_dir,
|
||||
options.target,
|
||||
options.link_libc,
|
||||
);
|
||||
|
||||
const sanitize_c: bool = options.want_sanitize_c orelse switch (options.optimize_mode) {
|
||||
.Debug, .ReleaseSafe => true,
|
||||
.ReleaseSmall, .ReleaseFast => false,
|
||||
};
|
||||
|
||||
mod.* = .{
|
||||
.gpa = gpa,
|
||||
.arena_state = arena_allocator.state,
|
||||
.zig_lib_dir = options.zig_lib_dir,
|
||||
.zig_cache_dir_path = zig_cache_dir_path,
|
||||
.root_name = root_name,
|
||||
.root_pkg = options.root_pkg,
|
||||
.root_scope = root_scope,
|
||||
.bin_file_dir = bin_file_dir,
|
||||
.bin_file_path = options.bin_file_path,
|
||||
.bin_file = bin_file,
|
||||
.work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
|
||||
.keep_source_files_loaded = options.keep_source_files_loaded,
|
||||
.use_clang = use_clang,
|
||||
.clang_argv = options.clang_argv,
|
||||
.c_source_files = options.c_source_files,
|
||||
.cache = cache,
|
||||
.self_exe_path = options.self_exe_path,
|
||||
.libc_include_dir_list = libc_include_dir_list,
|
||||
.sanitize_c = sanitize_c,
|
||||
.rand = options.rand,
|
||||
.clang_passthrough_mode = options.clang_passthrough_mode,
|
||||
};
|
||||
break :mod mod;
|
||||
};
|
||||
errdefer mod.destroy();
|
||||
|
||||
// Add a `CObject` for each `c_source_files`.
|
||||
try c_object_table.ensureCapacity(gpa, options.c_source_files.len);
|
||||
try mod.c_object_table.ensureCapacity(gpa, options.c_source_files.len);
|
||||
for (options.c_source_files) |c_source_file| {
|
||||
var local_arena = std.heap.ArenaAllocator.init(gpa);
|
||||
errdefer local_arena.deinit();
|
||||
@ -1120,31 +1199,15 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
.extra_flags = &[0][]const u8{},
|
||||
.arena = local_arena.state,
|
||||
};
|
||||
c_object_table.putAssumeCapacityNoClobber(c_object, {});
|
||||
mod.c_object_table.putAssumeCapacityNoClobber(c_object, {});
|
||||
}
|
||||
|
||||
return Module{
|
||||
.gpa = gpa,
|
||||
.root_name = root_name,
|
||||
.root_pkg = options.root_pkg,
|
||||
.root_scope = root_scope,
|
||||
.bin_file_dir = bin_file_dir,
|
||||
.bin_file_path = options.bin_file_path,
|
||||
.bin_file = bin_file,
|
||||
.work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
|
||||
.keep_source_files_loaded = options.keep_source_files_loaded,
|
||||
.use_clang = use_clang,
|
||||
.clang_argv = options.clang_argv,
|
||||
.c_source_files = options.c_source_files,
|
||||
.cache = cache,
|
||||
.c_object_table = c_object_table,
|
||||
};
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Module) void {
|
||||
pub fn destroy(self: *Module) void {
|
||||
self.bin_file.destroy();
|
||||
const gpa = self.gpa;
|
||||
self.gpa.free(self.root_name);
|
||||
self.deletion_set.deinit(gpa);
|
||||
self.work_queue.deinit();
|
||||
|
||||
@ -1198,7 +1261,9 @@ pub fn deinit(self: *Module) void {
|
||||
}
|
||||
self.global_error_set.deinit(gpa);
|
||||
self.cache.release();
|
||||
self.* = undefined;
|
||||
|
||||
// This destroys `self`.
|
||||
self.arena_state.promote(gpa).deinit();
|
||||
}
|
||||
|
||||
fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
|
||||
@ -1209,7 +1274,7 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
|
||||
gpa.free(export_list);
|
||||
}
|
||||
|
||||
pub fn target(self: Module) std.Target {
|
||||
pub fn getTarget(self: Module) Target {
|
||||
return self.bin_file.options.target;
|
||||
}
|
||||
|
||||
@ -1440,27 +1505,333 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
|
||||
c_object.status = .{ .new = {} };
|
||||
},
|
||||
}
|
||||
if (!build_options.have_llvm) {
|
||||
self.buildCObject(c_object) catch |err| switch (err) {
|
||||
error.AnalysisFail => continue,
|
||||
else => {
|
||||
try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
|
||||
self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
|
||||
self.gpa,
|
||||
0,
|
||||
"clang not available: compiler not built with LLVM extensions enabled",
|
||||
.{},
|
||||
));
|
||||
c_object.status = .{ .failure = "" };
|
||||
continue;
|
||||
}
|
||||
try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
|
||||
self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
|
||||
self.gpa,
|
||||
0,
|
||||
"TODO: implement invoking clang to compile C source files",
|
||||
.{},
|
||||
"unable to build C object: {}",
|
||||
.{@errorName(err)},
|
||||
));
|
||||
c_object.status = .{ .failure = "" };
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn buildCObject(mod: *Module, c_object: *CObject) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
if (!build_options.have_llvm) {
|
||||
return mod.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{});
|
||||
}
|
||||
const self_exe_path = mod.self_exe_path orelse
|
||||
return mod.failCObj(c_object, "clang compilation disabled", .{});
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(mod.gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
var argv = std.ArrayList([]const u8).init(mod.gpa);
|
||||
defer argv.deinit();
|
||||
|
||||
const c_source_basename = std.fs.path.basename(c_object.src_path);
|
||||
// Special case when doing build-obj for just one C file. When there are more than one object
|
||||
// file and building an object we need to link them together, but with just one it should go
|
||||
// directly to the output file.
|
||||
const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and
|
||||
mod.bin_file.options.output_mode == .Obj and mod.bin_file.options.objects.len == 0;
|
||||
const o_basename_noext = if (direct_o) mod.root_name else mem.split(c_source_basename, ".").next().?;
|
||||
const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() });
|
||||
|
||||
// We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
|
||||
const out_obj_path = try mod.tmpFilePath(arena, o_basename);
|
||||
|
||||
try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" });
|
||||
|
||||
const ext = classifyFileExt(c_object.src_path);
|
||||
// TODO capture the .d file and deal with caching stuff
|
||||
try mod.addCCArgs(arena, &argv, ext, false, null);
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(out_obj_path);
|
||||
|
||||
try argv.append(c_object.src_path);
|
||||
try argv.appendSlice(c_object.extra_flags);
|
||||
|
||||
//for (argv.items) |arg| {
|
||||
// std.debug.print("{} ", .{arg});
|
||||
//}
|
||||
|
||||
const child = try std.ChildProcess.init(argv.items, arena);
|
||||
defer child.deinit();
|
||||
|
||||
if (mod.clang_passthrough_mode) {
|
||||
child.stdin_behavior = .Inherit;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stderr_behavior = .Inherit;
|
||||
|
||||
const term = child.spawnAndWait() catch |err| {
|
||||
return mod.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
|
||||
};
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
// TODO make std.process.exit and std.ChildProcess exit code have the same type
|
||||
// and forward it here. Currently it is u32 vs u8.
|
||||
std.process.exit(1);
|
||||
}
|
||||
},
|
||||
else => std.process.exit(1),
|
||||
}
|
||||
} else {
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Pipe;
|
||||
child.stderr_behavior = .Pipe;
|
||||
|
||||
try child.spawn();
|
||||
|
||||
const stdout_reader = child.stdout.?.reader();
|
||||
const stderr_reader = child.stderr.?.reader();
|
||||
|
||||
// TODO Need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
|
||||
const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
|
||||
const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
|
||||
|
||||
const term = child.wait() catch |err| {
|
||||
return mod.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
|
||||
};
|
||||
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
// TODO parse clang stderr and turn it into an error message
|
||||
// and then call failCObjWithOwnedErrorMsg
|
||||
std.log.err("clang failed with stderr: {}", .{stderr});
|
||||
return mod.failCObj(c_object, "clang exited with code {}", .{code});
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.log.err("clang terminated with stderr: {}", .{stderr});
|
||||
return mod.failCObj(c_object, "clang terminated unexpectedly", .{});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO handle .d files
|
||||
|
||||
// TODO rename into place
|
||||
std.debug.print("TODO rename {} into cache dir\n", .{out_obj_path});
|
||||
|
||||
// TODO use the cache file name instead of tmp file name
|
||||
const success_file_path = try mod.gpa.dupe(u8, out_obj_path);
|
||||
c_object.status = .{ .success = success_file_path };
|
||||
}
|
||||
|
||||
fn tmpFilePath(mod: *Module, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
|
||||
const s = std.fs.path.sep_str;
|
||||
return std.fmt.allocPrint(
|
||||
arena,
|
||||
"{}" ++ s ++ "tmp" ++ s ++ "{x}-{}",
|
||||
.{ mod.zig_cache_dir_path, mod.rand.int(u64), suffix },
|
||||
);
|
||||
}
|
||||
|
||||
/// Add common C compiler args between translate-c and C object compilation.
|
||||
fn addCCArgs(
|
||||
mod: *Module,
|
||||
arena: *Allocator,
|
||||
argv: *std.ArrayList([]const u8),
|
||||
ext: FileExt,
|
||||
translate_c: bool,
|
||||
out_dep_path: ?[]const u8,
|
||||
) !void {
|
||||
const target = mod.getTarget();
|
||||
|
||||
if (translate_c) {
|
||||
try argv.appendSlice(&[_][]const u8{ "-x", "c" });
|
||||
}
|
||||
|
||||
if (ext == .cpp) {
|
||||
try argv.append("-nostdinc++");
|
||||
}
|
||||
try argv.appendSlice(&[_][]const u8{
|
||||
"-nostdinc",
|
||||
"-fno-spell-checking",
|
||||
});
|
||||
|
||||
// We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
|
||||
// we want Clang to infer it, and in normal mode we always want it off, which will be true since
|
||||
// clang will detect stderr as a pipe rather than a terminal.
|
||||
if (!mod.clang_passthrough_mode) {
|
||||
// Make stderr more easily parseable.
|
||||
try argv.append("-fno-caret-diagnostics");
|
||||
}
|
||||
|
||||
if (mod.bin_file.options.function_sections) {
|
||||
try argv.append("-ffunction-sections");
|
||||
}
|
||||
|
||||
try argv.ensureCapacity(argv.items.len + mod.bin_file.options.framework_dirs.len * 2);
|
||||
for (mod.bin_file.options.framework_dirs) |framework_dir| {
|
||||
argv.appendAssumeCapacity("-iframework");
|
||||
argv.appendAssumeCapacity(framework_dir);
|
||||
}
|
||||
|
||||
if (mod.bin_file.options.link_libcpp) {
|
||||
const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{
|
||||
mod.zig_lib_dir, "libcxx", "include",
|
||||
});
|
||||
const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{
|
||||
mod.zig_lib_dir, "libcxxabi", "include",
|
||||
});
|
||||
|
||||
try argv.append("-isystem");
|
||||
try argv.append(libcxx_include_path);
|
||||
|
||||
try argv.append("-isystem");
|
||||
try argv.append(libcxxabi_include_path);
|
||||
|
||||
if (target.abi.isMusl()) {
|
||||
try argv.append("-D_LIBCPP_HAS_MUSL_LIBC");
|
||||
}
|
||||
try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
|
||||
try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
|
||||
}
|
||||
|
||||
const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
|
||||
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
|
||||
|
||||
switch (ext) {
|
||||
.c, .cpp, .h => {
|
||||
// According to Rich Felker libc headers are supposed to go before C language headers.
|
||||
// However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
|
||||
// and other compiler specific items.
|
||||
const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ mod.zig_lib_dir, "include" });
|
||||
try argv.append("-isystem");
|
||||
try argv.append(c_headers_dir);
|
||||
|
||||
for (mod.libc_include_dir_list) |include_dir| {
|
||||
try argv.append("-isystem");
|
||||
try argv.append(include_dir);
|
||||
}
|
||||
|
||||
if (target.cpu.model.llvm_name) |llvm_name| {
|
||||
try argv.appendSlice(&[_][]const u8{
|
||||
"-Xclang", "-target-cpu", "-Xclang", llvm_name,
|
||||
});
|
||||
}
|
||||
// TODO CLI args for target features
|
||||
//if (g->zig_target->llvm_cpu_features != nullptr) {
|
||||
// // https://github.com/ziglang/zig/issues/5017
|
||||
// SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(","));
|
||||
// Optional<Slice<uint8_t>> flag = SplitIterator_next(&it);
|
||||
// while (flag.is_some) {
|
||||
// try argv.append("-Xclang");
|
||||
// try argv.append("-target-feature");
|
||||
// try argv.append("-Xclang");
|
||||
// try argv.append(buf_ptr(buf_create_from_slice(flag.value)));
|
||||
// flag = SplitIterator_next(&it);
|
||||
// }
|
||||
//}
|
||||
if (translate_c) {
|
||||
// This gives us access to preprocessing entities, presumably at the cost of performance.
|
||||
try argv.append("-Xclang");
|
||||
try argv.append("-detailed-preprocessing-record");
|
||||
}
|
||||
if (out_dep_path) |p| {
|
||||
try argv.append("-MD");
|
||||
try argv.append("-MV");
|
||||
try argv.append("-MF");
|
||||
try argv.append(p);
|
||||
}
|
||||
},
|
||||
.assembly, .ll, .bc, .unknown => {},
|
||||
}
|
||||
// TODO CLI args for cpu features when compiling assembly
|
||||
//for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) {
|
||||
// try argv.append(g->zig_target->llvm_cpu_features_asm_ptr[i]);
|
||||
//}
|
||||
|
||||
if (target.os.tag == .freestanding) {
|
||||
try argv.append("-ffreestanding");
|
||||
}
|
||||
|
||||
// windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning.
|
||||
// So for this target, we disable this warning.
|
||||
if (target.os.tag == .windows and target.abi.isGnu()) {
|
||||
try argv.append("-Wno-pragma-pack");
|
||||
}
|
||||
|
||||
if (!mod.bin_file.options.strip) {
|
||||
try argv.append("-g");
|
||||
}
|
||||
|
||||
if (mod.haveFramePointer()) {
|
||||
try argv.append("-fno-omit-frame-pointer");
|
||||
} else {
|
||||
try argv.append("-fomit-frame-pointer");
|
||||
}
|
||||
|
||||
if (mod.sanitize_c) {
|
||||
try argv.append("-fsanitize=undefined");
|
||||
try argv.append("-fsanitize-trap=undefined");
|
||||
}
|
||||
|
||||
switch (mod.bin_file.options.optimize_mode) {
|
||||
.Debug => {
|
||||
// windows c runtime requires -D_DEBUG if using debug libraries
|
||||
try argv.append("-D_DEBUG");
|
||||
try argv.append("-Og");
|
||||
|
||||
if (mod.bin_file.options.link_libc) {
|
||||
try argv.append("-fstack-protector-strong");
|
||||
try argv.append("--param");
|
||||
try argv.append("ssp-buffer-size=4");
|
||||
} else {
|
||||
try argv.append("-fno-stack-protector");
|
||||
}
|
||||
},
|
||||
.ReleaseSafe => {
|
||||
// See the comment in the BuildModeFastRelease case for why we pass -O2 rather
|
||||
// than -O3 here.
|
||||
try argv.append("-O2");
|
||||
if (mod.bin_file.options.link_libc) {
|
||||
try argv.append("-D_FORTIFY_SOURCE=2");
|
||||
try argv.append("-fstack-protector-strong");
|
||||
try argv.append("--param");
|
||||
try argv.append("ssp-buffer-size=4");
|
||||
} else {
|
||||
try argv.append("-fno-stack-protector");
|
||||
}
|
||||
},
|
||||
.ReleaseFast => {
|
||||
try argv.append("-DNDEBUG");
|
||||
// Here we pass -O2 rather than -O3 because, although we do the equivalent of
|
||||
// -O3 in Zig code, the justification for the difference here is that Zig
|
||||
// has better detection and prevention of undefined behavior, so -O3 is safer for
|
||||
// Zig code than it is for C code. Also, C programmers are used to their code
|
||||
// running in -O2 and thus the -O3 path has been tested less.
|
||||
try argv.append("-O2");
|
||||
try argv.append("-fno-stack-protector");
|
||||
},
|
||||
.ReleaseSmall => {
|
||||
try argv.append("-DNDEBUG");
|
||||
try argv.append("-Os");
|
||||
try argv.append("-fno-stack-protector");
|
||||
},
|
||||
}
|
||||
|
||||
// TODO add CLI args for PIC
|
||||
//if (target_supports_fpic(g->zig_target) and g->have_pic) {
|
||||
// try argv.append("-fPIC");
|
||||
//}
|
||||
|
||||
try argv.appendSlice(mod.clang_argv);
|
||||
}
|
||||
|
||||
pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
|
||||
@ -3041,7 +3412,7 @@ pub fn cmpNumeric(
|
||||
} else if (rhs_ty_tag == .ComptimeFloat) {
|
||||
break :x lhs.ty;
|
||||
}
|
||||
if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
|
||||
if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
|
||||
break :x lhs.ty;
|
||||
} else {
|
||||
break :x rhs.ty;
|
||||
@ -3100,7 +3471,7 @@ pub fn cmpNumeric(
|
||||
} else if (lhs_is_float) {
|
||||
dest_float_type = lhs.ty;
|
||||
} else {
|
||||
const int_info = lhs.ty.intInfo(self.target());
|
||||
const int_info = lhs.ty.intInfo(self.getTarget());
|
||||
lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
|
||||
}
|
||||
|
||||
@ -3135,7 +3506,7 @@ pub fn cmpNumeric(
|
||||
} else if (rhs_is_float) {
|
||||
dest_float_type = rhs.ty;
|
||||
} else {
|
||||
const int_info = rhs.ty.intInfo(self.target());
|
||||
const int_info = rhs.ty.intInfo(self.getTarget());
|
||||
rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
|
||||
}
|
||||
|
||||
@ -3200,13 +3571,13 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
|
||||
next_inst.ty.isInt() and
|
||||
prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
|
||||
{
|
||||
if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) {
|
||||
if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
|
||||
prev_inst = next_inst;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
|
||||
if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) {
|
||||
if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
|
||||
prev_inst = next_inst;
|
||||
}
|
||||
continue;
|
||||
@ -3274,8 +3645,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
|
||||
if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
|
||||
assert(inst.value() == null); // handled above
|
||||
|
||||
const src_info = inst.ty.intInfo(self.target());
|
||||
const dst_info = dest_type.intInfo(self.target());
|
||||
const src_info = inst.ty.intInfo(self.getTarget());
|
||||
const dst_info = dest_type.intInfo(self.getTarget());
|
||||
if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
|
||||
// small enough unsigned ints can get casted to large enough signed ints
|
||||
(src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
|
||||
@ -3289,8 +3660,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
|
||||
if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
|
||||
assert(inst.value() == null); // handled above
|
||||
|
||||
const src_bits = inst.ty.floatBits(self.target());
|
||||
const dst_bits = dest_type.floatBits(self.target());
|
||||
const src_bits = inst.ty.floatBits(self.getTarget());
|
||||
const dst_bits = dest_type.floatBits(self.getTarget());
|
||||
if (dst_bits >= src_bits) {
|
||||
const b = try self.requireRuntimeBlock(scope, inst.src);
|
||||
return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
|
||||
@ -3312,14 +3683,14 @@ pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*
|
||||
}
|
||||
return self.fail(scope, inst.src, "TODO float to int", .{});
|
||||
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
|
||||
if (!val.intFitsInType(dest_type, self.target())) {
|
||||
if (!val.intFitsInType(dest_type, self.getTarget())) {
|
||||
return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
|
||||
}
|
||||
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
|
||||
}
|
||||
} else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
|
||||
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
|
||||
const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
|
||||
const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
|
||||
error.Overflow => return self.fail(
|
||||
scope,
|
||||
inst.src,
|
||||
@ -3370,6 +3741,22 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
|
||||
return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
|
||||
}
|
||||
|
||||
fn failCObj(mod: *Module, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
|
||||
@setCold(true);
|
||||
const err_msg = try ErrorMsg.create(mod.gpa, 0, "unable to build C object: " ++ format, args);
|
||||
return mod.failCObjWithOwnedErrorMsg(c_object, err_msg);
|
||||
}
|
||||
|
||||
fn failCObjWithOwnedErrorMsg(mod: *Module, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
|
||||
{
|
||||
errdefer err_msg.destroy(mod.gpa);
|
||||
try mod.failed_c_objects.ensureCapacity(mod.gpa, mod.failed_c_objects.items().len + 1);
|
||||
}
|
||||
mod.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
|
||||
c_object.status = .{ .failure = "" };
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
|
||||
@setCold(true);
|
||||
const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
|
||||
@ -3560,7 +3947,7 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
|
||||
pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
|
||||
var bit_count = switch (float_type.tag()) {
|
||||
.comptime_float => 128,
|
||||
else => float_type.floatBits(self.target()),
|
||||
else => float_type.floatBits(self.getTarget()),
|
||||
};
|
||||
|
||||
const allocator = scope.arena();
|
||||
@ -3594,7 +3981,7 @@ pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
|
||||
pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
|
||||
var bit_count = switch (float_type.tag()) {
|
||||
.comptime_float => 128,
|
||||
else => float_type.floatBits(self.target()),
|
||||
else => float_type.floatBits(self.getTarget()),
|
||||
};
|
||||
|
||||
const allocator = scope.arena();
|
||||
@ -3865,3 +4252,106 @@ pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: Pani
|
||||
_ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
|
||||
return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
|
||||
}
|
||||
|
||||
pub const FileExt = enum {
|
||||
c,
|
||||
cpp,
|
||||
h,
|
||||
ll,
|
||||
bc,
|
||||
assembly,
|
||||
unknown,
|
||||
};
|
||||
|
||||
pub fn hasCExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".c");
|
||||
}
|
||||
|
||||
pub fn hasCppExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".C") or
|
||||
mem.endsWith(u8, filename, ".cc") or
|
||||
mem.endsWith(u8, filename, ".cpp") or
|
||||
mem.endsWith(u8, filename, ".cxx");
|
||||
}
|
||||
|
||||
pub fn hasAsmExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
|
||||
}
|
||||
|
||||
pub fn classifyFileExt(filename: []const u8) FileExt {
|
||||
if (hasCExt(filename)) {
|
||||
return .c;
|
||||
} else if (hasCppExt(filename)) {
|
||||
return .cpp;
|
||||
} else if (mem.endsWith(u8, filename, ".ll")) {
|
||||
return .ll;
|
||||
} else if (mem.endsWith(u8, filename, ".bc")) {
|
||||
return .bc;
|
||||
} else if (hasAsmExt(filename)) {
|
||||
return .assembly;
|
||||
} else if (mem.endsWith(u8, filename, ".h")) {
|
||||
return .h;
|
||||
} else {
|
||||
// TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z
|
||||
return .unknown;
|
||||
}
|
||||
}
|
||||
|
||||
fn haveFramePointer(mod: *Module) bool {
|
||||
return switch (mod.bin_file.options.optimize_mode) {
|
||||
.Debug, .ReleaseSafe => !mod.bin_file.options.strip,
|
||||
.ReleaseSmall, .ReleaseFast => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn detectLibCIncludeDirs(
|
||||
arena: *Allocator,
|
||||
zig_lib_dir: []const u8,
|
||||
target: Target,
|
||||
link_libc: bool,
|
||||
) ![]const []const u8 {
|
||||
if (!link_libc) return &[0][]u8{};
|
||||
|
||||
// TODO Support --libc file explicitly providing libc paths. Or not? Maybe we are better off
|
||||
// deleting that feature.
|
||||
|
||||
if (target_util.canBuildLibC(target)) {
|
||||
const generic_name = target_util.libCGenericName(target);
|
||||
// Some architectures are handled by the same set of headers.
|
||||
const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch);
|
||||
const os_name = @tagName(target.os.tag);
|
||||
// Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
|
||||
const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
|
||||
const s = std.fs.path.sep_str;
|
||||
const arch_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}",
|
||||
.{ zig_lib_dir, arch_name, os_name, abi_name },
|
||||
);
|
||||
const generic_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}",
|
||||
.{ zig_lib_dir, generic_name },
|
||||
);
|
||||
const arch_os_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any",
|
||||
.{ zig_lib_dir, @tagName(target.cpu.arch), os_name },
|
||||
);
|
||||
const generic_os_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any",
|
||||
.{ zig_lib_dir, os_name },
|
||||
);
|
||||
|
||||
const list = try arena.alloc([]const u8, 4);
|
||||
list[0] = arch_include_dir;
|
||||
list[1] = generic_include_dir;
|
||||
list[2] = arch_os_include_dir;
|
||||
list[3] = generic_os_include_dir;
|
||||
return list;
|
||||
}
|
||||
|
||||
// TODO finish porting detect_libc from codegen.cpp
|
||||
return error.LibCDetectionUnimplemented;
|
||||
}
|
||||
|
125
src-self-hosted/codegen/llvm.zig
Normal file
125
src-self-hosted/codegen/llvm.zig
Normal file
@ -0,0 +1,125 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {
|
||||
const llvm_arch = switch (target.cpu.arch) {
|
||||
.arm => "arm",
|
||||
.armeb => "armeb",
|
||||
.aarch64 => "aarch64",
|
||||
.aarch64_be => "aarch64_be",
|
||||
.aarch64_32 => "aarch64_32",
|
||||
.arc => "arc",
|
||||
.avr => "avr",
|
||||
.bpfel => "bpfel",
|
||||
.bpfeb => "bpfeb",
|
||||
.hexagon => "hexagon",
|
||||
.mips => "mips",
|
||||
.mipsel => "mipsel",
|
||||
.mips64 => "mips64",
|
||||
.mips64el => "mips64el",
|
||||
.msp430 => "msp430",
|
||||
.powerpc => "powerpc",
|
||||
.powerpc64 => "powerpc64",
|
||||
.powerpc64le => "powerpc64le",
|
||||
.r600 => "r600",
|
||||
.amdgcn => "amdgcn",
|
||||
.riscv32 => "riscv32",
|
||||
.riscv64 => "riscv64",
|
||||
.sparc => "sparc",
|
||||
.sparcv9 => "sparcv9",
|
||||
.sparcel => "sparcel",
|
||||
.s390x => "s390x",
|
||||
.tce => "tce",
|
||||
.tcele => "tcele",
|
||||
.thumb => "thumb",
|
||||
.thumbeb => "thumbeb",
|
||||
.i386 => "i386",
|
||||
.x86_64 => "x86_64",
|
||||
.xcore => "xcore",
|
||||
.nvptx => "nvptx",
|
||||
.nvptx64 => "nvptx64",
|
||||
.le32 => "le32",
|
||||
.le64 => "le64",
|
||||
.amdil => "amdil",
|
||||
.amdil64 => "amdil64",
|
||||
.hsail => "hsail",
|
||||
.hsail64 => "hsail64",
|
||||
.spir => "spir",
|
||||
.spir64 => "spir64",
|
||||
.kalimba => "kalimba",
|
||||
.shave => "shave",
|
||||
.lanai => "lanai",
|
||||
.wasm32 => "wasm32",
|
||||
.wasm64 => "wasm64",
|
||||
.renderscript32 => "renderscript32",
|
||||
.renderscript64 => "renderscript64",
|
||||
.ve => "ve",
|
||||
.spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
|
||||
};
|
||||
// TODO Add a sub-arch for some architectures depending on CPU features.
|
||||
|
||||
const llvm_os = switch (target.os.tag) {
|
||||
.freestanding => "unknown",
|
||||
.ananas => "ananas",
|
||||
.cloudabi => "cloudabi",
|
||||
.dragonfly => "dragonfly",
|
||||
.freebsd => "freebsd",
|
||||
.fuchsia => "fuchsia",
|
||||
.ios => "ios",
|
||||
.kfreebsd => "kfreebsd",
|
||||
.linux => "linux",
|
||||
.lv2 => "lv2",
|
||||
.macosx => "macosx",
|
||||
.netbsd => "netbsd",
|
||||
.openbsd => "openbsd",
|
||||
.solaris => "solaris",
|
||||
.windows => "windows",
|
||||
.haiku => "haiku",
|
||||
.minix => "minix",
|
||||
.rtems => "rtems",
|
||||
.nacl => "nacl",
|
||||
.cnk => "cnk",
|
||||
.aix => "aix",
|
||||
.cuda => "cuda",
|
||||
.nvcl => "nvcl",
|
||||
.amdhsa => "amdhsa",
|
||||
.ps4 => "ps4",
|
||||
.elfiamcu => "elfiamcu",
|
||||
.tvos => "tvos",
|
||||
.watchos => "watchos",
|
||||
.mesa3d => "mesa3d",
|
||||
.contiki => "contiki",
|
||||
.amdpal => "amdpal",
|
||||
.hermit => "hermit",
|
||||
.hurd => "hurd",
|
||||
.wasi => "wasi",
|
||||
.emscripten => "emscripten",
|
||||
.uefi => "windows",
|
||||
.other => "unknown",
|
||||
};
|
||||
|
||||
const llvm_abi = switch (target.abi) {
|
||||
.none => "unknown",
|
||||
.gnu => "gnu",
|
||||
.gnuabin32 => "gnuabin32",
|
||||
.gnuabi64 => "gnuabi64",
|
||||
.gnueabi => "gnueabi",
|
||||
.gnueabihf => "gnueabihf",
|
||||
.gnux32 => "gnux32",
|
||||
.code16 => "code16",
|
||||
.eabi => "eabi",
|
||||
.eabihf => "eabihf",
|
||||
.android => "android",
|
||||
.musl => "musl",
|
||||
.musleabi => "musleabi",
|
||||
.musleabihf => "musleabihf",
|
||||
.msvc => "msvc",
|
||||
.itanium => "itanium",
|
||||
.cygnus => "cygnus",
|
||||
.coreclr => "coreclr",
|
||||
.simulator => "simulator",
|
||||
.macabi => "macabi",
|
||||
};
|
||||
|
||||
return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi });
|
||||
}
|
@ -93,46 +93,3 @@ pub fn openGlobalCacheDir() !fs.Dir {
|
||||
const path_name = try resolveGlobalCacheDir(&fba.allocator);
|
||||
return fs.cwd().makeOpenPath(path_name, .{});
|
||||
}
|
||||
|
||||
var compiler_id_mutex = std.Mutex{};
|
||||
var compiler_id: [16]u8 = undefined;
|
||||
var compiler_id_computed = false;
|
||||
|
||||
pub fn resolveCompilerId(gpa: *mem.Allocator) ![16]u8 {
|
||||
const held = compiler_id_mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
if (compiler_id_computed)
|
||||
return compiler_id;
|
||||
compiler_id_computed = true;
|
||||
|
||||
var cache_dir = try openGlobalCacheDir();
|
||||
defer cache_dir.close();
|
||||
|
||||
var ch = try CacheHash.init(gpa, cache_dir, "exe");
|
||||
defer ch.release();
|
||||
|
||||
const self_exe_path = try fs.selfExePathAlloc(gpa);
|
||||
defer gpa.free(self_exe_path);
|
||||
|
||||
_ = try ch.addFile(self_exe_path, null);
|
||||
|
||||
if (try ch.hit()) |digest| {
|
||||
compiler_id = digest[0..16].*;
|
||||
return compiler_id;
|
||||
}
|
||||
|
||||
const libs = try std.process.getSelfExeSharedLibPaths(gpa);
|
||||
defer {
|
||||
for (libs) |lib| gpa.free(lib);
|
||||
gpa.free(libs);
|
||||
}
|
||||
|
||||
for (libs) |lib| {
|
||||
try ch.addFilePost(lib);
|
||||
}
|
||||
|
||||
const digest = ch.final();
|
||||
compiler_id = digest[0..16].*;
|
||||
return compiler_id;
|
||||
}
|
||||
|
@ -35,6 +35,9 @@ pub const Options = struct {
|
||||
/// other objects.
|
||||
/// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
|
||||
use_llvm: bool = false,
|
||||
link_libc: bool = false,
|
||||
link_libcpp: bool = false,
|
||||
function_sections: bool = false,
|
||||
|
||||
objects: []const []const u8 = &[0][]const u8{},
|
||||
framework_dirs: []const []const u8 = &[0][]const u8{},
|
||||
|
@ -219,8 +219,11 @@ pub const SrcFn = struct {
|
||||
pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
|
||||
assert(options.object_format == .elf);
|
||||
|
||||
if (options.use_llvm) return error.LLVM_BackendIsTODO_ForELF; // TODO
|
||||
if (options.use_lld) return error.LLD_LinkingIsTODOForELF; // TODO
|
||||
if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
|
||||
|
||||
if (build_options.have_llvm and options.use_lld) {
|
||||
std.debug.print("TODO open a temporary object file, not the final output file because we want to link with LLD\n", .{});
|
||||
}
|
||||
|
||||
const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
|
||||
errdefer file.close();
|
||||
@ -741,8 +744,21 @@ pub const abbrev_base_type = 4;
|
||||
pub const abbrev_pad1 = 5;
|
||||
pub const abbrev_parameter = 6;
|
||||
|
||||
/// Commit pending changes and write headers.
|
||||
pub fn flush(self: *Elf, module: *Module) !void {
|
||||
if (build_options.have_llvm and self.base.options.use_lld) {
|
||||
// If there is no Zig code to compile, then we should skip flushing the output file because it
|
||||
// will not be part of the linker line anyway.
|
||||
if (module.root_pkg != null) {
|
||||
try self.flushInner(module);
|
||||
}
|
||||
std.debug.print("TODO create an LLD command line and invoke it\n", .{});
|
||||
} else {
|
||||
return self.flushInner(module);
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit pending changes and write headers.
|
||||
fn flushInner(self: *Elf, module: *Module) !void {
|
||||
const target_endian = self.base.options.target.cpu.arch.endian();
|
||||
const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
|
||||
const ptr_width_bytes: u8 = self.ptrWidthBytes();
|
||||
|
@ -40,6 +40,7 @@ const usage =
|
||||
\\ c++ Use Zig as a drop-in C++ compiler
|
||||
\\ env Print lib path, std path, compiler id and version
|
||||
\\ fmt [source] Parse file and render in canonical zig format
|
||||
\\ translate-c [source] Convert C code to Zig code
|
||||
\\ targets List available compilation targets
|
||||
\\ version Print version number and exit
|
||||
\\ zen Print zen of zig and exit
|
||||
@ -47,15 +48,21 @@ const usage =
|
||||
\\
|
||||
;
|
||||
|
||||
pub const log_level: std.log.Level = switch (std.builtin.mode) {
|
||||
.Debug => .debug,
|
||||
.ReleaseSafe, .ReleaseFast => .info,
|
||||
.ReleaseSmall => .crit,
|
||||
};
|
||||
|
||||
pub fn log(
|
||||
comptime level: std.log.Level,
|
||||
comptime scope: @TypeOf(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
// Hide anything more verbose than warn unless it was added with `-Dlog=foo`.
|
||||
// Hide debug messages unless added with `-Dlog=foo`.
|
||||
if (@enumToInt(level) > @enumToInt(std.log.level) or
|
||||
@enumToInt(level) > @enumToInt(std.log.Level.warn))
|
||||
@enumToInt(level) > @enumToInt(std.log.Level.info))
|
||||
{
|
||||
const scope_name = @tagName(scope);
|
||||
const ok = comptime for (build_options.log_scopes) |log_scope| {
|
||||
@ -67,13 +74,15 @@ pub fn log(
|
||||
return;
|
||||
}
|
||||
|
||||
// We only recognize 4 log levels in this application.
|
||||
const level_txt = switch (level) {
|
||||
.emerg => "error",
|
||||
.warn => "warning",
|
||||
else => @tagName(level),
|
||||
.emerg, .alert, .crit => "error",
|
||||
.err, .warn => "warning",
|
||||
.notice, .info => "info",
|
||||
.debug => "debug",
|
||||
};
|
||||
const prefix1 = level_txt ++ ": ";
|
||||
const prefix2 = if (scope == .default) "" else "(" ++ @tagName(scope) ++ "): ";
|
||||
const prefix1 = level_txt;
|
||||
const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
|
||||
|
||||
// Print the message to stderr, silently ignoring any errors
|
||||
std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args);
|
||||
@ -93,8 +102,8 @@ pub fn main() !void {
|
||||
const args = try process.argsAlloc(arena);
|
||||
|
||||
if (args.len <= 1) {
|
||||
std.debug.print("expected command argument\n\n{}", .{usage});
|
||||
process.exit(1);
|
||||
std.log.info("{}", .{usage});
|
||||
fatal("expected command argument", .{});
|
||||
}
|
||||
|
||||
const cmd = args[1];
|
||||
@ -109,6 +118,8 @@ pub fn main() !void {
|
||||
return buildOutputType(gpa, arena, args, .cc);
|
||||
} else if (mem.eql(u8, cmd, "c++")) {
|
||||
return buildOutputType(gpa, arena, args, .cpp);
|
||||
} else if (mem.eql(u8, cmd, "translate-c")) {
|
||||
return buildOutputType(gpa, arena, args, .translate_c);
|
||||
} else if (mem.eql(u8, cmd, "clang") or
|
||||
mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
|
||||
{
|
||||
@ -128,8 +139,8 @@ pub fn main() !void {
|
||||
} else if (mem.eql(u8, cmd, "help")) {
|
||||
try io.getStdOut().writeAll(usage);
|
||||
} else {
|
||||
std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
|
||||
process.exit(1);
|
||||
std.log.info("{}", .{usage});
|
||||
fatal("unknown command: {}", .{args[1]});
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,6 +234,7 @@ pub fn buildOutputType(
|
||||
build: std.builtin.OutputMode,
|
||||
cc,
|
||||
cpp,
|
||||
translate_c,
|
||||
},
|
||||
) !void {
|
||||
var color: Color = .Auto;
|
||||
@ -251,8 +263,8 @@ pub fn buildOutputType(
|
||||
var emit_h: Emit = undefined;
|
||||
var ensure_libc_on_non_freestanding = false;
|
||||
var ensure_libcpp_on_non_freestanding = false;
|
||||
var have_libc = false;
|
||||
var have_libcpp = false;
|
||||
var link_libc = false;
|
||||
var link_libcpp = false;
|
||||
var want_native_include_dirs = false;
|
||||
var enable_cache: ?bool = null;
|
||||
var want_pic: ?bool = null;
|
||||
@ -298,13 +310,20 @@ pub fn buildOutputType(
|
||||
var frameworks = std.ArrayList([]const u8).init(gpa);
|
||||
defer frameworks.deinit();
|
||||
|
||||
if (arg_mode == .build) {
|
||||
output_mode = arg_mode.build;
|
||||
emit_h = switch (output_mode) {
|
||||
.Exe => .no,
|
||||
.Obj, .Lib => .yes_default_path,
|
||||
if (arg_mode == .build or arg_mode == .translate_c) {
|
||||
output_mode = switch (arg_mode) {
|
||||
.build => |m| m,
|
||||
.translate_c => .Obj,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
switch (arg_mode) {
|
||||
.build => switch (output_mode) {
|
||||
.Exe => emit_h = .no,
|
||||
.Obj, .Lib => emit_h = .yes_default_path,
|
||||
},
|
||||
.translate_c => emit_h = .no,
|
||||
else => unreachable,
|
||||
}
|
||||
const args = all_args[2..];
|
||||
var i: usize = 0;
|
||||
while (i < args.len) : (i += 1) {
|
||||
@ -499,7 +518,7 @@ pub fn buildOutputType(
|
||||
mem.endsWith(u8, arg, ".lib"))
|
||||
{
|
||||
try link_objects.append(arg);
|
||||
} else if (hasAsmExt(arg) or hasCExt(arg) or hasCppExt(arg)) {
|
||||
} else if (Module.hasAsmExt(arg) or Module.hasCExt(arg) or Module.hasCppExt(arg)) {
|
||||
try c_source_files.append(arg);
|
||||
} else if (mem.endsWith(u8, arg, ".so") or
|
||||
mem.endsWith(u8, arg, ".dylib") or
|
||||
@ -543,7 +562,7 @@ pub fn buildOutputType(
|
||||
try clang_argv.appendSlice(it.other_args);
|
||||
},
|
||||
.positional => {
|
||||
const file_ext = classify_file_ext(mem.spanZ(it.only_arg));
|
||||
const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg));
|
||||
switch (file_ext) {
|
||||
.assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg),
|
||||
.unknown => try link_objects.append(it.only_arg),
|
||||
@ -819,28 +838,28 @@ pub fn buildOutputType(
|
||||
.diagnostics = &diags,
|
||||
}) catch |err| switch (err) {
|
||||
error.UnknownCpuModel => {
|
||||
std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
|
||||
diags.cpu_name.?,
|
||||
@tagName(diags.arch.?),
|
||||
});
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(arena);
|
||||
for (diags.arch.?.allCpuModels()) |cpu| {
|
||||
std.debug.print(" {}\n", .{cpu.name});
|
||||
help_text.writer().print(" {}\n", .{cpu.name}) catch break :help;
|
||||
}
|
||||
process.exit(1);
|
||||
std.log.info("Available CPUs for architecture '{}': {}", .{
|
||||
@tagName(diags.arch.?), help_text.items,
|
||||
});
|
||||
}
|
||||
fatal("Unknown CPU: '{}'", .{diags.cpu_name.?});
|
||||
},
|
||||
error.UnknownCpuFeature => {
|
||||
std.debug.print(
|
||||
\\Unknown CPU feature: '{}'
|
||||
\\Available CPU features for architecture '{}':
|
||||
\\
|
||||
, .{
|
||||
diags.unknown_feature_name,
|
||||
@tagName(diags.arch.?),
|
||||
});
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(arena);
|
||||
for (diags.arch.?.allFeaturesList()) |feature| {
|
||||
std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
|
||||
help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help;
|
||||
}
|
||||
process.exit(1);
|
||||
std.log.info("Available CPU features for architecture '{}': {}", .{
|
||||
@tagName(diags.arch.?), help_text.items,
|
||||
});
|
||||
}
|
||||
fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name});
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
@ -849,14 +868,16 @@ pub fn buildOutputType(
|
||||
if (target_info.cpu_detection_unimplemented) {
|
||||
// TODO We want to just use detected_info.target but implementing
|
||||
// CPU model & feature detection is todo so here we rely on LLVM.
|
||||
// TODO The workaround to use LLVM to detect features needs to be used for
|
||||
// `zig targets` as well.
|
||||
fatal("CPU features detection is not yet available for this system without LLVM extensions", .{});
|
||||
}
|
||||
|
||||
if (target_info.target.os.tag != .freestanding) {
|
||||
if (ensure_libc_on_non_freestanding)
|
||||
have_libc = true;
|
||||
link_libc = true;
|
||||
if (ensure_libcpp_on_non_freestanding)
|
||||
have_libcpp = true;
|
||||
link_libcpp = true;
|
||||
}
|
||||
|
||||
// Now that we have target info, we can find out if any of the system libraries
|
||||
@ -867,12 +888,12 @@ pub fn buildOutputType(
|
||||
while (i < system_libs.items.len) {
|
||||
const lib_name = system_libs.items[i];
|
||||
if (is_libc_lib_name(target_info.target, lib_name)) {
|
||||
have_libc = true;
|
||||
link_libc = true;
|
||||
_ = system_libs.orderedRemove(i);
|
||||
continue;
|
||||
}
|
||||
if (is_libcpp_lib_name(target_info.target, lib_name)) {
|
||||
have_libcpp = true;
|
||||
link_libcpp = true;
|
||||
_ = system_libs.orderedRemove(i);
|
||||
continue;
|
||||
}
|
||||
@ -960,7 +981,21 @@ pub fn buildOutputType(
|
||||
.yes_default_path => try std.fmt.allocPrint(arena, "{}.h", .{root_name}),
|
||||
};
|
||||
|
||||
var module = Module.init(gpa, .{
|
||||
const self_exe_path = try fs.selfExePathAlloc(arena);
|
||||
const zig_lib_dir = introspect.resolveZigLibDir(gpa) catch |err| {
|
||||
fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
|
||||
};
|
||||
defer gpa.free(zig_lib_dir);
|
||||
|
||||
const random_seed = blk: {
|
||||
var random_seed: u64 = undefined;
|
||||
try std.crypto.randomBytes(mem.asBytes(&random_seed));
|
||||
break :blk random_seed;
|
||||
};
|
||||
var default_prng = std.rand.DefaultPrng.init(random_seed);
|
||||
|
||||
const module = Module.create(gpa, .{
|
||||
.zig_lib_dir = zig_lib_dir,
|
||||
.root_name = root_name,
|
||||
.target = target_info.target,
|
||||
.output_mode = output_mode,
|
||||
@ -980,8 +1015,8 @@ pub fn buildOutputType(
|
||||
.frameworks = frameworks.items,
|
||||
.system_libs = system_libs.items,
|
||||
.emit_h = emit_h_path,
|
||||
.have_libc = have_libc,
|
||||
.have_libcpp = have_libcpp,
|
||||
.link_libc = link_libc,
|
||||
.link_libcpp = link_libcpp,
|
||||
.want_pic = want_pic,
|
||||
.want_sanitize_c = want_sanitize_c,
|
||||
.use_llvm = use_llvm,
|
||||
@ -1000,16 +1035,19 @@ pub fn buildOutputType(
|
||||
.linker_z_defs = linker_z_defs,
|
||||
.stack_size_override = stack_size_override,
|
||||
.strip = strip,
|
||||
.self_exe_path = self_exe_path,
|
||||
.rand = &default_prng.random,
|
||||
.clang_passthrough_mode = arg_mode != .build,
|
||||
}) catch |err| {
|
||||
fatal("unable to initialize module: {}", .{@errorName(err)});
|
||||
fatal("unable to create module: {}", .{@errorName(err)});
|
||||
};
|
||||
defer module.deinit();
|
||||
defer module.destroy();
|
||||
|
||||
const stdin = std.io.getStdIn().inStream();
|
||||
const stderr = std.io.getStdErr().outStream();
|
||||
var repl_buf: [1024]u8 = undefined;
|
||||
|
||||
try updateModule(gpa, &module, zir_out_path);
|
||||
try updateModule(gpa, module, zir_out_path);
|
||||
|
||||
if (build_options.have_llvm and only_pp_or_asm) {
|
||||
// this may include dumping the output to stdout
|
||||
@ -1031,7 +1069,7 @@ pub fn buildOutputType(
|
||||
if (output_mode == .Exe) {
|
||||
try module.makeBinFileWritable();
|
||||
}
|
||||
try updateModule(gpa, &module, zir_out_path);
|
||||
try updateModule(gpa, module, zir_out_path);
|
||||
} else if (mem.eql(u8, actual_line, "exit")) {
|
||||
break;
|
||||
} else if (mem.eql(u8, actual_line, "help")) {
|
||||
@ -1062,12 +1100,10 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
|
||||
full_err_msg.msg,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
std.log.info("Update completed in {} ms", .{update_nanos / std.time.ns_per_ms});
|
||||
}
|
||||
|
||||
if (zir_out_path) |zop| {
|
||||
var new_zir_module = try zir.emit(gpa, module.*);
|
||||
var new_zir_module = try zir.emit(gpa, module);
|
||||
defer new_zir_module.deinit(gpa);
|
||||
|
||||
const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
|
||||
@ -1422,50 +1458,6 @@ pub const info_zen =
|
||||
\\
|
||||
;
|
||||
|
||||
const FileExt = enum {
|
||||
c,
|
||||
cpp,
|
||||
h,
|
||||
ll,
|
||||
bc,
|
||||
assembly,
|
||||
unknown,
|
||||
};
|
||||
|
||||
fn hasCExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".c");
|
||||
}
|
||||
|
||||
fn hasCppExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".C") or
|
||||
mem.endsWith(u8, filename, ".cc") or
|
||||
mem.endsWith(u8, filename, ".cpp") or
|
||||
mem.endsWith(u8, filename, ".cxx");
|
||||
}
|
||||
|
||||
fn hasAsmExt(filename: []const u8) bool {
|
||||
return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
|
||||
}
|
||||
|
||||
fn classify_file_ext(filename: []const u8) FileExt {
|
||||
if (hasCExt(filename)) {
|
||||
return .c;
|
||||
} else if (hasCppExt(filename)) {
|
||||
return .cpp;
|
||||
} else if (mem.endsWith(u8, filename, ".ll")) {
|
||||
return .ll;
|
||||
} else if (mem.endsWith(u8, filename, ".bc")) {
|
||||
return .bc;
|
||||
} else if (hasAsmExt(filename)) {
|
||||
return .assembly;
|
||||
} else if (mem.endsWith(u8, filename, ".h")) {
|
||||
return .h;
|
||||
} else {
|
||||
// TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z
|
||||
return .unknown;
|
||||
}
|
||||
}
|
||||
|
||||
extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
|
||||
|
||||
/// TODO make it so the return value can be !noreturn
|
||||
|
@ -4,60 +4,11 @@ const io = std.io;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const Target = std.Target;
|
||||
const target = @import("target.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const introspect = @import("introspect.zig");
|
||||
|
||||
// TODO this is hard-coded until self-hosted gains this information canonically
|
||||
const available_libcs = [_][]const u8{
|
||||
"aarch64_be-linux-gnu",
|
||||
"aarch64_be-linux-musl",
|
||||
"aarch64_be-windows-gnu",
|
||||
"aarch64-linux-gnu",
|
||||
"aarch64-linux-musl",
|
||||
"aarch64-windows-gnu",
|
||||
"armeb-linux-gnueabi",
|
||||
"armeb-linux-gnueabihf",
|
||||
"armeb-linux-musleabi",
|
||||
"armeb-linux-musleabihf",
|
||||
"armeb-windows-gnu",
|
||||
"arm-linux-gnueabi",
|
||||
"arm-linux-gnueabihf",
|
||||
"arm-linux-musleabi",
|
||||
"arm-linux-musleabihf",
|
||||
"arm-windows-gnu",
|
||||
"i386-linux-gnu",
|
||||
"i386-linux-musl",
|
||||
"i386-windows-gnu",
|
||||
"mips64el-linux-gnuabi64",
|
||||
"mips64el-linux-gnuabin32",
|
||||
"mips64el-linux-musl",
|
||||
"mips64-linux-gnuabi64",
|
||||
"mips64-linux-gnuabin32",
|
||||
"mips64-linux-musl",
|
||||
"mipsel-linux-gnu",
|
||||
"mipsel-linux-musl",
|
||||
"mips-linux-gnu",
|
||||
"mips-linux-musl",
|
||||
"powerpc64le-linux-gnu",
|
||||
"powerpc64le-linux-musl",
|
||||
"powerpc64-linux-gnu",
|
||||
"powerpc64-linux-musl",
|
||||
"powerpc-linux-gnu",
|
||||
"powerpc-linux-musl",
|
||||
"riscv64-linux-gnu",
|
||||
"riscv64-linux-musl",
|
||||
"s390x-linux-gnu",
|
||||
"s390x-linux-musl",
|
||||
"sparc-linux-gnu",
|
||||
"sparcv9-linux-gnu",
|
||||
"wasm32-freestanding-musl",
|
||||
"x86_64-linux-gnu",
|
||||
"x86_64-linux-gnux32",
|
||||
"x86_64-linux-musl",
|
||||
"x86_64-windows-gnu",
|
||||
};
|
||||
|
||||
pub fn cmdTargets(
|
||||
allocator: *Allocator,
|
||||
args: []const []const u8,
|
||||
@ -127,9 +78,13 @@ pub fn cmdTargets(
|
||||
|
||||
try jws.objectField("libc");
|
||||
try jws.beginArray();
|
||||
for (available_libcs) |libc| {
|
||||
for (target.available_libcs) |libc| {
|
||||
const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{
|
||||
@tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
|
||||
});
|
||||
defer allocator.free(tmp);
|
||||
try jws.arrayElem();
|
||||
try jws.emitString(libc);
|
||||
try jws.emitString(tmp);
|
||||
}
|
||||
try jws.endArray();
|
||||
|
||||
|
111
src-self-hosted/target.zig
Normal file
111
src-self-hosted/target.zig
Normal file
@ -0,0 +1,111 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const ArchOsAbi = struct {
|
||||
arch: std.Target.Cpu.Arch,
|
||||
os: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
};
|
||||
|
||||
pub const available_libcs = [_]ArchOsAbi{
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .armeb, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .arm, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .i386, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .i386, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .i386, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc64le, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .powerpc64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .riscv64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .riscv64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .s390x, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .s390x, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .sparc, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .sparcv9, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .x86_64, .os = .windows, .abi = .gnu },
|
||||
};
|
||||
|
||||
pub fn libCGenericName(target: std.Target) [:0]const u8 {
|
||||
if (target.os.tag == .windows)
|
||||
return "mingw";
|
||||
switch (target.abi) {
|
||||
.gnu,
|
||||
.gnuabin32,
|
||||
.gnuabi64,
|
||||
.gnueabi,
|
||||
.gnueabihf,
|
||||
.gnux32,
|
||||
=> return "glibc",
|
||||
.musl,
|
||||
.musleabi,
|
||||
.musleabihf,
|
||||
.none,
|
||||
=> return "musl",
|
||||
.code16,
|
||||
.eabi,
|
||||
.eabihf,
|
||||
.android,
|
||||
.msvc,
|
||||
.itanium,
|
||||
.cygnus,
|
||||
.coreclr,
|
||||
.simulator,
|
||||
.macabi,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn archMuslName(arch: std.Target.Cpu.Arch) [:0]const u8 {
|
||||
switch (arch) {
|
||||
.aarch64, .aarch64_be => return "aarch64",
|
||||
.arm, .armeb => return "arm",
|
||||
.mips, .mipsel => return "mips",
|
||||
.mips64el, .mips64 => return "mips64",
|
||||
.powerpc => return "powerpc",
|
||||
.powerpc64, .powerpc64le => return "powerpc64",
|
||||
.s390x => return "s390x",
|
||||
.i386 => return "i386",
|
||||
.x86_64 => return "x86_64",
|
||||
.riscv64 => return "riscv64",
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn canBuildLibC(target: std.Target) bool {
|
||||
for (available_libcs) |libc| {
|
||||
if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
@ -4,6 +4,7 @@ const Module = @import("Module.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const zir = @import("zir.zig");
|
||||
const Package = @import("Package.zig");
|
||||
const introspect = @import("introspect.zig");
|
||||
const build_options = @import("build_options");
|
||||
const enable_qemu: bool = build_options.enable_qemu;
|
||||
const enable_wine: bool = build_options.enable_wine;
|
||||
@ -406,6 +407,16 @@ pub const TestContext = struct {
|
||||
const root_node = try progress.start("tests", self.cases.items.len);
|
||||
defer root_node.end();
|
||||
|
||||
const zig_lib_dir = try introspect.resolveZigLibDir(std.testing.allocator);
|
||||
defer std.testing.allocator.free(zig_lib_dir);
|
||||
|
||||
const random_seed = blk: {
|
||||
var random_seed: u64 = undefined;
|
||||
try std.crypto.randomBytes(std.mem.asBytes(&random_seed));
|
||||
break :blk random_seed;
|
||||
};
|
||||
var default_prng = std.rand.DefaultPrng.init(random_seed);
|
||||
|
||||
for (self.cases.items) |case| {
|
||||
var prg_node = root_node.start(case.name, case.updates.items.len);
|
||||
prg_node.activate();
|
||||
@ -416,11 +427,18 @@ pub const TestContext = struct {
|
||||
progress.initial_delay_ns = 0;
|
||||
progress.refresh_rate_ns = 0;
|
||||
|
||||
try self.runOneCase(std.testing.allocator, &prg_node, case);
|
||||
try self.runOneCase(std.testing.allocator, &prg_node, case, zig_lib_dir, &default_prng.random);
|
||||
}
|
||||
}
|
||||
|
||||
fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
|
||||
fn runOneCase(
|
||||
self: *TestContext,
|
||||
allocator: *Allocator,
|
||||
root_node: *std.Progress.Node,
|
||||
case: Case,
|
||||
zig_lib_dir: []const u8,
|
||||
rand: *std.rand.Random,
|
||||
) !void {
|
||||
const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
|
||||
const target = target_info.target;
|
||||
|
||||
@ -438,7 +456,9 @@ pub const TestContext = struct {
|
||||
const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
|
||||
const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null, ofmt);
|
||||
|
||||
var module = try Module.init(allocator, .{
|
||||
const module = try Module.create(allocator, .{
|
||||
.zig_lib_dir = zig_lib_dir,
|
||||
.rand = rand,
|
||||
.root_name = "test_case",
|
||||
.target = target,
|
||||
// TODO: support tests for object file building, and library builds
|
||||
@ -453,7 +473,7 @@ pub const TestContext = struct {
|
||||
.keep_source_files_loaded = true,
|
||||
.object_format = ofmt,
|
||||
});
|
||||
defer module.deinit();
|
||||
defer module.destroy();
|
||||
|
||||
for (case.updates.items) |update, update_index| {
|
||||
var update_node = root_node.start("update", 3);
|
||||
|
@ -1700,12 +1700,12 @@ const Parser = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
|
||||
pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
|
||||
var ctx: EmitZIR = .{
|
||||
.allocator = allocator,
|
||||
.decls = .{},
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
.old_module = &old_module,
|
||||
.old_module = old_module,
|
||||
.next_auto_name = 0,
|
||||
.names = std.StringArrayHashMap(void).init(allocator),
|
||||
.primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
|
||||
@ -2539,7 +2539,7 @@ const EmitZIR = struct {
|
||||
return self.emitUnnamedDecl(&fntype_inst.base);
|
||||
},
|
||||
.Int => {
|
||||
const info = ty.intInfo(self.old_module.target());
|
||||
const info = ty.intInfo(self.old_module.getTarget());
|
||||
const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false");
|
||||
const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
|
||||
bits_payload.* = .{ .int = info.bits };
|
||||
|
Loading…
x
Reference in New Issue
Block a user