Commit 7377dce368
Changed files (8)
lib
std
test
behavior
lib/std/target.zig
@@ -1440,16 +1440,6 @@ pub const Target = struct {
return !self.cpu.arch.isWasm();
}
- pub fn supportsTailCall(self: Target) bool {
- switch (self.cpu.arch) {
- .wasm32, .wasm64 => return wasm.featureSetHas(self.cpu.features, .tail_call),
- // TODO these might not be true but LLVM doesn't seem to be able to handle them
- .mips, .mipsel, .mips64, .mips64el => return false,
- .powerpc, .powerpcle, .powerpc64, .powerpc64le => return false,
- else => return true,
- }
- }
-
pub const FloatAbi = enum {
hard,
soft,
src/codegen/llvm.zig
@@ -177,6 +177,116 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {
return llvm_triple.toOwnedSliceSentinel(0);
}
+pub fn targetOs(os_tag: std.Target.Os.Tag) llvm.OSType {
+ return switch (os_tag) {
+ .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .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 targetArch(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,
+ .m68k => .m68k,
+ .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,
+ .sparc64 => .sparcv9, // In LLVM, sparc64 == 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,
+ };
+}
+
+pub fn supportsTailCall(target: std.Target) bool {
+ switch (target.cpu.arch) {
+ .wasm32, .wasm64 => return std.Target.wasm.featureSetHas(target.cpu.features, .tail_call),
+ // Although these ISAs support tail calls, LLVM does not support tail calls on them.
+ .mips, .mipsel, .mips64, .mips64el => return false,
+ .powerpc, .powerpcle, .powerpc64, .powerpc64le => return false,
+ else => return true,
+ }
+}
+
pub const Object = struct {
gpa: Allocator,
module: *Module,
src/Compilation.zig
@@ -4766,6 +4766,24 @@ pub fn dump_argv(argv: []const []const u8) void {
std.debug.print("{s}\n", .{argv[argv.len - 1]});
}
+pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
+ const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
+ if (use_stage1) return .stage1;
+ if (build_options.have_llvm and comp.bin_file.options.use_llvm) return .stage2_llvm;
+ const target = comp.bin_file.options.target;
+ 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,
+ .i386 => .stage2_x86,
+ .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
+ .riscv64 => .stage2_riscv64,
+ .sparc64 => .stage2_sparc64,
+ else => .other,
+ };
+}
+
pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 {
const tracy_trace = trace(@src());
defer tracy_trace.end();
@@ -4775,23 +4793,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
const target = comp.getTarget();
const generic_arch_name = target.cpu.arch.genericName();
- const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
-
- const zig_backend: std.builtin.CompilerBackend = blk: {
- if (use_stage1) break :blk .stage1;
- if (build_options.have_llvm and comp.bin_file.options.use_llvm) break :blk .stage2_llvm;
- if (target.ofmt == .c) break :blk .stage2_c;
- break :blk switch (target.cpu.arch) {
- .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
- .arm, .armeb, .thumb, .thumbeb => .stage2_arm,
- .x86_64 => .stage2_x86_64,
- .i386 => .stage2_x86,
- .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
- .riscv64 => .stage2_riscv64,
- .sparc64 => .stage2_sparc64,
- else => .other,
- };
- };
+ const zig_backend = comp.getZigBackend();
@setEvalBranchQuota(4000);
try buffer.writer().print(
src/link.zig
@@ -931,9 +931,10 @@ pub const File = struct {
std.debug.print("\n", .{});
}
- const llvm = @import("codegen/llvm/bindings.zig");
- const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag);
- const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
+ const llvm_bindings = @import("codegen/llvm/bindings.zig");
+ const llvm = @import("codegen/llvm.zig");
+ const os_tag = llvm.targetOs(base.options.target.os.tag);
+ const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag);
if (bad) return error.UnableToWriteArchive;
if (!base.options.disable_lld_caching) {
src/mingw.zig
@@ -6,7 +6,6 @@ const assert = std.debug.assert;
const log = std.log.scoped(.mingw);
const builtin = @import("builtin");
-const target_util = @import("target.zig");
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
const Cache = @import("Cache.zig");
@@ -404,11 +403,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
});
errdefer comp.gpa.free(lib_final_path);
- const llvm = @import("codegen/llvm/bindings.zig");
- const arch_type = target_util.archToLLVM(target.cpu.arch);
+ const llvm_bindings = @import("codegen/llvm/bindings.zig");
+ const llvm = @import("codegen/llvm.zig");
+ const arch_tag = llvm.targetArch(target.cpu.arch);
const def_final_path_z = try arena.dupeZ(u8, def_final_path);
const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
- if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) {
+ if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) {
// TODO surface a proper error here
log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
return error.WritingImportLibFailed;
src/Sema.zig
@@ -6161,8 +6161,11 @@ fn analyzeCall(
fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
const target = sema.mod.getTarget();
- if (!target.supportsTailCall()) {
- return sema.fail(block, call_src, "unable to perform tail call: target does not support tail calls", .{});
+ const backend = sema.mod.comp.getZigBackend();
+ if (!target_util.supportsTailCall(target, backend)) {
+ return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{
+ @tagName(backend), @tagName(target.cpu.arch),
+ });
}
const func_decl = sema.mod.declPtr(sema.owner_func.?.owner_decl);
if (!func_ty.eql(func_decl.ty, sema.mod)) {
src/target.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const llvm = @import("codegen/llvm/bindings.zig");
const Type = @import("type.zig").Type;
pub const ArchOsAbi = struct {
@@ -317,106 +316,6 @@ pub fn supportsReturnAddress(target: std.Target) bool {
};
}
-pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
- return switch (os_tag) {
- .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .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,
- .m68k => .m68k,
- .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,
- .sparc64 => .sparcv9, // In LLVM, sparc64 == 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);
@@ -770,3 +669,10 @@ pub fn supportsFunctionAlignment(target: std.Target) bool {
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),
+ else => return false,
+ }
+}
test/behavior/call.zig
@@ -270,7 +270,12 @@ test "forced tail call" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
- if (comptime !builtin.target.supportsTailCall()) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_llvm) {
+ // Only attempt this test on targets we know have tail call support in LLVM.
+ if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) {
+ return error.SkipZigTest;
+ }
+ }
const S = struct {
fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 {
@@ -298,7 +303,12 @@ test "inline call preserves tail call" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
- if (comptime !builtin.target.supportsTailCall()) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_llvm) {
+ // Only attempt this test on targets we know have tail call support in LLVM.
+ if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) {
+ return error.SkipZigTest;
+ }
+ }
const max = std.math.maxInt(u16);
const S = struct {