diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index 1544fcbc8f..8734b37a02 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -531,12 +531,12 @@ export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usiz // ABI warning export fn stage2_list_features_for_arch(arch_name_ptr: [*]const u8, arch_name_len: usize, show_subfeatures: bool) void { - print_features_for_arch(arch_name_ptr[0..arch_name_len], show_subfeatures) catch |err| { + printFeaturesForArch(arch_name_ptr[0..arch_name_len], show_subfeatures) catch |err| { std.debug.warn("Failed to list features: {}\n", .{ @errorName(err) }); }; } -fn print_features_for_arch(arch_name: []const u8, show_subfeatures: bool) !void { +fn printFeaturesForArch(arch_name: []const u8, show_subfeatures: bool) !void { const stdout_stream = &std.io.getStdOut().outStream().stream; const arch = Target.parseArchTag(arch_name) catch { @@ -575,12 +575,12 @@ fn print_features_for_arch(arch_name: []const u8, show_subfeatures: bool) !void // ABI warning export fn stage2_list_cpus_for_arch(arch_name_ptr: [*]const u8, arch_name_len: usize, show_subfeatures: bool) void { - print_cpus_for_arch(arch_name_ptr[0..arch_name_len], show_subfeatures) catch |err| { + printCpusForArch(arch_name_ptr[0..arch_name_len], show_subfeatures) catch |err| { std.debug.warn("Failed to list features: {}\n", .{ @errorName(err) }); }; } -fn print_cpus_for_arch(arch_name: []const u8, show_subfeatures: bool) !void { +fn printCpusForArch(arch_name: []const u8, show_subfeatures: bool) !void { const stdout_stream = &std.io.getStdOut().outStream().stream; const arch = Target.parseArchTag(arch_name) catch { @@ -618,3 +618,90 @@ fn print_cpus_for_arch(arch_name: []const u8, show_subfeatures: bool) !void { } // use target_arch_name(ZigLLVM_ArchType) to get name from main.cpp 'target'. +// ABI warning +export fn stage2_validate_cpu_and_features( + arch_name: [*:0]const u8, + cpu: ?[*:0]const u8, + features: ?[*:0]const u8, +) bool { + const arch = Target.parseArchTag(std.mem.toSliceConst(u8, arch_name)) catch { + std.debug.warn("Failed to parse arch '{}'\nInvoke 'zig targets' for a list of valid architectures\n", .{ arch_name }); + return false; + }; + + const res = validateCpuAndFeatures( + arch, + if (cpu) |def_cpu| std.mem.toSliceConst(u8, def_cpu) else "", + if (features) |def_features| std.mem.toSliceConst(u8, def_features) else ""); + + switch (res) { + .Ok => return true, + .InvalidCpu => |invalid_cpu| { + std.debug.warn("Invalid CPU '{}'\n", .{ invalid_cpu }); + return false; + }, + .InvalidFeaturesString => { + std.debug.warn("Invalid features string\n", .{}); + std.debug.warn("Must have format \"+yes_feature,-no_feature\"\n", .{}); + return false; + }, + .InvalidFeature => |invalid_feature| { + std.debug.warn("Invalid feature '{}'\n", .{ invalid_feature }); + return false; + } + } +} + +const ValidateCpuAndFeaturesResult = union(enum) { + Ok, + InvalidCpu: []const u8, + InvalidFeaturesString, + InvalidFeature: []const u8, +}; + +fn validateCpuAndFeatures(arch: @TagType(std.Target.Arch), cpu: []const u8, features: []const u8) ValidateCpuAndFeaturesResult { + + const known_cpus = std.target.getCpusForArch(arch); + const known_features = std.target.getFeaturesForArch(arch); + + if (cpu.len > 0) { + var found_cpu = false; + for (known_cpus) |known_cpu| { + if (std.mem.eql(u8, cpu, known_cpu.name)) { + found_cpu = true; + break; + } + } + + if (!found_cpu) { + return .{ .InvalidCpu = cpu }; + } + } + + if (features.len > 0) { + var start: usize = 0; + while (start < features.len) { + const next_comma_pos = std.mem.indexOfScalar(u8, features[start..], ',') orelse features.len - start; + var feature = features[start..start+next_comma_pos]; + + if (feature.len < 2) return .{ .InvalidFeaturesString = {} }; + + if (feature[0] != '+' and feature[0] != '-') return .{ .InvalidFeaturesString = {} }; + feature = feature[1..]; + + var found_feature = false; + for (known_features) |known_feature| { + if (std.mem.eql(u8, feature, known_feature.name)) { + found_feature = true; + break; + } + } + + if (!found_feature) return .{ .InvalidFeature = feature }; + + start += next_comma_pos + 1; + } + } + + return .{ .Ok = {} }; +} diff --git a/src/all_types.hpp b/src/all_types.hpp index df52c29a4e..af4914e29e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2215,6 +2215,9 @@ struct CodeGen { const char **clang_argv; size_t clang_argv_len; + + const char *llvm_cpu; + const char *llvm_features; }; struct ZigVar { diff --git a/src/codegen.cpp b/src/codegen.cpp index 3d4d2a8c31..43fc002a12 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8800,6 +8800,15 @@ static void init(CodeGen *g) { target_specific_features = ""; } + // Override CPU and features if non-null. + if (g->llvm_cpu != nullptr) { + target_specific_cpu_args = g->llvm_cpu; + } + + if (g->llvm_features != nullptr) { + target_specific_features = g->llvm_features; + } + g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str), target_specific_cpu_args, target_specific_features, opt_level, reloc_mode, LLVMCodeModelDefault, g->function_sections); diff --git a/src/main.cpp b/src/main.cpp index ee2faa9a78..f061b13414 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -100,6 +100,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --override-lib-dir [arg] override path to Zig lib directory\n" " -ffunction-sections places each function in a separate section\n" " -D[macro]=[value] define C [macro] to [value] (1 if [value] omitted)\n" + " --cpu [cpu] compile for [cpu] on the current target\n" + " --features [feature_str] compile with features in [feature_str] on the current target\n" "\n" "Link Options:\n" " --bundle-compiler-rt for static libraries, include compiler-rt symbols\n" @@ -533,6 +535,8 @@ int main(int argc, char **argv) { WantStackCheck want_stack_check = WantStackCheckAuto; WantCSanitize want_sanitize_c = WantCSanitizeAuto; bool function_sections = false; + const char *cpu = ""; + const char *features = ""; const char *targets_list_features_arch = nullptr; const char *targets_list_cpus_arch = nullptr; @@ -951,6 +955,10 @@ int main(int argc, char **argv) { targets_list_features_arch = argv[i]; } else if (strcmp(arg, "--list-cpus") == 0) { targets_list_cpus_arch = argv[i]; + } else if (strcmp(arg, "--cpu") == 0) { + cpu = argv[i]; + } else if (strcmp(arg, "--features") == 0) { + features = argv[i]; }else { fprintf(stderr, "Invalid argument: %s\n", arg); return print_error_usage(arg0); @@ -1248,6 +1256,7 @@ int main(int argc, char **argv) { g->system_linker_hack = system_linker_hack; g->function_sections = function_sections; + for (size_t i = 0; i < lib_dirs.length; i += 1) { codegen_add_lib_dir(g, lib_dirs.at(i)); } @@ -1269,6 +1278,13 @@ int main(int argc, char **argv) { codegen_add_rpath(g, rpath_list.at(i)); } + if (!stage2_validate_cpu_and_features(target_arch_name(target.arch), cpu, features)) { + return 1; + } + + g->llvm_cpu = cpu; + g->llvm_features = features; + codegen_set_rdynamic(g, rdynamic); if (mmacosx_version_min && mios_version_min) { fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); diff --git a/src/userland.cpp b/src/userland.cpp index 87ef99c03a..e0c8b33fa2 100644 --- a/src/userland.cpp +++ b/src/userland.cpp @@ -91,3 +91,4 @@ void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_coun void stage2_list_features_for_arch(const char *arch_name_ptr, size_t arch_name_len, bool show_subfeatures) {} void stage2_list_cpus_for_arch(const char *arch_name_ptr, size_t arch_name_len, bool show_subfeatures) {} +bool stage2_validate_cpu_and_features(const char *arch_name, const char *cpu, const char *features) { return true; } diff --git a/src/userland.h b/src/userland.h index c85684cb36..9d3e9623fb 100644 --- a/src/userland.h +++ b/src/userland.h @@ -180,4 +180,7 @@ ZIG_EXTERN_C void stage2_list_features_for_arch(const char *arch_name_ptr, size_ // ABI warning ZIG_EXTERN_C void stage2_list_cpus_for_arch(const char *arch_name_ptr, size_t arch_name_len, bool show_subfeatures); +// ABI warning +ZIG_EXTERN_C bool stage2_validate_cpu_and_features(const char *arch_name, const char *cpu, const char *features); + #endif