From 801582b56dc696c2ff28a277b05eb468788592d0 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 14 Apr 2024 00:05:09 -0600 Subject: [PATCH] Integrate optimization pipeline --- bootstrap/Compilation.zig | 26 ++++ bootstrap/backend/llvm.zig | 84 +++++++++---- bootstrap/backend/llvm_bindings.zig | 1 + build.zig | 2 +- build/test_runner.zig | 181 +++++++++++----------------- lib/std/build.nat | 9 +- lib/std/builtin.nat | 14 ++- src/llvm/llvm.cpp | 14 +-- 8 files changed, 180 insertions(+), 151 deletions(-) diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index 288afb1..f9410a9 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -49,6 +49,17 @@ const SliceField = enum { const length_field_name = @tagName(SliceField.length); +const Optimization = enum{ + none, + debug_prefer_fast, + debug_prefer_size, + lightly_optimize_for_speed, + optimize_for_speed, + optimize_for_size, + aggressively_optimize_for_speed, + aggressively_optimize_for_size, +}; + pub fn createContext(allocator: Allocator, my_allocator: *MyAllocator) !*const Context { const context: *Context = try allocator.create(Context); @@ -96,6 +107,7 @@ pub fn compileBuildExecutable(context: *const Context, arguments: []const []cons .object_path = "nat/build.o", .link_libc = @import("builtin").os.tag == .macos, .link_libcpp = false, + .optimization = .none, .generate_debug_information = true, .name = "build", .is_test = false, @@ -2790,6 +2802,7 @@ pub fn buildExecutable(context: *const Context, arguments: []const []const u8, o var link_libc = false; var maybe_executable_name: ?[]const u8 = null; var c_source_files = UnpinnedArray([]const u8){}; + var optimization = Optimization.none; const generate_debug_information = true; if (arguments.len == 0) return error.InvalidInput; @@ -2907,6 +2920,15 @@ pub fn buildExecutable(context: *const Context, arguments: []const []const u8, o } else { reportUnterminatedArgumentError(current_argument); } + } else if (byte_equal(current_argument, "-optimize")) { + if (i + 1 != arguments.len) { + i += 1; + + const optimize_string = arguments[i]; + optimization = data_structures.enumFromString(Optimization, optimize_string) orelse unreachable; + } else { + reportUnterminatedArgumentError(current_argument); + } } else { @panic(current_argument); // std.debug.panic("Unrecognized argument: {s}", .{current_argument}); @@ -2948,6 +2970,7 @@ pub fn buildExecutable(context: *const Context, arguments: []const []const u8, o .arch = arch, .os = os, .abi = abi, + .optimization = optimization, .link_libc = switch (os) { .linux => link_libc, .macos => true, @@ -16364,6 +16387,7 @@ pub const Descriptor = struct { arch: Arch, os: Os, abi: Abi, + optimization: Optimization, only_parse: bool, link_libc: bool, link_libcpp: bool, @@ -16691,3 +16715,5 @@ pub fn write(kind: LogKind, string: []const u8) !void { try std.io.getStdOut().writeAll(string); } } + + diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index d0c15b2..2b68a5f 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -123,6 +123,7 @@ pub const LLVM = struct { const createDebugInfoBuilder = bindings.NativityLLVMModuleCreateDebugInfoBuilder; const setTargetMachineDataLayout = bindings.NativityLLVMModuleSetTargetMachineDataLayout; const setTargetTriple = bindings.NativityLLVMModuleSetTargetTriple; + const runOptimizationPipeline = bindings.NativityLLVMRunOptimizationPipeline; const addPassesToEmitFile = bindings.NativityLLVMModuleAddPassesToEmitFile; }; @@ -498,6 +499,11 @@ pub const LLVM = struct { aggressive = 3, }; + pub const OptimizationLevel = extern struct{ + speed_level: c_uint, + size_level: c_uint, + }; + pub const FramePointerKind = enum(c_uint) { none = 0, non_leaf = 1, @@ -2341,18 +2347,23 @@ pub const LLVM = struct { function.setSubprogram(subprogram); switch (declaration.initial_value) { - .function_declaration => { - try llvm.llvm_external_functions.put_no_clobber(context.my_allocator, declaration, function); - }, .function_definition => |function_definition_index| { const function_definition = unit.function_definitions.get(function_definition_index); const scope = subprogram.toLocalScope().toScope(); try llvm.scope_map.put_no_clobber(context.my_allocator, &function_definition.scope.scope, scope); }, + .function_declaration => {}, else => |t| @panic(@tagName(t)), } } + + switch (declaration.initial_value) { + .function_declaration => try llvm.llvm_external_functions.put_no_clobber(context.my_allocator, declaration, function), + .function_definition => {}, + else => |t| @panic(@tagName(t)), + } + } }; @@ -2392,7 +2403,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo .context = llvm_context, .module = module, .builder = builder, - .debug_info_builder = module.createDebugInfoBuilder() orelse return Error.debug_info_builder, + .debug_info_builder = if (unit.descriptor.generate_debug_information) module.createDebugInfoBuilder() orelse return Error.debug_info_builder else undefined, .attributes = .{ .naked = llvm_context.getAttributeFromEnum(.Naked, 0), .noreturn = llvm_context.getAttributeFromEnum(.NoReturn, 0), @@ -2401,6 +2412,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo .@"noalias" = llvm_context.getAttributeFromEnum(.NoAlias, 0), }, }; + unit.descriptor.generate_debug_information = false; if (unit.descriptor.generate_debug_information) { const full_path = try std.fs.cwd().realpathAlloc(context.allocator, unit.descriptor.main_package_path); @@ -2531,30 +2543,35 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo switch (sema_instruction.*) { .push_scope => |push_scope| { - const old_scope = try llvm.getScope(unit, context, push_scope.old); - assert(@intFromEnum(push_scope.old.kind) >= @intFromEnum(Compilation.Debug.Scope.Kind.function)); + if (unit.descriptor.generate_debug_information) { + const old_scope = try llvm.getScope(unit, context, push_scope.old); + assert(@intFromEnum(push_scope.old.kind) >= @intFromEnum(Compilation.Debug.Scope.Kind.function)); - const lexical_block = llvm.debug_info_builder.createLexicalBlock(old_scope, llvm.file, push_scope.new.line + 1, push_scope.new.column + 1) orelse unreachable; - try llvm.scope_map.put_no_clobber(context.my_allocator, push_scope.new, lexical_block.toScope()); - llvm.scope = lexical_block.toScope(); + const lexical_block = llvm.debug_info_builder.createLexicalBlock(old_scope, llvm.file, push_scope.new.line + 1, push_scope.new.column + 1) orelse unreachable; + try llvm.scope_map.put_no_clobber(context.my_allocator, push_scope.new, lexical_block.toScope()); + llvm.scope = lexical_block.toScope(); + } }, .pop_scope => |pop_scope| { - const new = try llvm.getScope(unit, context, pop_scope.new); - if (pop_scope.new.kind == .function) { - assert(new.toSubprogram() orelse unreachable == llvm.function.getSubprogram() orelse unreachable); + if (unit.descriptor.generate_debug_information) { + const new = try llvm.getScope(unit, context, pop_scope.new); + if (pop_scope.new.kind == .function) { + assert(new.toSubprogram() orelse unreachable == llvm.function.getSubprogram() orelse unreachable); + } + llvm.scope = new; + var scope = pop_scope.old; + while (scope.kind != .function) { + scope = scope.parent.?; + } + const subprogram_scope = try llvm.getScope(unit, context, scope); + assert(llvm.function.getSubprogram() orelse unreachable == subprogram_scope.toSubprogram() orelse unreachable); } - llvm.scope = new; - var scope = pop_scope.old; - while (scope.kind != .function) { - scope = scope.parent.?; - } - const subprogram_scope = try llvm.getScope(unit, context, scope); - assert(llvm.function.getSubprogram() orelse unreachable == subprogram_scope.toSubprogram() orelse unreachable); }, .debug_checkpoint => |debug_checkpoint| { - const scope = try llvm.getScope(unit, context, debug_checkpoint.scope); - // assert(scope == llvm.scope); - llvm.builder.setCurrentDebugLocation(llvm.context, debug_checkpoint.line + 1, debug_checkpoint.column + 1, scope, llvm.function); + if (unit.descriptor.generate_debug_information) { + const scope = try llvm.getScope(unit, context, debug_checkpoint.scope); + llvm.builder.setCurrentDebugLocation(llvm.context, debug_checkpoint.line + 1, debug_checkpoint.column + 1, scope, llvm.function); + } }, .inline_assembly => |inline_assembly_index| { const assembly_block = unit.inline_assembly.get(inline_assembly_index); @@ -3251,7 +3268,13 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const jit = false; const code_model: LLVM.CodeModel = undefined; const is_code_model_present = false; - const codegen_optimization_level = LLVM.CodegenOptimizationLevel.none; + const codegen_optimization_level: LLVM.CodegenOptimizationLevel = switch (unit.descriptor.optimization) { + .none => .none, + .debug_prefer_fast, .debug_prefer_size => .none, + .lightly_optimize_for_speed => .less, + .optimize_for_speed, .optimize_for_size => .default, + .aggressively_optimize_for_speed, .aggressively_optimize_for_size => .aggressive, + }; const target_machine = target.createTargetMachine(target_triple.ptr, target_triple.len, cpu, cpu.len, features, features.len, LLVM.RelocationModel.static, code_model, is_code_model_present, codegen_optimization_level, jit) orelse unreachable; llvm.module.setTargetMachineDataLayout(target_machine); llvm.module.setTargetTriple(target_triple.ptr, target_triple.len); @@ -3265,6 +3288,21 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const object_file_path = slice[0 .. slice.len - 1 :0]; break :blk object_file_path; }; + + if (unit.descriptor.optimization != .none) { + const optimization_level: LLVM.OptimizationLevel = switch (unit.descriptor.optimization) { + .none => unreachable, + .debug_prefer_fast, .debug_prefer_size => .{ .speed_level = 0, .size_level = 0 }, // -O0 + .lightly_optimize_for_speed => .{ .speed_level = 1, .size_level = 0 }, // -O1 + .optimize_for_speed => .{ .speed_level = 2, .size_level = 0 }, // -O2 + .optimize_for_size => .{ .speed_level = 2, .size_level = 1 }, // -Os + .aggressively_optimize_for_speed => .{ .speed_level = 3, .size_level = 0 }, // -O3 + .aggressively_optimize_for_size => .{ .speed_level = 2, .size_level = 2 }, // -Oz + }; + + llvm.module.runOptimizationPipeline(target_machine, optimization_level); + } + const disable_verify = false; const result = llvm.module.addPassesToEmitFile(target_machine, object_file_path.ptr, object_file_path.len, LLVM.CodeGenFileType.object, disable_verify); if (!result) { diff --git a/bootstrap/backend/llvm_bindings.zig b/bootstrap/backend/llvm_bindings.zig index fff5d07..68b16ce 100644 --- a/bootstrap/backend/llvm_bindings.zig +++ b/bootstrap/backend/llvm_bindings.zig @@ -150,6 +150,7 @@ pub extern fn NativityLLVMGetTarget(target_triple_ptr: [*]const u8, target_tripl pub extern fn NativityLLVMTargetCreateTargetMachine(target: *LLVM.Target, target_triple_ptr: [*]const u8, target_triple_len: usize, cpu_ptr: [*]const u8, cpu_len: usize, features_ptr: [*]const u8, features_len: usize, relocation_model: LLVM.RelocationModel, maybe_code_model: LLVM.CodeModel, is_code_model_present: bool, optimization_level: LLVM.CodegenOptimizationLevel, jit: bool) ?*LLVM.Target.Machine; pub extern fn NativityLLVMModuleSetTargetMachineDataLayout(module: *LLVM.Module, target_machine: *LLVM.Target.Machine) void; pub extern fn NativityLLVMModuleSetTargetTriple(module: *LLVM.Module, target_triple_ptr: [*]const u8, target_triple_len: usize) void; +pub extern fn NativityLLVMRunOptimizationPipeline(module: *LLVM.Module, target_machine: *LLVM.Target.Machine, optimization_level: LLVM.OptimizationLevel) void; pub extern fn NativityLLVMModuleAddPassesToEmitFile(module: *LLVM.Module, target_machine: *LLVM.Target.Machine, object_file_path_ptr: [*]const u8, object_file_path_len: usize, codegen_file_type: LLVM.CodeGenFileType, disable_verify: bool) bool; pub extern fn NativityLLVMTypeAssertEqual(a: *LLVM.Type, b: *LLVM.Type) void; diff --git a/build.zig b/build.zig index 894de4d..d698be6 100644 --- a/build.zig +++ b/build.zig @@ -23,7 +23,7 @@ pub fn build(b: *std.Build) !void { const self_hosted_ci = b.option(bool, "self_hosted_ci", "This option enables the self-hosted CI behavior") orelse false; const third_party_ci = b.option(bool, "third_party_ci", "This option enables the third-party CI behavior") orelse false; const is_ci = self_hosted_ci or third_party_ci; - const print_stack_trace = b.option(bool, "print_stack_trace", "This option enables printing stack traces inside the compiler") orelse is_ci; //or os == .macos; + const print_stack_trace = b.option(bool, "print_stack_trace", "This option enables printing stack traces inside the compiler") orelse is_ci or os == .macos; const native_target = b.resolveTargetQuery(.{}); const optimization = b.standardOptimizeOption(.{}); const use_debug = b.option(bool, "use_debug", "This option enables the LLVM debug build in the development PC") orelse false; diff --git a/build/test_runner.zig b/build/test_runner.zig index 8e422b6..d223ed8 100644 --- a/build/test_runner.zig +++ b/build/test_runner.zig @@ -36,6 +36,7 @@ fn runStandalone(allocator: Allocator, args: struct { group_name: []const u8, self_hosted: bool, is_test: bool, + compiler_path: []const u8, }) !void { const test_names = try collectDirectoryDirEntries(allocator, args.directory_path); @@ -55,7 +56,7 @@ fn runStandalone(allocator: Allocator, args: struct { const compile_run = try std.ChildProcess.run(.{ .allocator = allocator, // TODO: delete -main_source_file? - .argv = &.{ if (args.self_hosted) self_hosted_relative_path else bootstrap_relative_path, if (args.is_test) "test" else "exe", "-main_source_file", source_file_path }, + .argv = &.{ args.compiler_path, if (args.is_test) "test" else "exe", "-main_source_file", source_file_path }, .max_output_bytes = std.math.maxInt(u64), }); ran_compilation_count += 1; @@ -120,100 +121,16 @@ fn runStandalone(allocator: Allocator, args: struct { } } -fn runStandaloneTests(allocator: Allocator, args: struct { - self_hosted: bool, -}) !void { - const standalone_test_dir_path = "test/standalone"; - const standalone_test_names = try collectDirectoryDirEntries(allocator, standalone_test_dir_path); - - const total_compilation_count = standalone_test_names.len; - var ran_compilation_count: usize = 0; - var failed_compilation_count: usize = 0; - - var ran_test_count: usize = 0; - var failed_test_count: usize = 0; - const total_test_count = standalone_test_names.len; - - for (standalone_test_names) |standalone_test_name| { - std.debug.print("{s}... ", .{standalone_test_name}); - const source_file_path = try std.mem.concat(allocator, u8, &.{ standalone_test_dir_path, "/", standalone_test_name, "/main.nat" }); - const compile_run = try std.ChildProcess.run(.{ - .allocator = allocator, - // TODO: delete -main_source_file? - .argv = &.{ if (args.self_hosted) self_hosted_relative_path else bootstrap_relative_path, "exe", "-main_source_file", source_file_path }, - .max_output_bytes = std.math.maxInt(u64), - }); - ran_compilation_count += 1; - - const compilation_result: TestError!bool = switch (compile_run.term) { - .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, - .Signal => error.signaled, - .Stopped => error.stopped, - .Unknown => error.unknown, - }; - - const compilation_success = compilation_result catch b: { - failed_compilation_count += 1; - break :b false; - }; - - std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); - if (compile_run.stdout.len > 0) { - std.debug.print("STDOUT:\n\n{s}\n\n", .{compile_run.stdout}); - } - if (compile_run.stderr.len > 0) { - std.debug.print("STDERR:\n\n{s}\n\n", .{compile_run.stderr}); - } - - if (compilation_success and !args.self_hosted) { - const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", standalone_test_name }); - const test_run = try std.ChildProcess.run(.{ - .allocator = allocator, - // TODO: delete -main_source_file? - .argv = &.{test_path}, - .max_output_bytes = std.math.maxInt(u64), - }); - ran_test_count += 1; - const test_result: TestError!bool = switch (test_run.term) { - .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, - .Signal => error.signaled, - .Stopped => error.stopped, - .Unknown => error.unknown, - }; - - const test_success = test_result catch b: { - failed_test_count += 1; - break :b false; - }; - std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); - if (test_run.stdout.len > 0) { - std.debug.print("STDOUT:\n\n{s}\n\n", .{test_run.stdout}); - } - if (test_run.stderr.len > 0) { - std.debug.print("STDERR:\n\n{s}\n\n", .{test_run.stderr}); - } - } else { - std.debug.print("\n", .{}); - } - } - - std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{ total_compilation_count, failed_compilation_count }); - std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{ total_test_count, ran_test_count, failed_test_count }); - - if (failed_compilation_count > 0 or failed_test_count > 0) { - return error.fail; - } -} - fn runBuildTests(allocator: Allocator, args: struct { self_hosted: bool, + compiler_path: []const u8, }) !void { std.debug.print("\n[BUILD TESTS]\n\n", .{}); const previous_cwd = try std.fs.cwd().realpathAlloc(allocator, "."); const test_dir_path = "test/build"; const test_names = try collectDirectoryDirEntries(allocator, test_dir_path); const test_dir_realpath = try std.fs.cwd().realpathAlloc(allocator, test_dir_path); - const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, if (args.self_hosted) self_hosted_relative_path else bootstrap_relative_path); + const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, args.compiler_path); try std.posix.chdir(test_dir_realpath); const total_compilation_count = test_names.len; @@ -303,13 +220,14 @@ fn runBuildTests(allocator: Allocator, args: struct { fn runStdTests(allocator: Allocator, args: struct { self_hosted: bool, + compiler_path: []const u8, }) !void { var errors = false; std.debug.print("std... ", .{}); const result = try std.ChildProcess.run(.{ .allocator = allocator, - .argv = &.{ if (args.self_hosted) self_hosted_relative_path else bootstrap_relative_path, "test", "-main_source_file", "lib/std/std.nat", "-name", "std" }, + .argv = &.{ args.compiler_path, "test", "-main_source_file", "lib/std/std.nat", "-name", "std" }, .max_output_bytes = std.math.maxInt(u64), }); const compilation_result: TestError!bool = switch (result.term) { @@ -362,12 +280,16 @@ fn runStdTests(allocator: Allocator, args: struct { if (errors) return error.fail; } -fn runCmakeTests(allocator: Allocator, dir_path: []const u8) !void { +fn runCmakeTests(allocator: Allocator, args: struct { + dir_path: []const u8, + compiler_path: []const u8, +}) !void { var errors = false; const original_dir = try std.fs.cwd().realpathAlloc(allocator, "."); - const cc_dir = try std.fs.cwd().openDir(dir_path, .{ + const cc_dir = try std.fs.cwd().openDir(args.dir_path, .{ .iterate = true, }); + const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, args.compiler_path); const cc_dir_path = try cc_dir.realpathAlloc(allocator, "."); try std.posix.chdir(cc_dir_path); @@ -392,9 +314,9 @@ fn runCmakeTests(allocator: Allocator, dir_path: []const u8) !void { // "--debug-output", // "-G", "Unix Makefiles", // "-DCMAKE_VERBOSE_MAKEFILE=On", - try std.mem.concat(allocator, u8, &.{ "-DCMAKE_C_COMPILER=", "nat;cc" }), - try std.mem.concat(allocator, u8, &.{ "-DCMAKE_CXX_COMPILER=", "nat;c++" }), - try std.mem.concat(allocator, u8, &.{ "-DCMAKE_ASM_COMPILER=", "nat;cc" }), + try std.mem.concat(allocator, u8, &.{ "-DCMAKE_C_COMPILER=", compiler_realpath, ";cc" }), + try std.mem.concat(allocator, u8, &.{ "-DCMAKE_CXX_COMPILER=", compiler_realpath, ";c++" }), + try std.mem.concat(allocator, u8, &.{ "-DCMAKE_ASM_COMPILER=", compiler_realpath, ";cc" }), }, .max_output_bytes = std.math.maxInt(u64), }); @@ -500,11 +422,13 @@ const self_hosted_relative_path = "nat/" ++ self_hosted_exe_name; fn compile_self_hosted(allocator: Allocator, args: struct { is_test: bool, -}) !void { + optimization: Optimization, +}) ![]const u8 { + const name = try std.mem.concat(allocator, u8, &.{self_hosted_exe_name, "_", @tagName(args.optimization)}); const compile_run = try std.ChildProcess.run(.{ .allocator = allocator, // TODO: delete -main_source_file? - .argv = &.{ bootstrap_relative_path, if (args.is_test) "test" else "exe", "-main_source_file", "src/main.nat", "-name", self_hosted_exe_name }, + .argv = &.{ bootstrap_relative_path, if (args.is_test) "test" else "exe", "-main_source_file", "src/main.nat", "-name", name, "-optimize", @tagName(args.optimization) }, .max_output_bytes = std.math.maxInt(u64), }); @@ -515,8 +439,6 @@ fn compile_self_hosted(allocator: Allocator, args: struct { .Unknown => error.unknown, }; - - _ = compilation_result catch |err| { std.debug.print("Compiling the self-hosted compiler failed!\n", .{}); if (compile_run.stdout.len > 0) { @@ -527,47 +449,75 @@ fn compile_self_hosted(allocator: Allocator, args: struct { } return err; }; + + return try std.mem.concat(allocator, u8, &.{"nat/", name}); } +const Optimization = enum{ + none, + debug_prefer_fast, + debug_prefer_size, + lightly_optimize_for_speed, + optimize_for_speed, + optimize_for_size, + aggressively_optimize_for_speed, + aggressively_optimize_for_size, +}; + fn run_test_suite(allocator: Allocator, args: struct { self_hosted: bool, + compiler_path: []const u8, }) bool { const self_hosted = args.self_hosted; - std.debug.print("TESTING {s} COMPILER...\n=================\n", .{if (self_hosted) "SELF-HOSTED" else "BOOTSTRAP"}); + std.debug.print("TESTING {s} COMPILER: {s}...\n=================\n", .{if (self_hosted) "SELF-HOSTED" else "BOOTSTRAP", args.compiler_path}); var errors = false; + runStandalone(allocator, .{ .directory_path = "test/standalone", .group_name = "STANDALONE", .is_test = false, .self_hosted = self_hosted, + .compiler_path = args.compiler_path, }) catch { errors = true; }; + runBuildTests(allocator, .{ .self_hosted = self_hosted, + .compiler_path = args.compiler_path, }) catch { errors = true; }; + runStandalone(allocator, .{ .directory_path = "test/tests", .group_name = "TEST EXECUTABLE", .is_test = true, .self_hosted = self_hosted, + .compiler_path = args.compiler_path, }) catch { errors = true; }; - // + runStdTests(allocator, .{ .self_hosted = self_hosted, + .compiler_path = args.compiler_path, }) catch { errors = true; }; if (!self_hosted) { - runCmakeTests(allocator, "test/cc") catch { + runCmakeTests(allocator, .{ + .dir_path = "test/cc", + .compiler_path = args.compiler_path, + }) catch { errors = true; }; - runCmakeTests(allocator, "test/c++") catch { + + runCmakeTests(allocator, .{ + .dir_path = "test/c++", + .compiler_path = args.compiler_path, + }) catch { errors = true; }; @@ -575,7 +525,10 @@ fn run_test_suite(allocator: Allocator, args: struct { .macos => {}, .windows => {}, .linux => switch (@import("builtin").abi) { - .gnu => runCmakeTests(allocator, "test/cc_linux") catch { + .gnu => runCmakeTests(allocator, .{ + .dir_path = "test/cc_linux", + .compiler_path = args.compiler_path, + }) catch { errors = true; }, .musl => {}, @@ -594,18 +547,24 @@ pub fn main() !void { var errors = run_test_suite(allocator, .{ .self_hosted = false, + .compiler_path = bootstrap_relative_path, }); if (!errors) { - if (compile_self_hosted(allocator, .{ - .is_test = false, - })) |_| { - errors = errors or run_test_suite(allocator, .{ - .self_hosted = true, - }); - } else |err| { - err catch {}; - errors = true; + inline for (@typeInfo(Optimization).Enum.fields) |opt| { + const optimization = @field(Optimization, opt.name); + if (compile_self_hosted(allocator, .{ + .is_test = false, + .optimization = optimization, + })) |compiler_path| { + errors = errors or run_test_suite(allocator, .{ + .self_hosted = true, + .compiler_path = compiler_path, + }); + } else |err| { + err catch {}; + errors = true; + } } } diff --git a/lib/std/build.nat b/lib/std/build.nat index 679981e..c57d01d 100644 --- a/lib/std/build.nat +++ b/lib/std/build.nat @@ -3,14 +3,7 @@ const assert = std.assert; const Allocator = std.Allocator; const Target = std.Target; -const Optimization = enum{ - none, - light, - prefer_size, - prefer_speed, - speed_aggressive, - size_aggressive, -}; +const Optimization = std.builtin.Optimization; const Executable = struct{ target: Target, diff --git a/lib/std/builtin.nat b/lib/std/builtin.nat index e952fd3..8c3548e 100644 --- a/lib/std/builtin.nat +++ b/lib/std/builtin.nat @@ -12,7 +12,8 @@ const Cpu = enum{ const Abi = enum{ none, - gnu, msvc, + gnu, + msvc, }; const CallingConvention = enum{ @@ -37,3 +38,14 @@ const TestFunction = struct{ const StructOptions = struct{ sliceable: bool = false, }; + +const Optimization = enum{ + none, + debug_prefer_fast, + debug_prefer_size, + lightly_optimize_for_speed, + optimize_for_speed, + optimize_for_size, + aggressively_optimize_for_speed, + aggressively_optimize_for_size, +}; diff --git a/src/llvm/llvm.cpp b/src/llvm/llvm.cpp index 00d29d8..183280d 100644 --- a/src/llvm/llvm.cpp +++ b/src/llvm/llvm.cpp @@ -935,17 +935,18 @@ extern "C" void NativityLLVMModuleSetTargetTriple(Module& module, const char* ta module.setTargetTriple(target_triple); } -extern "C" void NativityLLVMRunOptimizationPipeline(Module& module, TargetMachine& target_machine) +extern "C" void NativityLLVMRunOptimizationPipeline(Module& module, TargetMachine& target_machine, OptimizationLevel optimization_level) { // TODO: PGO // TODO: CS profile + bool loop_optimizations = !optimization_level.isOptimizingForSize() && optimization_level.getSpeedupLevel() >= 2; llvm::PipelineTuningOptions pipeline_tuning_options; - pipeline_tuning_options.LoopUnrolling = false; - pipeline_tuning_options.LoopInterleaving = false; - pipeline_tuning_options.LoopVectorization = false; - pipeline_tuning_options.SLPVectorization = false; - pipeline_tuning_options.MergeFunctions = false; + pipeline_tuning_options.LoopUnrolling = loop_optimizations; + pipeline_tuning_options.LoopInterleaving = loop_optimizations; + pipeline_tuning_options.LoopVectorization = loop_optimizations; + pipeline_tuning_options.SLPVectorization = loop_optimizations; + pipeline_tuning_options.MergeFunctions = true; pipeline_tuning_options.CallGraphProfile = false; pipeline_tuning_options.UnifiedLTO = false; @@ -971,7 +972,6 @@ extern "C" void NativityLLVMRunOptimizationPipeline(Module& module, TargetMachin ModulePassManager module_pass_manager; - OptimizationLevel optimization_level = OptimizationLevel::O0; bool thin_lto = false; bool lto = false;