mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-07 14:24:43 +00:00
530 lines
15 KiB
Zig
530 lines
15 KiB
Zig
const std = @import("std");
|
|
const Type = @import("type.zig").Type;
|
|
const AddressSpace = std.builtin.AddressSpace;
|
|
const Alignment = @import("InternPool.zig").Alignment;
|
|
const Feature = @import("Module.zig").Feature;
|
|
|
|
pub const default_stack_protector_buffer_size = 4;
|
|
|
|
pub fn cannotDynamicLink(target: std.Target) bool {
|
|
return switch (target.os.tag) {
|
|
.freestanding, .other => true,
|
|
else => target.isSpirV(),
|
|
};
|
|
}
|
|
|
|
/// 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 and target.os.tag != .uefi;
|
|
}
|
|
|
|
pub fn alwaysSingleThreaded(target: std.Target) bool {
|
|
_ = target;
|
|
return false;
|
|
}
|
|
|
|
pub fn defaultSingleThreaded(target: std.Target) bool {
|
|
return switch (target.cpu.arch) {
|
|
.wasm32, .wasm64 => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Valgrind supports more, but Zig does not support them yet.
|
|
pub fn hasValgrindSupport(target: std.Target) bool {
|
|
switch (target.cpu.arch) {
|
|
.x86,
|
|
.x86_64,
|
|
.aarch64,
|
|
.aarch64_32,
|
|
.aarch64_be,
|
|
=> {
|
|
return target.os.tag == .linux or target.os.tag == .solaris or target.os.tag == .illumos 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, ofmt: std.Target.ObjectFormat) bool {
|
|
switch (ofmt) {
|
|
// LLVM does not support these object formats:
|
|
.c,
|
|
.plan9,
|
|
=> return false,
|
|
|
|
.coff,
|
|
.elf,
|
|
.macho,
|
|
.wasm,
|
|
.spirv,
|
|
.hex,
|
|
.raw,
|
|
.nvptx,
|
|
.dxcontainer,
|
|
=> {},
|
|
}
|
|
|
|
return switch (target.cpu.arch) {
|
|
.arm,
|
|
.armeb,
|
|
.aarch64,
|
|
.aarch64_be,
|
|
.aarch64_32,
|
|
.arc,
|
|
.avr,
|
|
.bpfel,
|
|
.bpfeb,
|
|
.csky,
|
|
.dxil,
|
|
.hexagon,
|
|
.loongarch32,
|
|
.loongarch64,
|
|
.m68k,
|
|
.mips,
|
|
.mipsel,
|
|
.mips64,
|
|
.mips64el,
|
|
.msp430,
|
|
.powerpc,
|
|
.powerpcle,
|
|
.powerpc64,
|
|
.powerpc64le,
|
|
.r600,
|
|
.amdgcn,
|
|
.riscv32,
|
|
.riscv64,
|
|
.sparc,
|
|
.sparc64,
|
|
.sparcel,
|
|
.s390x,
|
|
.tce,
|
|
.tcele,
|
|
.thumb,
|
|
.thumbeb,
|
|
.x86,
|
|
.x86_64,
|
|
.xcore,
|
|
.xtensa,
|
|
.nvptx,
|
|
.nvptx64,
|
|
.le32,
|
|
.le64,
|
|
.amdil,
|
|
.amdil64,
|
|
.hsail,
|
|
.hsail64,
|
|
.spir,
|
|
.spir64,
|
|
.spirv32,
|
|
.spirv64,
|
|
.kalimba,
|
|
.shave,
|
|
.lanai,
|
|
.wasm32,
|
|
.wasm64,
|
|
.renderscript32,
|
|
.renderscript64,
|
|
.ve,
|
|
=> true,
|
|
|
|
.spu_2 => false,
|
|
};
|
|
}
|
|
|
|
/// The set of targets that Zig supports using LLD to link for.
|
|
pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
|
|
return switch (ofmt) {
|
|
.elf, .coff, .wasm => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// The set of targets that our own self-hosted backends have robust support for.
|
|
/// Used to select between LLVM backend and self-hosted backend when compiling in
|
|
/// debug mode. A given target should only return true here if it is passing greater
|
|
/// than or equal to the number of behavior tests as the respective LLVM backend.
|
|
pub fn selfHostedBackendIsAsRobustAsLlvm(target: std.Target) bool {
|
|
_ = target;
|
|
return false;
|
|
}
|
|
|
|
pub fn supportsStackProbing(target: std.Target) bool {
|
|
return target.os.tag != .windows and target.os.tag != .uefi and
|
|
(target.cpu.arch == .x86 or target.cpu.arch == .x86_64);
|
|
}
|
|
|
|
pub fn supportsStackProtector(target: std.Target, backend: std.builtin.CompilerBackend) bool {
|
|
switch (target.os.tag) {
|
|
.plan9 => return false,
|
|
else => {},
|
|
}
|
|
switch (target.cpu.arch) {
|
|
.spirv32, .spirv64 => return false,
|
|
else => {},
|
|
}
|
|
return switch (backend) {
|
|
.stage2_llvm => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn clangSupportsStackProtector(target: std.Target) bool {
|
|
return switch (target.cpu.arch) {
|
|
.spirv32, .spirv64 => return false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
pub fn libcProvidesStackProtector(target: std.Target) bool {
|
|
return !target.isMinGW() and target.os.tag != .wasi and !target.isSpirV();
|
|
}
|
|
|
|
pub fn supportsReturnAddress(target: std.Target) bool {
|
|
return switch (target.cpu.arch) {
|
|
.wasm32, .wasm64 => target.os.tag == .emscripten,
|
|
.bpfel, .bpfeb => false,
|
|
.spirv32, .spirv64 => false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
pub const CompilerRtClassification = enum { none, only_compiler_rt, only_libunwind, both };
|
|
|
|
pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerRtClassification {
|
|
if (target.abi.isGnu() and std.mem.eql(u8, name, "gcc_s")) {
|
|
// libgcc_s includes exception handling functions, so if linking this library
|
|
// is requested, zig needs to instead link libunwind. Otherwise we end up with
|
|
// the linker unable to find `_Unwind_RaiseException` and other related symbols.
|
|
return .both;
|
|
}
|
|
if (std.mem.eql(u8, name, "compiler_rt")) {
|
|
return .only_compiler_rt;
|
|
}
|
|
if (std.mem.eql(u8, name, "unwind")) {
|
|
return .only_libunwind;
|
|
}
|
|
return .none;
|
|
}
|
|
|
|
pub fn hasDebugInfo(target: std.Target) bool {
|
|
return switch (target.cpu.arch) {
|
|
.nvptx, .nvptx64 => std.Target.nvptx.featureSetHas(target.cpu.features, .ptx75) or
|
|
std.Target.nvptx.featureSetHas(target.cpu.features, .ptx76) or
|
|
std.Target.nvptx.featureSetHas(target.cpu.features, .ptx77) or
|
|
std.Target.nvptx.featureSetHas(target.cpu.features, .ptx78) or
|
|
std.Target.nvptx.featureSetHas(target.cpu.features, .ptx80) or
|
|
std.Target.nvptx.featureSetHas(target.cpu.features, .ptx81),
|
|
.bpfel, .bpfeb => false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.OptimizeMode {
|
|
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,
|
|
.x86,
|
|
.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",
|
|
},
|
|
.solaris, .illumos => &[_][]const u8{
|
|
"-lm",
|
|
"-lsocket",
|
|
"-lnsl",
|
|
// Solaris releases after 10 merged the threading libraries into libc.
|
|
"-lc",
|
|
},
|
|
.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 or target.isDarwin() or std.dwarf.abi.supportsUnwinding(target);
|
|
}
|
|
|
|
pub fn defaultAddressSpace(
|
|
target: std.Target,
|
|
context: enum {
|
|
/// Query the default address space for global constant values.
|
|
global_constant,
|
|
/// Query the default address space for global mutable values.
|
|
global_mutable,
|
|
/// Query the default address space for function-local values.
|
|
local,
|
|
/// Query the default address space for functions themselves.
|
|
function,
|
|
},
|
|
) AddressSpace {
|
|
// The default address space for functions on AVR is .flash to produce
|
|
// correct fixups into progmem.
|
|
if (context == .function and target.cpu.arch == .avr) return .flash;
|
|
return .generic;
|
|
}
|
|
|
|
/// Returns true if pointers in `from` can be converted to a pointer in `to`.
|
|
pub fn addrSpaceCastIsValid(
|
|
target: std.Target,
|
|
from: AddressSpace,
|
|
to: AddressSpace,
|
|
) bool {
|
|
const arch = target.cpu.arch;
|
|
switch (arch) {
|
|
.x86_64, .x86 => return arch.supportsAddressSpace(from) and arch.supportsAddressSpace(to),
|
|
.nvptx64, .nvptx, .amdgcn => {
|
|
const to_generic = arch.supportsAddressSpace(from) and to == .generic;
|
|
const from_generic = arch.supportsAddressSpace(to) and from == .generic;
|
|
return to_generic or from_generic;
|
|
},
|
|
else => return from == .generic and to == .generic,
|
|
}
|
|
}
|
|
|
|
pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 {
|
|
const have_float = switch (target.abi) {
|
|
.gnuilp32 => return "ilp32",
|
|
.gnueabihf, .musleabihf, .eabihf => true,
|
|
else => false,
|
|
};
|
|
|
|
switch (target.cpu.arch) {
|
|
.riscv64 => {
|
|
const featureSetHas = std.Target.riscv.featureSetHas;
|
|
if (featureSetHas(target.cpu.features, .d)) {
|
|
return "lp64d";
|
|
} else if (have_float) {
|
|
return "lp64f";
|
|
} else {
|
|
return "lp64";
|
|
}
|
|
},
|
|
.riscv32 => {
|
|
const featureSetHas = std.Target.riscv.featureSetHas;
|
|
if (featureSetHas(target.cpu.features, .d)) {
|
|
return "ilp32d";
|
|
} else if (have_float) {
|
|
return "ilp32f";
|
|
} else if (featureSetHas(target.cpu.features, .e)) {
|
|
return "ilp32e";
|
|
} else {
|
|
return "ilp32";
|
|
}
|
|
},
|
|
//TODO add ARM, Mips, and PowerPC
|
|
else => return null,
|
|
}
|
|
}
|
|
|
|
/// This function returns 1 if function alignment is not observable or settable.
|
|
pub fn defaultFunctionAlignment(target: std.Target) Alignment {
|
|
return switch (target.cpu.arch) {
|
|
.arm, .armeb => .@"4",
|
|
.aarch64, .aarch64_32, .aarch64_be => .@"4",
|
|
.sparc, .sparcel, .sparc64 => .@"4",
|
|
.riscv64 => .@"2",
|
|
else => .@"1",
|
|
};
|
|
}
|
|
|
|
pub fn supportsFunctionAlignment(target: std.Target) bool {
|
|
return switch (target.cpu.arch) {
|
|
.wasm32, .wasm64 => false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool {
|
|
switch (backend) {
|
|
.stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target),
|
|
.stage2_c => return true,
|
|
else => return false,
|
|
}
|
|
}
|
|
|
|
pub fn supportsThreads(target: std.Target, backend: std.builtin.CompilerBackend) bool {
|
|
return switch (backend) {
|
|
.stage2_x86_64 => target.ofmt == .macho or target.ofmt == .elf,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
pub fn libcFloatPrefix(float_bits: u16) []const u8 {
|
|
return switch (float_bits) {
|
|
16, 80 => "__",
|
|
32, 64, 128 => "",
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn libcFloatSuffix(float_bits: u16) []const u8 {
|
|
return switch (float_bits) {
|
|
16 => "h", // Non-standard
|
|
32 => "f",
|
|
64 => "",
|
|
80 => "x", // Non-standard
|
|
128 => "q", // Non-standard (mimics convention in GCC libquadmath)
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn compilerRtFloatAbbrev(float_bits: u16) []const u8 {
|
|
return switch (float_bits) {
|
|
16 => "h",
|
|
32 => "s",
|
|
64 => "d",
|
|
80 => "x",
|
|
128 => "t",
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn compilerRtIntAbbrev(bits: u16) []const u8 {
|
|
return switch (bits) {
|
|
16 => "h",
|
|
32 => "s",
|
|
64 => "d",
|
|
128 => "t",
|
|
else => "o", // Non-standard
|
|
};
|
|
}
|
|
|
|
pub fn fnCallConvAllowsZigTypes(target: std.Target, cc: std.builtin.CallingConvention) bool {
|
|
return switch (cc) {
|
|
.Unspecified, .Async, .Inline => true,
|
|
// For now we want to authorize PTX kernel to use zig objects, even if
|
|
// we end up exposing the ABI. The goal is to experiment with more
|
|
// integrated CPU/GPU code.
|
|
.Kernel => target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend {
|
|
if (use_llvm) return .stage2_llvm;
|
|
if (target.ofmt == .c) return .stage2_c;
|
|
return switch (target.cpu.arch) {
|
|
.wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
|
|
.arm, .armeb, .thumb, .thumbeb => .stage2_arm,
|
|
.x86_64 => .stage2_x86_64,
|
|
.x86 => .stage2_x86,
|
|
.aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
|
|
.riscv64 => .stage2_riscv64,
|
|
.sparc64 => .stage2_sparc64,
|
|
.spirv64 => .stage2_spirv64,
|
|
else => .other,
|
|
};
|
|
}
|
|
|
|
pub fn backendSupportsFeature(
|
|
cpu_arch: std.Target.Cpu.Arch,
|
|
ofmt: std.Target.ObjectFormat,
|
|
use_llvm: bool,
|
|
feature: Feature,
|
|
) bool {
|
|
return switch (feature) {
|
|
.panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64,
|
|
.panic_unwrap_error => ofmt == .c or use_llvm,
|
|
.safety_check_formatted => ofmt == .c or use_llvm,
|
|
.error_return_trace => use_llvm,
|
|
.is_named_enum_value => use_llvm,
|
|
.error_set_has_value => use_llvm or cpu_arch.isWasm(),
|
|
.field_reordering => use_llvm,
|
|
.safety_checked_instructions => use_llvm,
|
|
};
|
|
}
|