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:
Andrew Kelley 2020-09-09 00:05:38 -07:00
parent c99e34a00e
commit 193ad413f0
11 changed files with 1076 additions and 407 deletions

File diff suppressed because it is too large Load Diff

View 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 });
}

View File

@ -93,46 +93,3 @@ pub fn openGlobalCacheDir() !fs.Dir {
const path_name = try resolveGlobalCacheDir(&fba.allocator); const path_name = try resolveGlobalCacheDir(&fba.allocator);
return fs.cwd().makeOpenPath(path_name, .{}); 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;
}

View File

@ -35,6 +35,9 @@ pub const Options = struct {
/// other objects. /// other objects.
/// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
use_llvm: bool = false, use_llvm: bool = false,
link_libc: bool = false,
link_libcpp: bool = false,
function_sections: bool = false,
objects: []const []const u8 = &[0][]const u8{}, objects: []const []const u8 = &[0][]const u8{},
framework_dirs: []const []const u8 = &[0][]const u8{}, framework_dirs: []const []const u8 = &[0][]const u8{},

View File

@ -219,8 +219,11 @@ pub const SrcFn = struct {
pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
assert(options.object_format == .elf); assert(options.object_format == .elf);
if (options.use_llvm) return error.LLVM_BackendIsTODO_ForELF; // TODO if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
if (options.use_lld) return error.LLD_LinkingIsTODOForELF; // 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) }); const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
errdefer file.close(); errdefer file.close();
@ -741,8 +744,21 @@ pub const abbrev_base_type = 4;
pub const abbrev_pad1 = 5; pub const abbrev_pad1 = 5;
pub const abbrev_parameter = 6; pub const abbrev_parameter = 6;
/// Commit pending changes and write headers.
pub fn flush(self: *Elf, module: *Module) !void { 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 target_endian = self.base.options.target.cpu.arch.endian();
const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
const ptr_width_bytes: u8 = self.ptrWidthBytes(); const ptr_width_bytes: u8 = self.ptrWidthBytes();

View File

@ -33,13 +33,14 @@ const usage =
\\ \\
\\Commands: \\Commands:
\\ \\
\\ build-exe [source] Create executable from source or object files \\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library 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-obj [source] Create object from source or assembly
\\ cc Use Zig as a drop-in C compiler \\ cc Use Zig as a drop-in C compiler
\\ c++ 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 \\ 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 \\ targets List available compilation targets
\\ version Print version number and exit \\ version Print version number and exit
\\ zen Print zen of zig 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( pub fn log(
comptime level: std.log.Level, comptime level: std.log.Level,
comptime scope: @TypeOf(.EnumLiteral), comptime scope: @TypeOf(.EnumLiteral),
comptime format: []const u8, comptime format: []const u8,
args: anytype, args: anytype,
) void { ) 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 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 scope_name = @tagName(scope);
const ok = comptime for (build_options.log_scopes) |log_scope| { const ok = comptime for (build_options.log_scopes) |log_scope| {
@ -67,13 +74,15 @@ pub fn log(
return; return;
} }
// We only recognize 4 log levels in this application.
const level_txt = switch (level) { const level_txt = switch (level) {
.emerg => "error", .emerg, .alert, .crit => "error",
.warn => "warning", .err, .warn => "warning",
else => @tagName(level), .notice, .info => "info",
.debug => "debug",
}; };
const prefix1 = level_txt ++ ": "; const prefix1 = level_txt;
const prefix2 = if (scope == .default) "" else "(" ++ @tagName(scope) ++ "): "; const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
// Print the message to stderr, silently ignoring any errors // Print the message to stderr, silently ignoring any errors
std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args); std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args);
@ -93,8 +102,8 @@ pub fn main() !void {
const args = try process.argsAlloc(arena); const args = try process.argsAlloc(arena);
if (args.len <= 1) { if (args.len <= 1) {
std.debug.print("expected command argument\n\n{}", .{usage}); std.log.info("{}", .{usage});
process.exit(1); fatal("expected command argument", .{});
} }
const cmd = args[1]; const cmd = args[1];
@ -109,6 +118,8 @@ pub fn main() !void {
return buildOutputType(gpa, arena, args, .cc); return buildOutputType(gpa, arena, args, .cc);
} else if (mem.eql(u8, cmd, "c++")) { } else if (mem.eql(u8, cmd, "c++")) {
return buildOutputType(gpa, arena, args, .cpp); 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 } else if (mem.eql(u8, cmd, "clang") or
mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) 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")) { } else if (mem.eql(u8, cmd, "help")) {
try io.getStdOut().writeAll(usage); try io.getStdOut().writeAll(usage);
} else { } else {
std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage }); std.log.info("{}", .{usage});
process.exit(1); fatal("unknown command: {}", .{args[1]});
} }
} }
@ -223,6 +234,7 @@ pub fn buildOutputType(
build: std.builtin.OutputMode, build: std.builtin.OutputMode,
cc, cc,
cpp, cpp,
translate_c,
}, },
) !void { ) !void {
var color: Color = .Auto; var color: Color = .Auto;
@ -251,8 +263,8 @@ pub fn buildOutputType(
var emit_h: Emit = undefined; var emit_h: Emit = undefined;
var ensure_libc_on_non_freestanding = false; var ensure_libc_on_non_freestanding = false;
var ensure_libcpp_on_non_freestanding = false; var ensure_libcpp_on_non_freestanding = false;
var have_libc = false; var link_libc = false;
var have_libcpp = false; var link_libcpp = false;
var want_native_include_dirs = false; var want_native_include_dirs = false;
var enable_cache: ?bool = null; var enable_cache: ?bool = null;
var want_pic: ?bool = null; var want_pic: ?bool = null;
@ -298,13 +310,20 @@ pub fn buildOutputType(
var frameworks = std.ArrayList([]const u8).init(gpa); var frameworks = std.ArrayList([]const u8).init(gpa);
defer frameworks.deinit(); defer frameworks.deinit();
if (arg_mode == .build) { if (arg_mode == .build or arg_mode == .translate_c) {
output_mode = arg_mode.build; output_mode = switch (arg_mode) {
emit_h = switch (output_mode) { .build => |m| m,
.Exe => .no, .translate_c => .Obj,
.Obj, .Lib => .yes_default_path, 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..]; const args = all_args[2..];
var i: usize = 0; var i: usize = 0;
while (i < args.len) : (i += 1) { while (i < args.len) : (i += 1) {
@ -499,7 +518,7 @@ pub fn buildOutputType(
mem.endsWith(u8, arg, ".lib")) mem.endsWith(u8, arg, ".lib"))
{ {
try link_objects.append(arg); 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); try c_source_files.append(arg);
} else if (mem.endsWith(u8, arg, ".so") or } else if (mem.endsWith(u8, arg, ".so") or
mem.endsWith(u8, arg, ".dylib") or mem.endsWith(u8, arg, ".dylib") or
@ -543,7 +562,7 @@ pub fn buildOutputType(
try clang_argv.appendSlice(it.other_args); try clang_argv.appendSlice(it.other_args);
}, },
.positional => { .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) { switch (file_ext) {
.assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg), .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg),
.unknown => try link_objects.append(it.only_arg), .unknown => try link_objects.append(it.only_arg),
@ -819,28 +838,28 @@ pub fn buildOutputType(
.diagnostics = &diags, .diagnostics = &diags,
}) catch |err| switch (err) { }) catch |err| switch (err) {
error.UnknownCpuModel => { error.UnknownCpuModel => {
std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ help: {
diags.cpu_name.?, var help_text = std.ArrayList(u8).init(arena);
@tagName(diags.arch.?), for (diags.arch.?.allCpuModels()) |cpu| {
}); help_text.writer().print(" {}\n", .{cpu.name}) catch break :help;
for (diags.arch.?.allCpuModels()) |cpu| { }
std.debug.print(" {}\n", .{cpu.name}); std.log.info("Available CPUs for architecture '{}': {}", .{
@tagName(diags.arch.?), help_text.items,
});
} }
process.exit(1); fatal("Unknown CPU: '{}'", .{diags.cpu_name.?});
}, },
error.UnknownCpuFeature => { error.UnknownCpuFeature => {
std.debug.print( help: {
\\Unknown CPU feature: '{}' var help_text = std.ArrayList(u8).init(arena);
\\Available CPU features for architecture '{}': for (diags.arch.?.allFeaturesList()) |feature| {
\\ help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help;
, .{ }
diags.unknown_feature_name, std.log.info("Available CPU features for architecture '{}': {}", .{
@tagName(diags.arch.?), @tagName(diags.arch.?), help_text.items,
}); });
for (diags.arch.?.allFeaturesList()) |feature| {
std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
} }
process.exit(1); fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name});
}, },
else => |e| return e, else => |e| return e,
}; };
@ -849,14 +868,16 @@ pub fn buildOutputType(
if (target_info.cpu_detection_unimplemented) { if (target_info.cpu_detection_unimplemented) {
// TODO We want to just use detected_info.target but implementing // TODO We want to just use detected_info.target but implementing
// CPU model & feature detection is todo so here we rely on LLVM. // 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", .{}); fatal("CPU features detection is not yet available for this system without LLVM extensions", .{});
} }
if (target_info.target.os.tag != .freestanding) { if (target_info.target.os.tag != .freestanding) {
if (ensure_libc_on_non_freestanding) if (ensure_libc_on_non_freestanding)
have_libc = true; link_libc = true;
if (ensure_libcpp_on_non_freestanding) 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 // 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) { while (i < system_libs.items.len) {
const lib_name = system_libs.items[i]; const lib_name = system_libs.items[i];
if (is_libc_lib_name(target_info.target, lib_name)) { if (is_libc_lib_name(target_info.target, lib_name)) {
have_libc = true; link_libc = true;
_ = system_libs.orderedRemove(i); _ = system_libs.orderedRemove(i);
continue; continue;
} }
if (is_libcpp_lib_name(target_info.target, lib_name)) { if (is_libcpp_lib_name(target_info.target, lib_name)) {
have_libcpp = true; link_libcpp = true;
_ = system_libs.orderedRemove(i); _ = system_libs.orderedRemove(i);
continue; continue;
} }
@ -960,7 +981,21 @@ pub fn buildOutputType(
.yes_default_path => try std.fmt.allocPrint(arena, "{}.h", .{root_name}), .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, .root_name = root_name,
.target = target_info.target, .target = target_info.target,
.output_mode = output_mode, .output_mode = output_mode,
@ -980,8 +1015,8 @@ pub fn buildOutputType(
.frameworks = frameworks.items, .frameworks = frameworks.items,
.system_libs = system_libs.items, .system_libs = system_libs.items,
.emit_h = emit_h_path, .emit_h = emit_h_path,
.have_libc = have_libc, .link_libc = link_libc,
.have_libcpp = have_libcpp, .link_libcpp = link_libcpp,
.want_pic = want_pic, .want_pic = want_pic,
.want_sanitize_c = want_sanitize_c, .want_sanitize_c = want_sanitize_c,
.use_llvm = use_llvm, .use_llvm = use_llvm,
@ -1000,16 +1035,19 @@ pub fn buildOutputType(
.linker_z_defs = linker_z_defs, .linker_z_defs = linker_z_defs,
.stack_size_override = stack_size_override, .stack_size_override = stack_size_override,
.strip = strip, .strip = strip,
.self_exe_path = self_exe_path,
.rand = &default_prng.random,
.clang_passthrough_mode = arg_mode != .build,
}) catch |err| { }) 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 stdin = std.io.getStdIn().inStream();
const stderr = std.io.getStdErr().outStream(); const stderr = std.io.getStdErr().outStream();
var repl_buf: [1024]u8 = undefined; 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) { if (build_options.have_llvm and only_pp_or_asm) {
// this may include dumping the output to stdout // this may include dumping the output to stdout
@ -1031,7 +1069,7 @@ pub fn buildOutputType(
if (output_mode == .Exe) { if (output_mode == .Exe) {
try module.makeBinFileWritable(); 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")) { } else if (mem.eql(u8, actual_line, "exit")) {
break; break;
} else if (mem.eql(u8, actual_line, "help")) { } 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, full_err_msg.msg,
}); });
} }
} else {
std.log.info("Update completed in {} ms", .{update_nanos / std.time.ns_per_ms});
} }
if (zir_out_path) |zop| { 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); defer new_zir_module.deinit(gpa);
const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); 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; extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
/// TODO make it so the return value can be !noreturn /// TODO make it so the return value can be !noreturn

View File

@ -4,60 +4,11 @@ const io = std.io;
const mem = std.mem; const mem = std.mem;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Target = std.Target; const Target = std.Target;
const target = @import("target.zig");
const assert = std.debug.assert; const assert = std.debug.assert;
const introspect = @import("introspect.zig"); 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( pub fn cmdTargets(
allocator: *Allocator, allocator: *Allocator,
args: []const []const u8, args: []const []const u8,
@ -127,9 +78,13 @@ pub fn cmdTargets(
try jws.objectField("libc"); try jws.objectField("libc");
try jws.beginArray(); 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.arrayElem();
try jws.emitString(libc); try jws.emitString(tmp);
} }
try jws.endArray(); try jws.endArray();

111
src-self-hosted/target.zig Normal file
View 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;
}

View File

@ -4,6 +4,7 @@ const Module = @import("Module.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const zir = @import("zir.zig"); const zir = @import("zir.zig");
const Package = @import("Package.zig"); const Package = @import("Package.zig");
const introspect = @import("introspect.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
const enable_qemu: bool = build_options.enable_qemu; const enable_qemu: bool = build_options.enable_qemu;
const enable_wine: bool = build_options.enable_wine; 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); const root_node = try progress.start("tests", self.cases.items.len);
defer root_node.end(); 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| { for (self.cases.items) |case| {
var prg_node = root_node.start(case.name, case.updates.items.len); var prg_node = root_node.start(case.name, case.updates.items.len);
prg_node.activate(); prg_node.activate();
@ -416,11 +427,18 @@ pub const TestContext = struct {
progress.initial_delay_ns = 0; progress.initial_delay_ns = 0;
progress.refresh_rate_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_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
const target = target_info.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 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); 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", .root_name = "test_case",
.target = target, .target = target,
// TODO: support tests for object file building, and library builds // TODO: support tests for object file building, and library builds
@ -453,7 +473,7 @@ pub const TestContext = struct {
.keep_source_files_loaded = true, .keep_source_files_loaded = true,
.object_format = ofmt, .object_format = ofmt,
}); });
defer module.deinit(); defer module.destroy();
for (case.updates.items) |update, update_index| { for (case.updates.items) |update, update_index| {
var update_node = root_node.start("update", 3); var update_node = root_node.start("update", 3);

View File

@ -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 = .{ var ctx: EmitZIR = .{
.allocator = allocator, .allocator = allocator,
.decls = .{}, .decls = .{},
.arena = std.heap.ArenaAllocator.init(allocator), .arena = std.heap.ArenaAllocator.init(allocator),
.old_module = &old_module, .old_module = old_module,
.next_auto_name = 0, .next_auto_name = 0,
.names = std.StringArrayHashMap(void).init(allocator), .names = std.StringArrayHashMap(void).init(allocator),
.primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).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); return self.emitUnnamedDecl(&fntype_inst.base);
}, },
.Int => { .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 signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false");
const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64); const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
bits_payload.* = .{ .int = info.bits }; bits_payload.* = .{ .int = info.bits };

View File

@ -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" " -fno-emit-asm (default) do not output .s (assembly code)\n"
" -femit-llvm-ir produce a .ll file with LLVM IR\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" " -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" " -fno-emit-h (default) do not generate a C header file (.h)\n"
" --libc [file] Provide a file which specifies libc paths\n" " --libc [file] Provide a file which specifies libc paths\n"
" --name [name] override output name\n" " --name [name] override output name\n"