zig/src/target.zig
Andrew Kelley 8ce880ca75 avoid calling into stage1 backend when AstGen fails
The motivation for this commit is that there exists source files which
produce ast-check errors, but crash stage1 or otherwise trigger stage1
bugs. Previously to this commit, Zig would run AstGen, collect the
compile errors, run stage1, report stage1 compile errors and exit if
any, and then report AstGen compile errors.

The main change in this commit is to report AstGen errors prior to
invoking stage1, and in fact if any AstGen errors occur, do not invoke
stage1 at all.

This caused most of the compile error tests to fail due to things such
as unused local variables and mismatched stage1/stage2 error messages.
It was taking a long time to update the test cases one-by-one, so I
took this opportunity to unify the stage1 and stage2 testing harness,
specifically with regards to compile errors. In this way we can start
keeping track of which tests pass for 1, 2, or both.
`zig build test-compile-errors` no longer works; it is now integrated
into `zig build test-stage2`.

This is one step closer to executing compile error tests in parallel; in
fact the ThreadPool object is already in scope.

There are some cases where the stage1 compile errors were actually
better; those are left failing in this commit, to be addressed in a
follow-up commit.

Other changes in this commit:

 * build.zig: improve support for -Dstage1 used with the test step.
 * AstGen: minor cosmetic changes to error messages.
 * stage2: add -fstage1 and -fno-stage1 flags. This now allows one to
   download a binary of the zig compiler and use the llvm backend of
   self-hosted. This was also needed for hooking up the test harness.
   However, I realized that stage1 calls exit() and also has memory
   leaks, so had to complicate the test harness by not using this flag
   after all and instead invoking as a child process.
   - These CLI flags will disappear once we start shipping the
     self-hosted compiler as the main compiler. Until then, they can be
     used to try out the work-in-progress stage2.
 * stage2: select the LLVM backend by default for release modes, as long
   as the target architecture is supported by LLVM.
 * test harness: support setting the optimize mode
2021-07-02 13:27:28 -07:00

478 lines
14 KiB
Zig

const std = @import("std");
const llvm = @import("codegen/llvm/bindings.zig");
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 = .aarch64, .os = .macos, .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 = .thumb, .os = .linux, .abi = .gnueabi },
.{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
.{ .arch = .thumb, .os = .linux, .abi = .musleabi },
.{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
.{ .arch = .arm, .os = .windows, .abi = .gnu },
.{ .arch = .csky, .os = .linux, .abi = .gnueabi },
.{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
.{ .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 = .wasm32, .os = .wasi, .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 },
.{ .arch = .x86_64, .os = .macos, .abi = .gnu },
};
pub fn libCGenericName(target: std.Target) [:0]const u8 {
switch (target.os.tag) {
.windows => return "mingw",
.macos, .ios, .tvos, .watchos => return "darwin",
else => {},
}
switch (target.abi) {
.gnu,
.gnuabin32,
.gnuabi64,
.gnueabi,
.gnueabihf,
.gnux32,
.gnuilp32,
=> return "glibc",
.musl,
.musleabi,
.musleabihf,
.none,
=> return "musl",
.code16,
.eabi,
.eabihf,
.android,
.msvc,
.itanium,
.cygnus,
.coreclr,
.simulator,
.macabi,
=> 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;
}
pub fn cannotDynamicLink(target: std.Target) bool {
return switch (target.os.tag) {
.freestanding, .other => true,
else => false,
};
}
/// On Darwin, we always link libSystem which contains libc.
/// Similarly on FreeBSD and NetBSD we always link system libc
/// since this is the stable syscall interface.
pub fn osRequiresLibC(target: std.Target) bool {
return target.os.requiresLibC();
}
pub fn libcNeedsLibUnwind(target: std.Target) bool {
return switch (target.os.tag) {
.macos,
.ios,
.watchos,
.tvos,
.freestanding,
.wasi, // Wasm/WASI currently doesn't offer support for libunwind, so don't link it.
=> false,
.windows => target.abi != .msvc,
else => true,
};
}
pub fn requiresPIE(target: std.Target) bool {
return target.isAndroid() or target.isDarwin() or target.os.tag == .openbsd;
}
/// This function returns whether non-pic code is completely invalid on the given target.
pub fn requiresPIC(target: std.Target, linking_libc: bool) bool {
return target.isAndroid() or
target.os.tag == .windows or target.os.tag == .uefi or
osRequiresLibC(target) or
(linking_libc and target.isGnuLibC());
}
/// This is not whether the target supports Position Independent Code, but whether the -fPIC
/// C compiler argument is valid to Clang.
pub fn supports_fpic(target: std.Target) bool {
return target.os.tag != .windows;
}
pub fn isSingleThreaded(target: std.Target) bool {
return target.isWasm();
}
/// Valgrind supports more, but Zig does not support them yet.
pub fn hasValgrindSupport(target: std.Target) bool {
switch (target.cpu.arch) {
.x86_64 => {
return target.os.tag == .linux or target.isDarwin() or target.os.tag == .solaris or
(target.os.tag == .windows and target.abi != .msvc);
},
else => return false,
}
}
/// The set of targets that LLVM has non-experimental support for.
/// Used to select between LLVM backend and self-hosted backend when compiling in
/// release modes.
pub fn hasLlvmSupport(target: std.Target) bool {
return switch (target.cpu.arch) {
.arm,
.armeb,
.aarch64,
.aarch64_be,
.aarch64_32,
.arc,
.avr,
.bpfel,
.bpfeb,
.csky,
.hexagon,
.mips,
.mipsel,
.mips64,
.mips64el,
.msp430,
.powerpc,
.powerpcle,
.powerpc64,
.powerpc64le,
.r600,
.amdgcn,
.riscv32,
.riscv64,
.sparc,
.sparcv9,
.sparcel,
.s390x,
.tce,
.tcele,
.thumb,
.thumbeb,
.i386,
.x86_64,
.xcore,
.nvptx,
.nvptx64,
.le32,
.le64,
.amdil,
.amdil64,
.hsail,
.hsail64,
.spir,
.spir64,
.kalimba,
.shave,
.lanai,
.wasm32,
.wasm64,
.renderscript32,
.renderscript64,
.ve,
=> true,
.spu_2,
.spirv32,
.spirv64,
=> false,
};
}
pub fn supportsStackProbing(target: std.Target) bool {
return target.os.tag != .windows and target.os.tag != .uefi and
(target.cpu.arch == .i386 or target.cpu.arch == .x86_64);
}
pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
return switch (os_tag) {
.freestanding, .other, .opencl, .glsl450, .vulkan => .UnknownOS,
.windows, .uefi => .Win32,
.ananas => .Ananas,
.cloudabi => .CloudABI,
.dragonfly => .DragonFly,
.freebsd => .FreeBSD,
.fuchsia => .Fuchsia,
.ios => .IOS,
.kfreebsd => .KFreeBSD,
.linux => .Linux,
.lv2 => .Lv2,
.macos => .MacOSX,
.netbsd => .NetBSD,
.openbsd => .OpenBSD,
.solaris => .Solaris,
.zos => .ZOS,
.haiku => .Haiku,
.minix => .Minix,
.rtems => .RTEMS,
.nacl => .NaCl,
.aix => .AIX,
.cuda => .CUDA,
.nvcl => .NVCL,
.amdhsa => .AMDHSA,
.ps4 => .PS4,
.elfiamcu => .ELFIAMCU,
.tvos => .TvOS,
.watchos => .WatchOS,
.mesa3d => .Mesa3D,
.contiki => .Contiki,
.amdpal => .AMDPAL,
.hermit => .HermitCore,
.hurd => .Hurd,
.wasi => .WASI,
.emscripten => .Emscripten,
};
}
pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
return switch (arch_tag) {
.arm => .arm,
.armeb => .armeb,
.aarch64 => .aarch64,
.aarch64_be => .aarch64_be,
.aarch64_32 => .aarch64_32,
.arc => .arc,
.avr => .avr,
.bpfel => .bpfel,
.bpfeb => .bpfeb,
.csky => .csky,
.hexagon => .hexagon,
.mips => .mips,
.mipsel => .mipsel,
.mips64 => .mips64,
.mips64el => .mips64el,
.msp430 => .msp430,
.powerpc => .ppc,
.powerpcle => .ppcle,
.powerpc64 => .ppc64,
.powerpc64le => .ppc64le,
.r600 => .r600,
.amdgcn => .amdgcn,
.riscv32 => .riscv32,
.riscv64 => .riscv64,
.sparc => .sparc,
.sparcv9 => .sparcv9,
.sparcel => .sparcel,
.s390x => .systemz,
.tce => .tce,
.tcele => .tcele,
.thumb => .thumb,
.thumbeb => .thumbeb,
.i386 => .x86,
.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, .spirv32, .spirv64 => .UnknownArch,
};
}
fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
if (ignore_case) {
return std.ascii.eqlIgnoreCase(a, b);
} else {
return std.mem.eql(u8, a, b);
}
}
pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
if (eqlIgnoreCase(ignore_case, name, "c"))
return true;
if (target.isMinGW()) {
if (eqlIgnoreCase(ignore_case, name, "m"))
return true;
return false;
}
if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) {
if (eqlIgnoreCase(ignore_case, name, "m"))
return true;
if (eqlIgnoreCase(ignore_case, name, "rt"))
return true;
if (eqlIgnoreCase(ignore_case, name, "pthread"))
return true;
if (eqlIgnoreCase(ignore_case, name, "crypt"))
return true;
if (eqlIgnoreCase(ignore_case, name, "util"))
return true;
if (eqlIgnoreCase(ignore_case, name, "xnet"))
return true;
if (eqlIgnoreCase(ignore_case, name, "resolv"))
return true;
if (eqlIgnoreCase(ignore_case, name, "dl"))
return true;
}
if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System"))
return true;
return false;
}
pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool {
const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
return eqlIgnoreCase(ignore_case, name, "c++") or
eqlIgnoreCase(ignore_case, name, "stdc++") or
eqlIgnoreCase(ignore_case, name, "c++abi");
}
pub fn hasDebugInfo(target: std.Target) bool {
return !target.cpu.arch.isWasm();
}
pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.Mode {
if (target.cpu.arch.isWasm() and target.os.tag == .freestanding) {
return .ReleaseSmall;
} else {
return .ReleaseFast;
}
}
pub fn hasRedZone(target: std.Target) bool {
return switch (target.cpu.arch) {
.x86_64,
.i386,
.powerpc,
.powerpc64,
.powerpc64le,
.aarch64,
.aarch64_be,
.aarch64_32,
=> true,
else => false,
};
}
pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
// The linking order of these is significant and should match the order other
// c compilers such as gcc or clang use.
return switch (target.os.tag) {
.netbsd, .openbsd => &[_][]const u8{
"-lm",
"-lpthread",
"-lc",
"-lutil",
},
.haiku => &[_][]const u8{
"-lm",
"-lroot",
"-lpthread",
"-lc",
},
else => switch (target.abi) {
.android => &[_][]const u8{
"-lm",
"-lc",
"-ldl",
},
else => &[_][]const u8{
"-lm",
"-lpthread",
"-lc",
"-ldl",
"-lrt",
"-lutil",
},
},
};
}
pub fn clangMightShellOutForAssembly(target: std.Target) bool {
// Clang defaults to using the system assembler over the internal one
// when targeting a non-BSD OS.
return target.cpu.arch.isSPARC();
}
/// Each backend architecture in Clang has a different codepath which may or may not
/// support an -mcpu flag.
pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {
return switch (target.cpu.arch) {
.arm, .armeb, .thumb, .thumbeb => true,
else => false,
};
}
pub fn needUnwindTables(target: std.Target) bool {
return target.os.tag == .windows;
}