Commit ff38f56040

vole-dev <dev@vole.dev>
2021-10-30 17:30:28
default mabi based on RISC-V extensions and -mabi build option
The target abi can also be set in build.zig via LibExeObjStep.target_abi The value passed in is checked that it is a valid value in std.Target.TargetAbi The target abi is also validated against the target cpu
1 parent 84704ef
lib/std/build.zig
@@ -1474,6 +1474,7 @@ pub const LibExeObjStep = struct {
     name_prefix: []const u8,
     filter: ?[]const u8,
     test_evented_io: bool = false,
+    target_abi: ?std.Target.TargetAbi = null,
     code_model: std.builtin.CodeModel = .default,
     wasi_exec_model: ?std.builtin.WasiExecModel = null,
 
@@ -2458,6 +2459,9 @@ pub const LibExeObjStep = struct {
             try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
         }
 
+        if (self.target_abi) |target_abi| {
+            try zig_args.append(builder.fmt("-mabi={s}", .{@tagName(target_abi)}));
+        }
         if (self.code_model != .default) {
             try zig_args.append("-mcmodel");
             try zig_args.append(@tagName(self.code_model));
lib/std/target.zig
@@ -560,6 +560,84 @@ pub const Target = struct {
         }
     };
 
+    /// processor specific ABI
+    pub const TargetAbi = enum {
+        //TODO add ARM, Mips, and PowerPC
+        ilp32,
+        ilp32d,
+        ilp32e,
+        ilp32f,
+        lp64,
+        lp64d,
+        lp64f,
+
+        const Riscv32ABI = struct {
+            fn default(features: Cpu.Feature.Set) TargetAbi {
+                if (riscv.featureSetHas(features, .d)) {
+                    return .ilp32d;
+                } else if (riscv.featureSetHas(features, .e)) {
+                    return .ilp32e;
+                } else {
+                    return .ilp32;
+                }
+            }
+            fn validate(target_abi: TargetAbi, features: Cpu.Feature.Set) ParseError!void {
+                const has_e = riscv.featureSetHas(features, .e);
+                const has_f = riscv.featureSetHas(features, .f);
+                const has_d = riscv.featureSetHas(features, .d);
+                return switch (target_abi) {
+                    .ilp32e => if (has_e) {} else error.FeatureAbiMismatch,
+                    .ilp32 => if (!has_e) {} else error.FeatureAbiMismatch,
+                    .ilp32f => if (!has_e and has_f) {} else error.FeatureAbiMismatch,
+                    .ilp32d => if (!has_e and has_d) {} else error.FeatureAbiMismatch,
+                    else => error.ArchAbiMismatch,
+                };
+            }
+        };
+        const Riscv64ABI = struct {
+            fn default(features: Cpu.Feature.Set) TargetAbi {
+                if (riscv.featureSetHas(features, .d)) {
+                    return .lp64d;
+                } else {
+                    return .lp64;
+                }
+            }
+            fn validate(target_abi: TargetAbi, features: Cpu.Feature.Set) ParseError!void {
+                const has_f = riscv.featureSetHas(features, .f);
+                const has_d = riscv.featureSetHas(features, .d);
+                return switch (target_abi) {
+                    .lp64 => {},
+                    .lp64f => if (has_f) {} else error.FeatureAbiMismatch,
+                    .lp64d => if (has_d) {} else error.FeatureAbiMismatch,
+                    else => error.ArchAbiMismatch,
+                };
+            }
+        };
+        pub fn default(arch: Cpu.Arch, features: Cpu.Feature.Set) ?TargetAbi {
+            return switch (arch) {
+                .riscv32 => Riscv32ABI.default(features),
+                .riscv64 => Riscv64ABI.default(features),
+                else => null,
+            };
+        }
+
+        pub const ParseError = error{
+            InvalidAbi,
+            ArchAbiMismatch,
+            FeatureAbiMismatch,
+        };
+        pub fn parse(cpu: Cpu, abi_string: []const u8) ParseError!TargetAbi {
+            const target_abi = std.meta.stringToEnum(TargetAbi, abi_string) orelse return error.InvalidAbi;
+
+            switch (cpu.arch) {
+                .riscv32 => try Riscv32ABI.validate(target_abi, cpu.features),
+                .riscv64 => try Riscv64ABI.validate(target_abi, cpu.features),
+                else => return error.ArchAbiMismatch,
+            }
+            return target_abi;
+        }
+    };
+
     pub const ObjectFormat = enum {
         /// Common Object File Format (Windows)
         coff,
src/codegen/llvm.zig
@@ -244,18 +244,7 @@ pub const Object = struct {
         // TODO handle float ABI better- it should depend on the ABI portion of std.Target
         const float_abi: llvm.ABIType = .Default;
 
-        // TODO a way to override this as part of std.Target ABI?
-        const abi_name: ?[*:0]const u8 = switch (options.target.cpu.arch) {
-            .riscv32 => switch (options.target.os.tag) {
-                .linux => "ilp32d",
-                else => "ilp32",
-            },
-            .riscv64 => switch (options.target.os.tag) {
-                .linux => "lp64d",
-                else => "lp64",
-            },
-            else => null,
-        };
+        const abi_name: ?[*:0]const u8 = if (options.target_abi) |t| @tagName(t) else null;
 
         const target_machine = llvm.TargetMachine.create(
             target,
src/stage1/codegen.cpp
@@ -9487,9 +9487,8 @@ static void init(CodeGen *g) {
     // TODO handle float ABI better- it should depend on the ABI portion of std.Target
     ZigLLVMABIType float_abi = ZigLLVMABITypeDefault;
 
-    // TODO a way to override this as part of std.Target ABI?
-    const char *abi_name = nullptr;
-    if (target_is_riscv(g->zig_target)) {
+    const char *abi_name = g->zig_target->llvm_target_abi;
+    if (abi_name == nullptr && target_is_riscv(g->zig_target)) {
         // RISC-V Linux defaults to ilp32d/lp64d
         if (g->zig_target->os == OsLinux) {
             abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32d" : "lp64d";
src/stage1/stage1.h
@@ -112,6 +112,7 @@ struct ZigTarget {
 
     const char *llvm_cpu_name;
     const char *llvm_cpu_features;
+    const char *llvm_target_abi;
 };
 
 // ABI warning
src/Compilation.zig
@@ -767,6 +767,7 @@ pub const InitOptions = struct {
     compatibility_version: ?std.builtin.Version = null,
     libc_installation: ?*const LibCInstallation = null,
     machine_code_model: std.builtin.CodeModel = .default,
+    target_abi: ?std.Target.TargetAbi,
     clang_preprocessor_mode: ClangPreprocessorMode = .no,
     /// This is for stage1 and should be deleted upon completion of self-hosting.
     color: @import("main.zig").Color = .auto,
@@ -1176,6 +1177,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         cache.hash.add(options.target.os.getVersionRange());
         cache.hash.add(options.is_native_os);
         cache.hash.add(options.target.abi);
+        cache.hash.addOptionalBytes(if (options.target_abi) |t| @tagName(t) else null);
         cache.hash.add(ofmt);
         cache.hash.add(pic);
         cache.hash.add(pie);
@@ -1488,6 +1490,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             .single_threaded = single_threaded,
             .verbose_link = options.verbose_link,
             .machine_code_model = options.machine_code_model,
+            .target_abi = options.target_abi,
             .dll_export_fns = dll_export_fns,
             .error_return_tracing = error_return_tracing,
             .llvm_cpu_features = llvm_cpu_features,
@@ -1780,8 +1783,8 @@ pub fn getTarget(self: Compilation) Target {
 
 /// Detect changes to source files, perform semantic analysis, and update the output files.
 pub fn update(self: *Compilation) !void {
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     self.clearMiscFailures();
 
@@ -2822,8 +2825,8 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
     if (!build_options.have_llvm)
         return error.ZigCompilerNotBuiltWithLLVMExtensions;
 
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     const cimport_zig_basename = "cimport.zig";
 
@@ -3077,8 +3080,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
     const self_exe_path = comp.self_exe_path orelse
         return comp.failCObj(c_object, "clang compilation disabled", .{});
 
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     log.debug("updating C object: {s}", .{c_object.src.src_path});
 
@@ -3451,6 +3454,9 @@ pub fn addCCArgs(
                 try argv.append("-mthumb");
             }
 
+            if (comp.bin_file.options.target_abi) |target_abi| {
+                try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{@tagName(target_abi)}));
+            }
             if (comp.sanitize_c and !comp.bin_file.options.tsan) {
                 try argv.append("-fsanitize=undefined");
                 try argv.append("-fsanitize-trap=undefined");
@@ -3583,6 +3589,11 @@ pub fn addCCArgs(
                     // TODO
                 },
             }
+
+            if (comp.bin_file.options.target_abi) |target_abi| {
+                try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{@tagName(target_abi)}));
+            }
+
             if (target_util.clangAssemblerSupportsMcpuArg(target)) {
                 if (target.cpu.model.llvm_name) |llvm_name| {
                     try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name}));
@@ -4036,8 +4047,8 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
 }
 
 fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) Allocator.Error!void {
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     const source = try comp.generateBuiltinZigSource(comp.gpa);
     defer comp.gpa.free(source);
@@ -4074,8 +4085,8 @@ pub fn dump_argv(argv: []const []const u8) void {
 }
 
 pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![]u8 {
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     var buffer = std.ArrayList(u8).init(allocator);
     defer buffer.deinit();
@@ -4320,8 +4331,8 @@ fn buildOutputFromZig(
     out: *?CRTFile,
     misc_task_tag: MiscTask,
 ) !void {
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     std.debug.assert(output_mode != .Exe);
     const special_sub = "std" ++ std.fs.path.sep_str ++ "special";
@@ -4378,6 +4389,7 @@ fn buildOutputFromZig(
         .strip = comp.compilerRtStrip(),
         .is_native_os = comp.bin_file.options.is_native_os,
         .is_native_abi = comp.bin_file.options.is_native_abi,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .verbose_cc = comp.verbose_cc,
         .verbose_link = comp.bin_file.options.verbose_link,
@@ -4419,8 +4431,8 @@ fn buildOutputFromZig(
 }
 
 fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node) !void {
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
     defer arena_allocator.deinit();
@@ -4566,6 +4578,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
         .is_native_cpu = false, // Only true when bootstrapping the compiler.
         .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
         .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
+        .llvm_target_abi = if (comp.bin_file.options.target_abi) |t| @tagName(t) else null,
     };
 
     comp.stage1_cache_manifest = &man;
@@ -4773,8 +4786,8 @@ pub fn build_crt_file(
     output_mode: std.builtin.OutputMode,
     c_source_files: []const Compilation.CSourceFile,
 ) !void {
-    const t = trace(@src());
-    defer t.end();
+    const tracy_trace = trace(@src());
+    defer tracy_trace.end();
 
     const target = comp.getTarget();
     const basename = try std.zig.binNameAlloc(comp.gpa, .{
@@ -4817,6 +4830,7 @@ pub fn build_crt_file(
         .strip = comp.compilerRtStrip(),
         .is_native_os = comp.bin_file.options.is_native_os,
         .is_native_abi = comp.bin_file.options.is_native_abi,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .c_source_files = c_source_files,
         .verbose_cc = comp.verbose_cc,
src/glibc.zig
@@ -961,6 +961,7 @@ fn buildSharedLib(
         .strip = comp.compilerRtStrip(),
         .is_native_os = false,
         .is_native_abi = false,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .verbose_cc = comp.verbose_cc,
         .verbose_link = comp.bin_file.options.verbose_link,
src/libcxx.zig
@@ -200,6 +200,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
         .strip = comp.compilerRtStrip(),
         .is_native_os = comp.bin_file.options.is_native_os,
         .is_native_abi = comp.bin_file.options.is_native_abi,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .c_source_files = c_source_files.items,
         .verbose_cc = comp.verbose_cc,
@@ -332,6 +333,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
         .strip = comp.compilerRtStrip(),
         .is_native_os = comp.bin_file.options.is_native_os,
         .is_native_abi = comp.bin_file.options.is_native_abi,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .c_source_files = c_source_files.items,
         .verbose_cc = comp.verbose_cc,
src/libtsan.zig
@@ -218,6 +218,7 @@ pub fn buildTsan(comp: *Compilation) !void {
         .strip = comp.compilerRtStrip(),
         .is_native_os = comp.bin_file.options.is_native_os,
         .is_native_abi = comp.bin_file.options.is_native_abi,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .c_source_files = c_source_files.items,
         .verbose_cc = comp.verbose_cc,
src/libunwind.zig
@@ -124,6 +124,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
         .strip = comp.compilerRtStrip(),
         .is_native_os = comp.bin_file.options.is_native_os,
         .is_native_abi = comp.bin_file.options.is_native_abi,
+        .target_abi = comp.bin_file.options.target_abi,
         .self_exe_path = comp.self_exe_path,
         .c_source_files = &c_source_files,
         .verbose_cc = comp.verbose_cc,
src/link.zig
@@ -134,6 +134,7 @@ pub const Options = struct {
     version_script: ?[]const u8,
     soname: ?[]const u8,
     llvm_cpu_features: ?[*:0]const u8,
+    target_abi: ?std.Target.TargetAbi,
 
     objects: []const []const u8,
     framework_dirs: []const []const u8,
src/main.zig
@@ -328,6 +328,7 @@ const usage_build_generic =
     \\Compile Options:
     \\  -target [name]            <arch><sub>-<os>-<abi> see the targets command
     \\  -mcpu [cpu]               Specify target CPU and feature set
+    \\  -mabi [target-abi]        Specify processor specific target-abi
     \\  -mcmodel=[default|tiny|   Limit range of code and data virtual addresses
     \\            small|kernel|
     \\            medium|large]
@@ -653,6 +654,7 @@ fn buildOutputType(
     var sysroot: ?[]const u8 = null;
     var libc_paths_file: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIBC");
     var machine_code_model: std.builtin.CodeModel = .default;
+    var target_abi_str: ?[]const u8 = null;
     var runtime_args_start: ?usize = null;
     var test_filter: ?[]const u8 = null;
     var test_name_prefix: ?[]const u8 = null;
@@ -926,6 +928,12 @@ fn buildOutputType(
                         target_mcpu = arg["-mcpu=".len..];
                     } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
                         machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
+                    } else if (mem.eql(u8, arg, "-mabi")) {
+                        if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
+                        i += 1;
+                        target_abi_str = args[i];
+                    } else if (mem.startsWith(u8, arg, "-mabi=")) {
+                        target_abi_str = arg["-mabi=".len..];
                     } else if (mem.startsWith(u8, arg, "-O")) {
                         optimize_mode_string = arg["-O".len..];
                     } else if (mem.eql(u8, arg, "--dynamic-linker")) {
@@ -1872,6 +1880,12 @@ fn buildOutputType(
     const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options);
     const target_info = try detectNativeTargetInfo(gpa, cross_target);
 
+    const target_abi = if (target_abi_str) |s| std.Target.TargetAbi.parse(target_info.target.cpu, s) catch |err| switch (err) {
+        error.InvalidAbi => fatal("invalid target-abi value '{s}'", .{target_abi_str}),
+        error.ArchAbiMismatch => fatal("target-abi {s} is not valid for arch {s}", .{ target_abi_str, target_info.target.cpu.arch }),
+        error.FeatureAbiMismatch => fatal("target-abi {s} is not compatible with CPU features", .{target_abi_str}),
+    } else std.Target.TargetAbi.default(target_info.target.cpu.arch, target_info.target.cpu.features);
+
     if (target_info.target.os.tag != .freestanding) {
         if (ensure_libc_on_non_freestanding)
             link_libc = true;
@@ -2459,6 +2473,7 @@ fn buildOutputType(
         .verbose_cimport = verbose_cimport,
         .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
         .machine_code_model = machine_code_model,
+        .target_abi = target_abi,
         .color = color,
         .time_report = time_report,
         .stack_report = stack_report,
@@ -3382,6 +3397,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
             .target = target_info.target,
             .is_native_os = cross_target.isNativeOs(),
             .is_native_abi = cross_target.isNativeAbi(),
+            .target_abi = std.Target.TargetAbi.default(target_info.target.cpu.arch, target_info.target.cpu.features),
             .dynamic_linker = target_info.dynamic_linker.get(),
             .output_mode = .Exe,
             .main_pkg = &main_pkg,
src/musl.zig
@@ -214,6 +214,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
                 .strip = comp.compilerRtStrip(),
                 .is_native_os = false,
                 .is_native_abi = false,
+                .target_abi = comp.bin_file.options.target_abi,
                 .self_exe_path = comp.self_exe_path,
                 .verbose_cc = comp.verbose_cc,
                 .verbose_link = comp.bin_file.options.verbose_link,
src/stage1.zig
@@ -364,6 +364,7 @@ pub const Stage2Target = extern struct {
 
     llvm_cpu_name: ?[*:0]const u8,
     llvm_cpu_features: ?[*:0]const u8,
+    llvm_target_abi: ?[*:0]const u8,
 };
 
 // ABI warning
src/test.zig
@@ -907,6 +907,7 @@ pub const TestContext = struct {
             .object_format = case.object_format,
             .is_native_os = case.target.isNativeOs(),
             .is_native_abi = case.target.isNativeAbi(),
+            .target_abi = std.Target.TargetAbi.default(target.cpu.arch, target.cpu.features),
             .dynamic_linker = target_info.dynamic_linker.get(),
             .link_libc = link_libc,
             .use_llvm = use_llvm,