Commit 193ad413f0
Changed files (11)
src
src-self-hosted
src/main.cpp
@@ -78,7 +78,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" -fno-emit-asm (default) do not output .s (assembly code)\n"
" -femit-llvm-ir produce a .ll file with LLVM IR\n"
" -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"
- " -femit-h generate a C header file (.h)\n"
+ " -femit-h generate a C header file (.h)\n"
" -fno-emit-h (default) do not generate a C header file (.h)\n"
" --libc [file] Provide a file which specifies libc paths\n"
" --name [name] override output name\n"
src-self-hosted/codegen/llvm.zig
@@ -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 });
+}
src-self-hosted/link/Elf.zig
@@ -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();
src-self-hosted/introspect.zig
@@ -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;
-}
src-self-hosted/link.zig
@@ -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{},
src-self-hosted/main.zig
@@ -33,13 +33,14 @@ const usage =
\\
\\Commands:
\\
- \\ build-exe [source] Create executable from source or object files
- \\ build-lib [source] Create library from source or object files
- \\ build-obj [source] Create object from source or assembly
+ \\ build-exe [source] Create executable from source or object files
+ \\ build-lib [source] Create library from source or object files
+ \\ build-obj [source] Create object from source or assembly
\\ cc Use Zig as a drop-in C compiler
\\ 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
+ \\ 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.?),
- });
- for (diags.arch.?.allCpuModels()) |cpu| {
- std.debug.print(" {}\n", .{cpu.name});
+ help: {
+ var help_text = std.ArrayList(u8).init(arena);
+ for (diags.arch.?.allCpuModels()) |cpu| {
+ help_text.writer().print(" {}\n", .{cpu.name}) catch break :help;
+ }
+ std.log.info("Available CPUs for architecture '{}': {}", .{
+ @tagName(diags.arch.?), help_text.items,
+ });
}
- process.exit(1);
+ 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.?),
- });
- for (diags.arch.?.allFeaturesList()) |feature| {
- std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
+ help: {
+ var help_text = std.ArrayList(u8).init(arena);
+ for (diags.arch.?.allFeaturesList()) |feature| {
+ help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help;
+ }
+ std.log.info("Available CPU features for architecture '{}': {}", .{
+ @tagName(diags.arch.?), help_text.items,
+ });
}
- process.exit(1);
+ 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
src-self-hosted/Module.zig
@@ -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,170 +960,232 @@ 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);
-
- const ofmt = options.object_format orelse options.target.getObjectFormat();
-
- // Make a decision on whether to use LLD or our own linker.
- const use_lld = if (options.use_lld) |explicit| explicit else blk: {
- if (!build_options.have_llvm)
+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();
+
+ // Make a decision on whether to use LLD or our own linker.
+ const use_lld = if (options.use_lld) |explicit| explicit else blk: {
+ if (!build_options.have_llvm)
+ break :blk false;
+
+ if (ofmt == .c)
+ break :blk false;
+
+ // Our linker can't handle objects or most advanced options yet.
+ if (options.link_objects.len != 0 or
+ options.c_source_files.len != 0 or
+ options.frameworks.len != 0 or
+ options.system_libs.len != 0 or
+ options.link_libc or options.link_libcpp or
+ options.linker_script != null or options.version_script != null)
+ {
+ break :blk true;
+ }
break :blk false;
+ };
- if (ofmt == .c)
+ // Make a decision on whether to use LLVM or our own backend.
+ const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
+ // We would want to prefer LLVM for release builds when it is available, however
+ // we don't have an LLVM backend yet :)
+ // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
break :blk false;
+ };
- // Our linker can't handle objects or most advanced options yet.
- if (options.link_objects.len != 0 or
- 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.linker_script != null or options.version_script != null)
- {
- break :blk true;
- }
- break :blk false;
- };
-
- // Make a decision on whether to use LLVM or our own backend.
- const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
- // We would want to prefer LLVM for release builds when it is available, however
- // we don't have an LLVM backend yet :)
- // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
- break :blk false;
- };
-
- const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
- const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
- .root_name = root_name,
- .root_pkg = options.root_pkg,
- .target = options.target,
- .output_mode = options.output_mode,
- .link_mode = options.link_mode orelse .Static,
- .object_format = ofmt,
- .optimize_mode = options.optimize_mode,
- .use_lld = use_lld,
- .use_llvm = use_llvm,
- .objects = options.link_objects,
- .frameworks = options.frameworks,
- .framework_dirs = options.framework_dirs,
- .system_libs = options.system_libs,
- .lib_dirs = options.lib_dirs,
- .rpath_list = options.rpath_list,
- .strip = options.strip,
- });
- errdefer bin_file.destroy();
-
- const root_scope = blk: {
- if (options.root_pkg) |root_pkg| {
- if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
- const root_scope = try gpa.create(Scope.File);
- root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .root_container = .{
- .file_scope = root_scope,
+ const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
+ const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
+ .root_name = root_name,
+ .root_pkg = options.root_pkg,
+ .target = options.target,
+ .output_mode = options.output_mode,
+ .link_mode = options.link_mode orelse .Static,
+ .object_format = ofmt,
+ .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,
+ .system_libs = options.system_libs,
+ .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")) {
+ const root_scope = try gpa.create(Scope.File);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ .root_container = .{
+ .file_scope = root_scope,
+ .decls = .{},
+ },
+ };
+ break :blk &root_scope.base;
+ } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
+ const root_scope = try gpa.create(Scope.ZIRModule);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
.decls = .{},
- },
- };
- break :blk &root_scope.base;
- } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
- const root_scope = try gpa.create(Scope.ZIRModule);
- root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .decls = .{},
- };
- break :blk &root_scope.base;
+ };
+ break :blk &root_scope.base;
+ } else {
+ unreachable;
+ }
} else {
- unreachable;
+ const root_scope = try gpa.create(Scope.None);
+ root_scope.* = .{};
+ break :blk &root_scope.base;
}
- } else {
- const root_scope = try gpa.create(Scope.None);
- root_scope.* = .{};
- break :blk &root_scope.base;
- }
- };
+ };
- // We put everything into the cache hash except for the root source file, because we want to
- // 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");
- errdefer cache.release();
-
- // Now we will prepare hash state initializations to avoid redundantly computing hashes.
- // First we add common things between things that apply to zig source and all c source files.
- cache.addBytes(build_options.version);
- cache.add(options.optimize_mode);
- cache.add(options.target.cpu.arch);
- cache.addBytes(options.target.cpu.model.name);
- cache.add(options.target.cpu.features.ints);
- cache.add(options.target.os.tag);
- switch (options.target.os.tag) {
- .linux => {
- cache.add(options.target.os.version_range.linux.range.min);
- cache.add(options.target.os.version_range.linux.range.max);
- cache.add(options.target.os.version_range.linux.glibc);
- },
- .windows => {
- cache.add(options.target.os.version_range.windows.min);
- cache.add(options.target.os.version_range.windows.max);
- },
- .freebsd,
- .macosx,
- .ios,
- .tvos,
- .watchos,
- .netbsd,
- .openbsd,
- .dragonfly,
- => {
- cache.add(options.target.os.version_range.semver.min);
- cache.add(options.target.os.version_range.semver.max);
- },
- else => {},
- }
- cache.add(options.target.abi);
- cache.add(ofmt);
- // TODO PIC (see detect_pic from codegen.cpp)
- cache.add(bin_file.options.link_mode);
- cache.add(options.strip);
-
- // Make a decision on whether to use Clang for translate-c and compiling C files.
- const use_clang = if (options.use_clang) |explicit| explicit else blk: {
- if (build_options.have_llvm) {
- // Can't use it if we don't have it!
- break :blk false;
+ // We put everything into the cache hash except for the root source file, because we want to
+ // 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_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.
+ // First we add common things between things that apply to zig source and all c source files.
+ cache.addBytes(build_options.version);
+ cache.add(options.optimize_mode);
+ cache.add(options.target.cpu.arch);
+ cache.addBytes(options.target.cpu.model.name);
+ cache.add(options.target.cpu.features.ints);
+ cache.add(options.target.os.tag);
+ switch (options.target.os.tag) {
+ .linux => {
+ cache.add(options.target.os.version_range.linux.range.min);
+ cache.add(options.target.os.version_range.linux.range.max);
+ cache.add(options.target.os.version_range.linux.glibc);
+ },
+ .windows => {
+ cache.add(options.target.os.version_range.windows.min);
+ cache.add(options.target.os.version_range.windows.max);
+ },
+ .freebsd,
+ .macosx,
+ .ios,
+ .tvos,
+ .watchos,
+ .netbsd,
+ .openbsd,
+ .dragonfly,
+ => {
+ cache.add(options.target.os.version_range.semver.min);
+ cache.add(options.target.os.version_range.semver.max);
+ },
+ else => {},
}
- // It's not planned to do our own translate-c or C compilation.
- break :blk true;
+ cache.add(options.target.abi);
+ cache.add(ofmt);
+ // TODO PIC (see detect_pic from codegen.cpp)
+ cache.add(bin_file.options.link_mode);
+ cache.add(options.strip);
+
+ // Make a decision on whether to use Clang for translate-c and compiling C files.
+ const use_clang = if (options.use_clang) |explicit| explicit else blk: {
+ if (build_options.have_llvm) {
+ // Can't use it if we don't have it!
+ break :blk false;
+ }
+ // It's not planned to do our own translate-c or C compilation.
+ break :blk true;
+ };
+
+ 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;
};
- var c_object_table = std.AutoArrayHashMapUnmanaged(*CObject, void){};
- errdefer {
- for (c_object_table.items()) |entry| entry.key.destroy(gpa);
- c_object_table.deinit(gpa);
- }
+ 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, {});
- }
-
- 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,
- };
+ mod.c_object_table.putAssumeCapacityNoClobber(c_object, {});
+ }
+
+ 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,29 +1505,335 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
c_object.status = .{ .new = {} };
},
}
- if (!build_options.have_llvm) {
- 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",
- .{},
- ));
- c_object.status = .{ .failure = "" };
+ 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,
+ "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 {
const tracy = trace(@src());
defer tracy.end();
@@ -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;
+}
src-self-hosted/print_targets.zig
@@ -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();
src-self-hosted/target.zig
@@ -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;
+}
src-self-hosted/test.zig
@@ -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);
src-self-hosted/zir.zig
@@ -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 };