authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-22 23:36:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-23 18:18:07-07:00
log0d4b6ac7417d1094ac972981b0241444ce2380ba
tree52afdf98a47470a20067188fcd7b6a85dff61675
parentab4f3aee3da8f2658c019c05de2cce991d010f01

add LTO support

The CLI gains -flto and -fno-lto options to override the default. However, the cool thing about this is that the defaults are great! In general when you use build-exe in release mode, Zig will enable LTO if it would work and it would help. zig cc supports detecting and honoring the -flto and -fno-lto flags as well. The linkWithLld functions are improved to all be the same with regards to copying the artifact instead of trying to pass single objects through LLD with -r. There is possibly a future improvement here as well; see the respective TODOs. stage1 is updated to support outputting LLVM bitcode instead of machine code when lto is enabled. This allows LLVM to optimize across the Zig and C/C++ code boundary. closes #2845

17 files changed, 576 insertions(+), 398 deletions(-)

src/Compilation.zig+37
......@@ -444,6 +444,7 @@ pub const InitOptions = struct {
444444 want_valgrind: ?bool = null,
445445 want_tsan: ?bool = null,
446446 want_compiler_rt: ?bool = null,
447 want_lto: ?bool = null,
447448 use_llvm: ?bool = null,
448449 use_lld: ?bool = null,
449450 use_clang: ?bool = null,
......@@ -602,6 +603,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
602603 if (ofmt == .c)
603604 break :blk false;
604605
606 if (options.want_lto) |lto| {
607 if (lto) {
608 break :blk true;
609 }
610 }
611
605612 // Our linker can't handle objects or most advanced options yet.
606613 if (options.link_objects.len != 0 or
607614 options.c_source_files.len != 0 or
......@@ -647,6 +654,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
647654 break :outer opts;
648655 } else .{};
649656
657 const lto = blk: {
658 if (options.want_lto) |explicit| {
659 if (!use_lld)
660 return error.LtoUnavailableWithoutLld;
661 break :blk explicit;
662 } else if (!use_lld) {
663 break :blk false;
664 } else if (options.c_source_files.len == 0) {
665 break :blk false;
666 } else if (darwin_options.system_linker_hack) {
667 break :blk false;
668 } else switch (options.output_mode) {
669 .Lib, .Obj => break :blk false,
670 .Exe => switch (options.optimize_mode) {
671 .Debug => break :blk false,
672 .ReleaseSafe, .ReleaseFast, .ReleaseSmall => break :blk true,
673 },
674 }
675 };
676
650677 const tsan = options.want_tsan orelse false;
651678
652679 const link_libc = options.link_libc or target_util.osRequiresLibC(options.target) or tsan;
......@@ -821,6 +848,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
821848 cache.hash.add(ofmt);
822849 cache.hash.add(pic);
823850 cache.hash.add(pie);
851 cache.hash.add(lto);
824852 cache.hash.add(tsan);
825853 cache.hash.add(stack_check);
826854 cache.hash.add(red_zone);
......@@ -1022,6 +1050,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
10221050 .libc_installation = libc_dirs.libc_installation,
10231051 .pic = pic,
10241052 .pie = pie,
1053 .lto = lto,
10251054 .valgrind = valgrind,
10261055 .tsan = tsan,
10271056 .stack_check = stack_check,
......@@ -2233,6 +2262,9 @@ pub fn addCCArgs(
22332262 "-nostdinc",
22342263 "-fno-spell-checking",
22352264 });
2265 if (comp.bin_file.options.lto) {
2266 try argv.append("-flto");
2267 }
22362268
22372269 // According to Rich Felker libc headers are supposed to go before C language headers.
22382270 // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
......@@ -3255,6 +3287,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
32553287 .err_color = @enumToInt(comp.color),
32563288 .pic = comp.bin_file.options.pic,
32573289 .pie = comp.bin_file.options.pie,
3290 .lto = comp.bin_file.options.lto,
32583291 .link_libc = comp.bin_file.options.link_libc,
32593292 .link_libcpp = comp.bin_file.options.link_libcpp,
32603293 .strip = comp.bin_file.options.strip,
......@@ -3415,6 +3448,10 @@ pub fn build_crt_file(
34153448 .want_tsan = false,
34163449 .want_pic = comp.bin_file.options.pic,
34173450 .want_pie = comp.bin_file.options.pie,
3451 .want_lto = switch (output_mode) {
3452 .Lib => comp.bin_file.options.lto,
3453 .Obj, .Exe => false,
3454 },
34183455 .emit_h = null,
34193456 .strip = comp.compilerRtStrip(),
34203457 .is_native_os = comp.bin_file.options.is_native_os,
src/clang_options_data.zig+24-3
......@@ -2732,7 +2732,14 @@ flagpd1("fkeep-static-consts"),
27322732flagpd1("flat_namespace"),
27332733flagpd1("flax-vector-conversions"),
27342734flagpd1("flimit-debug-info"),
2735flagpd1("flto"),
2735.{
2736 .name = "flto",
2737 .syntax = .flag,
2738 .zig_equivalent = .lto,
2739 .pd1 = true,
2740 .pd2 = false,
2741 .psl = false,
2742},
27362743flagpd1("flto-unit"),
27372744flagpd1("flto-visibility-public-std"),
27382745sepd1("fmacro-backtrace-limit"),
......@@ -2942,7 +2949,14 @@ flagpd1("fno-jump-tables"),
29422949flagpd1("fno-keep-static-consts"),
29432950flagpd1("fno-lax-vector-conversions"),
29442951flagpd1("fno-limit-debug-info"),
2945flagpd1("fno-lto"),
2952.{
2953 .name = "fno-lto",
2954 .syntax = .flag,
2955 .zig_equivalent = .no_lto,
2956 .pd1 = true,
2957 .pd2 = false,
2958 .psl = false,
2959},
29462960flagpd1("fno-lto-unit"),
29472961flagpd1("fno-math-builtin"),
29482962flagpd1("fno-math-errno"),
......@@ -5638,7 +5652,14 @@ jspd1("Ttext"),
56385652 .pd2 = true,
56395653 .psl = false,
56405654},
5641joinpd1("flto="),
5655.{
5656 .name = "flto=",
5657 .syntax = .joined,
5658 .zig_equivalent = .lto,
5659 .pd1 = true,
5660 .pd2 = false,
5661 .psl = false,
5662},
56425663joinpd1("gcoff"),
56435664joinpd1("mabi="),
56445665joinpd1("mabs="),
src/codegen/llvm/bindings.zig+7-1
......@@ -243,7 +243,13 @@ pub const TargetMachine = opaque {
243243 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
244244
245245 pub const emitToFile = LLVMTargetMachineEmitToFile;
246 extern fn LLVMTargetMachineEmitToFile(*const TargetMachine, M: *const Module, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool;
246 extern fn LLVMTargetMachineEmitToFile(
247 *const TargetMachine,
248 M: *const Module,
249 Filename: [*:0]const u8,
250 codegen: CodeGenFileType,
251 ErrorMessage: *[*:0]const u8,
252 ) LLVMBool;
247253};
248254
249255pub const CodeMode = extern enum {
src/link.zig+1
......@@ -74,6 +74,7 @@ pub const Options = struct {
7474 is_native_abi: bool,
7575 pic: bool,
7676 pie: bool,
77 lto: bool,
7778 valgrind: bool,
7879 tsan: bool,
7980 stack_check: bool,
src/link/Coff.zig+7
......@@ -945,6 +945,13 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
945945 if (!self.base.options.strip) {
946946 try argv.append("-DEBUG");
947947 }
948 if (self.base.options.lto) {
949 switch (self.base.options.optimize_mode) {
950 .Debug => {},
951 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
952 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
953 }
954 }
948955 if (self.base.options.output_mode == .Exe) {
949956 const stack_size = self.base.options.stack_size_override orelse 16777216;
950957 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
src/link/Elf.zig+319-285
......@@ -1384,351 +1384,385 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13841384 };
13851385 }
13861386
1387 // Create an LLD command line and invoke it.
1388 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1389 defer argv.deinit();
1390 // We will invoke ourselves as a child process to gain access to LLD.
1391 // This is necessary because LLD does not behave properly as a library -
1392 // it calls exit() and does not reset all global data between invocations.
1393 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });
1394 if (is_obj) {
1395 try argv.append("-r");
1396 }
1387 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
1388 if (self.base.options.output_mode == .Obj and self.base.options.lto) {
1389 // In this case we must do a simple file copy
1390 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1391 // build-obj. See also the corresponding TODO in linkAsArchive.
1392 const the_object_path = blk: {
1393 if (self.base.options.objects.len != 0)
1394 break :blk self.base.options.objects[0];
1395
1396 if (comp.c_object_table.count() != 0)
1397 break :blk comp.c_object_table.items()[0].key.status.success.object_path;
1398
1399 if (module_obj_path) |p|
1400 break :blk p;
1401
1402 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1403 // regarding eliding redundant object -> object transformations.
1404 return error.NoObjectsToLink;
1405 };
1406 // This can happen when using --enable-cache and using the stage1 backend. In this case
1407 // we can skip the file copy.
1408 if (!mem.eql(u8, the_object_path, full_out_path)) {
1409 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1410 }
1411 } else {
13971412
1398 try argv.append("-error-limit=0");
1413 // Create an LLD command line and invoke it.
1414 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1415 defer argv.deinit();
1416 // We will invoke ourselves as a child process to gain access to LLD.
1417 // This is necessary because LLD does not behave properly as a library -
1418 // it calls exit() and does not reset all global data between invocations.
1419 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });
1420 if (is_obj) {
1421 try argv.append("-r");
1422 }
13991423
1400 if (self.base.options.output_mode == .Exe) {
1401 try argv.append("-z");
1402 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
1403 }
1424 try argv.append("-error-limit=0");
14041425
1405 if (self.base.options.image_base_override) |image_base| {
1406 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
1407 }
1426 if (self.base.options.lto) {
1427 switch (self.base.options.optimize_mode) {
1428 .Debug => {},
1429 .ReleaseSmall => try argv.append("-O2"),
1430 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
1431 }
1432 }
14081433
1409 if (self.base.options.linker_script) |linker_script| {
1410 try argv.append("-T");
1411 try argv.append(linker_script);
1412 }
1434 if (self.base.options.output_mode == .Exe) {
1435 try argv.append("-z");
1436 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
1437 }
14131438
1414 if (gc_sections) {
1415 try argv.append("--gc-sections");
1416 }
1439 if (self.base.options.image_base_override) |image_base| {
1440 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
1441 }
14171442
1418 if (self.base.options.eh_frame_hdr) {
1419 try argv.append("--eh-frame-hdr");
1420 }
1443 if (self.base.options.linker_script) |linker_script| {
1444 try argv.append("-T");
1445 try argv.append(linker_script);
1446 }
14211447
1422 if (self.base.options.emit_relocs) {
1423 try argv.append("--emit-relocs");
1424 }
1448 if (gc_sections) {
1449 try argv.append("--gc-sections");
1450 }
14251451
1426 if (self.base.options.rdynamic) {
1427 try argv.append("--export-dynamic");
1428 }
1452 if (self.base.options.eh_frame_hdr) {
1453 try argv.append("--eh-frame-hdr");
1454 }
14291455
1430 try argv.appendSlice(self.base.options.extra_lld_args);
1456 if (self.base.options.emit_relocs) {
1457 try argv.append("--emit-relocs");
1458 }
14311459
1432 if (self.base.options.z_nodelete) {
1433 try argv.append("-z");
1434 try argv.append("nodelete");
1435 }
1436 if (self.base.options.z_defs) {
1437 try argv.append("-z");
1438 try argv.append("defs");
1439 }
1460 if (self.base.options.rdynamic) {
1461 try argv.append("--export-dynamic");
1462 }
14401463
1441 if (getLDMOption(target)) |ldm| {
1442 // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
1443 const arg = if (target.os.tag == .freebsd)
1444 try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm})
1445 else
1446 ldm;
1447 try argv.append("-m");
1448 try argv.append(arg);
1449 }
1464 try argv.appendSlice(self.base.options.extra_lld_args);
14501465
1451 if (self.base.options.link_mode == .Static) {
1452 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
1453 try argv.append("-Bstatic");
1454 } else {
1455 try argv.append("-static");
1466 if (self.base.options.z_nodelete) {
1467 try argv.append("-z");
1468 try argv.append("nodelete");
1469 }
1470 if (self.base.options.z_defs) {
1471 try argv.append("-z");
1472 try argv.append("defs");
14561473 }
1457 } else if (is_dyn_lib) {
1458 try argv.append("-shared");
1459 }
14601474
1461 if (self.base.options.pie and self.base.options.output_mode == .Exe) {
1462 try argv.append("-pie");
1463 }
1475 if (getLDMOption(target)) |ldm| {
1476 // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
1477 const arg = if (target.os.tag == .freebsd)
1478 try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm})
1479 else
1480 ldm;
1481 try argv.append("-m");
1482 try argv.append(arg);
1483 }
14641484
1465 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
1466 try argv.append("-o");
1467 try argv.append(full_out_path);
1485 if (self.base.options.link_mode == .Static) {
1486 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
1487 try argv.append("-Bstatic");
1488 } else {
1489 try argv.append("-static");
1490 }
1491 } else if (is_dyn_lib) {
1492 try argv.append("-shared");
1493 }
14681494
1469 if (link_in_crt) {
1470 const crt1o: []const u8 = o: {
1471 if (target.os.tag == .netbsd) {
1472 break :o "crt0.o";
1473 } else if (target.os.tag == .openbsd) {
1474 if (self.base.options.link_mode == .Static) {
1475 break :o "rcrt0.o";
1476 } else {
1495 if (self.base.options.pie and self.base.options.output_mode == .Exe) {
1496 try argv.append("-pie");
1497 }
1498
1499 try argv.append("-o");
1500 try argv.append(full_out_path);
1501
1502 if (link_in_crt) {
1503 const crt1o: []const u8 = o: {
1504 if (target.os.tag == .netbsd) {
14771505 break :o "crt0.o";
1478 }
1479 } else if (target.isAndroid()) {
1480 if (self.base.options.link_mode == .Dynamic) {
1481 break :o "crtbegin_dynamic.o";
1482 } else {
1483 break :o "crtbegin_static.o";
1484 }
1485 } else if (self.base.options.link_mode == .Static) {
1486 if (self.base.options.pie) {
1487 break :o "rcrt1.o";
1506 } else if (target.os.tag == .openbsd) {
1507 if (self.base.options.link_mode == .Static) {
1508 break :o "rcrt0.o";
1509 } else {
1510 break :o "crt0.o";
1511 }
1512 } else if (target.isAndroid()) {
1513 if (self.base.options.link_mode == .Dynamic) {
1514 break :o "crtbegin_dynamic.o";
1515 } else {
1516 break :o "crtbegin_static.o";
1517 }
1518 } else if (self.base.options.link_mode == .Static) {
1519 if (self.base.options.pie) {
1520 break :o "rcrt1.o";
1521 } else {
1522 break :o "crt1.o";
1523 }
14881524 } else {
1489 break :o "crt1.o";
1525 break :o "Scrt1.o";
14901526 }
1491 } else {
1492 break :o "Scrt1.o";
1527 };
1528 try argv.append(try comp.get_libc_crt_file(arena, crt1o));
1529 if (target_util.libc_needs_crti_crtn(target)) {
1530 try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
1531 }
1532 if (target.os.tag == .openbsd) {
1533 try argv.append(try comp.get_libc_crt_file(arena, "crtbegin.o"));
14931534 }
1494 };
1495 try argv.append(try comp.get_libc_crt_file(arena, crt1o));
1496 if (target_util.libc_needs_crti_crtn(target)) {
1497 try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
1498 }
1499 if (target.os.tag == .openbsd) {
1500 try argv.append(try comp.get_libc_crt_file(arena, "crtbegin.o"));
15011535 }
1502 }
15031536
1504 // rpaths
1505 var rpath_table = std.StringHashMap(void).init(self.base.allocator);
1506 defer rpath_table.deinit();
1507 for (self.base.options.rpath_list) |rpath| {
1508 if ((try rpath_table.fetchPut(rpath, {})) == null) {
1509 try argv.append("-rpath");
1510 try argv.append(rpath);
1537 // rpaths
1538 var rpath_table = std.StringHashMap(void).init(self.base.allocator);
1539 defer rpath_table.deinit();
1540 for (self.base.options.rpath_list) |rpath| {
1541 if ((try rpath_table.fetchPut(rpath, {})) == null) {
1542 try argv.append("-rpath");
1543 try argv.append(rpath);
1544 }
15111545 }
1512 }
1513 if (self.base.options.each_lib_rpath) {
1514 var test_path = std.ArrayList(u8).init(self.base.allocator);
1515 defer test_path.deinit();
1516 for (self.base.options.lib_dirs) |lib_dir_path| {
1517 for (self.base.options.system_libs.items()) |entry| {
1518 const link_lib = entry.key;
1519 test_path.shrinkRetainingCapacity(0);
1520 const sep = fs.path.sep_str;
1521 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib });
1522 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1523 error.FileNotFound => continue,
1524 else => |e| return e,
1525 };
1526 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
1527 try argv.append("-rpath");
1528 try argv.append(lib_dir_path);
1546 if (self.base.options.each_lib_rpath) {
1547 var test_path = std.ArrayList(u8).init(self.base.allocator);
1548 defer test_path.deinit();
1549 for (self.base.options.lib_dirs) |lib_dir_path| {
1550 for (self.base.options.system_libs.items()) |entry| {
1551 const link_lib = entry.key;
1552 test_path.shrinkRetainingCapacity(0);
1553 const sep = fs.path.sep_str;
1554 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib });
1555 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1556 error.FileNotFound => continue,
1557 else => |e| return e,
1558 };
1559 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
1560 try argv.append("-rpath");
1561 try argv.append(lib_dir_path);
1562 }
15291563 }
15301564 }
15311565 }
1532 }
15331566
1534 for (self.base.options.lib_dirs) |lib_dir| {
1535 try argv.append("-L");
1536 try argv.append(lib_dir);
1537 }
1538
1539 if (self.base.options.link_libc) {
1540 if (self.base.options.libc_installation) |libc_installation| {
1567 for (self.base.options.lib_dirs) |lib_dir| {
15411568 try argv.append("-L");
1542 try argv.append(libc_installation.crt_dir.?);
1569 try argv.append(lib_dir);
15431570 }
15441571
1545 if (have_dynamic_linker) {
1546 if (self.base.options.dynamic_linker) |dynamic_linker| {
1547 try argv.append("-dynamic-linker");
1548 try argv.append(dynamic_linker);
1572 if (self.base.options.link_libc) {
1573 if (self.base.options.libc_installation) |libc_installation| {
1574 try argv.append("-L");
1575 try argv.append(libc_installation.crt_dir.?);
15491576 }
1550 }
1551 }
15521577
1553 if (is_dyn_lib) {
1554 if (self.base.options.soname) |soname| {
1555 try argv.append("-soname");
1556 try argv.append(soname);
1557 }
1558 if (self.base.options.version_script) |version_script| {
1559 try argv.append("-version-script");
1560 try argv.append(version_script);
1578 if (have_dynamic_linker) {
1579 if (self.base.options.dynamic_linker) |dynamic_linker| {
1580 try argv.append("-dynamic-linker");
1581 try argv.append(dynamic_linker);
1582 }
1583 }
15611584 }
1562 }
1563
1564 // Positional arguments to the linker such as object files.
1565 try argv.appendSlice(self.base.options.objects);
15661585
1567 for (comp.c_object_table.items()) |entry| {
1568 try argv.append(entry.key.status.success.object_path);
1569 }
1586 if (is_dyn_lib) {
1587 if (self.base.options.soname) |soname| {
1588 try argv.append("-soname");
1589 try argv.append(soname);
1590 }
1591 if (self.base.options.version_script) |version_script| {
1592 try argv.append("-version-script");
1593 try argv.append(version_script);
1594 }
1595 }
15701596
1571 if (module_obj_path) |p| {
1572 try argv.append(p);
1573 }
1597 // Positional arguments to the linker such as object files.
1598 try argv.appendSlice(self.base.options.objects);
15741599
1575 // TSAN
1576 if (self.base.options.tsan) {
1577 try argv.append(comp.tsan_static_lib.?.full_object_path);
1578 }
1600 for (comp.c_object_table.items()) |entry| {
1601 try argv.append(entry.key.status.success.object_path);
1602 }
15791603
1580 // libc
1581 // TODO: enable when stage2 can build c.zig
1582 if (is_exe_or_dyn_lib and
1583 !self.base.options.skip_linker_dependencies and
1584 !self.base.options.link_libc and
1585 build_options.is_stage1)
1586 {
1587 try argv.append(comp.libc_static_lib.?.full_object_path);
1588 }
1604 if (module_obj_path) |p| {
1605 try argv.append(p);
1606 }
15891607
1590 // compiler-rt
1591 if (compiler_rt_path) |p| {
1592 try argv.append(p);
1593 }
1608 // TSAN
1609 if (self.base.options.tsan) {
1610 try argv.append(comp.tsan_static_lib.?.full_object_path);
1611 }
15941612
1595 // Shared libraries.
1596 if (is_exe_or_dyn_lib) {
1597 const system_libs = self.base.options.system_libs.items();
1598 try argv.ensureCapacity(argv.items.len + system_libs.len);
1599 for (system_libs) |entry| {
1600 const link_lib = entry.key;
1601 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
1602 // (the check for that needs to be earlier), but they could be full paths to .so files, in which
1603 // case we want to avoid prepending "-l".
1604 const ext = Compilation.classifyFileExt(link_lib);
1605 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
1606 argv.appendAssumeCapacity(arg);
1613 // libc
1614 // TODO: enable when stage2 can build c.zig
1615 if (is_exe_or_dyn_lib and
1616 !self.base.options.skip_linker_dependencies and
1617 !self.base.options.link_libc and
1618 build_options.is_stage1)
1619 {
1620 try argv.append(comp.libc_static_lib.?.full_object_path);
16071621 }
16081622
1609 // libc++ dep
1610 if (self.base.options.link_libcpp) {
1611 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1612 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1623 // compiler-rt
1624 if (compiler_rt_path) |p| {
1625 try argv.append(p);
16131626 }
16141627
1615 // libc dep
1616 if (self.base.options.link_libc) {
1617 if (self.base.options.libc_installation != null) {
1618 if (self.base.options.link_mode == .Static) {
1619 try argv.append("--start-group");
1620 try argv.append("-lc");
1621 try argv.append("-lm");
1622 try argv.append("--end-group");
1623 } else {
1624 try argv.append("-lc");
1625 try argv.append("-lm");
1626 }
1628 // Shared libraries.
1629 if (is_exe_or_dyn_lib) {
1630 const system_libs = self.base.options.system_libs.items();
1631 try argv.ensureCapacity(argv.items.len + system_libs.len);
1632 for (system_libs) |entry| {
1633 const link_lib = entry.key;
1634 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
1635 // (the check for that needs to be earlier), but they could be full paths to .so files, in which
1636 // case we want to avoid prepending "-l".
1637 const ext = Compilation.classifyFileExt(link_lib);
1638 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
1639 argv.appendAssumeCapacity(arg);
1640 }
16271641
1628 if (target.os.tag == .freebsd or target.os.tag == .netbsd or target.os.tag == .openbsd) {
1629 try argv.append("-lpthread");
1630 }
1631 } else if (target.isGnuLibC()) {
1632 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1633 for (glibc.libs) |lib| {
1634 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1635 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1636 });
1637 try argv.append(lib_path);
1642 // libc++ dep
1643 if (self.base.options.link_libcpp) {
1644 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1645 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1646 }
1647
1648 // libc dep
1649 if (self.base.options.link_libc) {
1650 if (self.base.options.libc_installation != null) {
1651 if (self.base.options.link_mode == .Static) {
1652 try argv.append("--start-group");
1653 try argv.append("-lc");
1654 try argv.append("-lm");
1655 try argv.append("--end-group");
1656 } else {
1657 try argv.append("-lc");
1658 try argv.append("-lm");
1659 }
1660
1661 if (target.os.tag == .freebsd or target.os.tag == .netbsd or target.os.tag == .openbsd) {
1662 try argv.append("-lpthread");
1663 }
1664 } else if (target.isGnuLibC()) {
1665 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1666 for (glibc.libs) |lib| {
1667 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1668 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1669 });
1670 try argv.append(lib_path);
1671 }
1672 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
1673 } else if (target.isMusl()) {
1674 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1675 try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
1676 .Static => "libc.a",
1677 .Dynamic => "libc.so",
1678 }));
1679 } else if (self.base.options.link_libcpp) {
1680 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1681 } else {
1682 unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
16381683 }
1639 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
1640 } else if (target.isMusl()) {
1641 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1642 try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
1643 .Static => "libc.a",
1644 .Dynamic => "libc.so",
1645 }));
1646 } else if (self.base.options.link_libcpp) {
1647 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1648 } else {
1649 unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
16501684 }
16511685 }
1652 }
16531686
1654 // crt end
1655 if (link_in_crt) {
1656 if (target.isAndroid()) {
1657 try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
1658 } else if (target.os.tag == .openbsd) {
1659 try argv.append(try comp.get_libc_crt_file(arena, "crtend.o"));
1660 } else if (target_util.libc_needs_crti_crtn(target)) {
1661 try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
1687 // crt end
1688 if (link_in_crt) {
1689 if (target.isAndroid()) {
1690 try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
1691 } else if (target.os.tag == .openbsd) {
1692 try argv.append(try comp.get_libc_crt_file(arena, "crtend.o"));
1693 } else if (target_util.libc_needs_crti_crtn(target)) {
1694 try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
1695 }
16621696 }
1663 }
16641697
1665 if (allow_shlib_undefined) {
1666 try argv.append("--allow-shlib-undefined");
1667 }
1698 if (allow_shlib_undefined) {
1699 try argv.append("--allow-shlib-undefined");
1700 }
16681701
1669 if (self.base.options.bind_global_refs_locally) {
1670 try argv.append("-Bsymbolic");
1671 }
1702 if (self.base.options.bind_global_refs_locally) {
1703 try argv.append("-Bsymbolic");
1704 }
16721705
1673 if (self.base.options.verbose_link) {
1674 // Skip over our own name so that the LLD linker name is the first argv item.
1675 Compilation.dump_argv(argv.items[1..]);
1676 }
1706 if (self.base.options.verbose_link) {
1707 // Skip over our own name so that the LLD linker name is the first argv item.
1708 Compilation.dump_argv(argv.items[1..]);
1709 }
16771710
1678 // Sadly, we must run LLD as a child process because it does not behave
1679 // properly as a library.
1680 const child = try std.ChildProcess.init(argv.items, arena);
1681 defer child.deinit();
1711 // Sadly, we must run LLD as a child process because it does not behave
1712 // properly as a library.
1713 const child = try std.ChildProcess.init(argv.items, arena);
1714 defer child.deinit();
16821715
1683 if (comp.clang_passthrough_mode) {
1684 child.stdin_behavior = .Inherit;
1685 child.stdout_behavior = .Inherit;
1686 child.stderr_behavior = .Inherit;
1716 if (comp.clang_passthrough_mode) {
1717 child.stdin_behavior = .Inherit;
1718 child.stdout_behavior = .Inherit;
1719 child.stderr_behavior = .Inherit;
16871720
1688 const term = child.spawnAndWait() catch |err| {
1689 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1690 return error.UnableToSpawnSelf;
1691 };
1692 switch (term) {
1693 .Exited => |code| {
1694 if (code != 0) {
1695 // TODO https://github.com/ziglang/zig/issues/6342
1696 std.process.exit(1);
1697 }
1698 },
1699 else => std.process.abort(),
1700 }
1701 } else {
1702 child.stdin_behavior = .Ignore;
1703 child.stdout_behavior = .Ignore;
1704 child.stderr_behavior = .Pipe;
1721 const term = child.spawnAndWait() catch |err| {
1722 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1723 return error.UnableToSpawnSelf;
1724 };
1725 switch (term) {
1726 .Exited => |code| {
1727 if (code != 0) {
1728 // TODO https://github.com/ziglang/zig/issues/6342
1729 std.process.exit(1);
1730 }
1731 },
1732 else => std.process.abort(),
1733 }
1734 } else {
1735 child.stdin_behavior = .Ignore;
1736 child.stdout_behavior = .Ignore;
1737 child.stderr_behavior = .Pipe;
17051738
1706 try child.spawn();
1739 try child.spawn();
17071740
1708 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1741 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
17091742
1710 const term = child.wait() catch |err| {
1711 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1712 return error.UnableToSpawnSelf;
1713 };
1743 const term = child.wait() catch |err| {
1744 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1745 return error.UnableToSpawnSelf;
1746 };
17141747
1715 switch (term) {
1716 .Exited => |code| {
1717 if (code != 0) {
1718 // TODO parse this output and surface with the Compilation API rather than
1719 // directly outputting to stderr here.
1720 std.debug.print("{s}", .{stderr});
1721 return error.LLDReportedFailure;
1722 }
1723 },
1724 else => {
1725 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1726 return error.LLDCrashed;
1727 },
1728 }
1748 switch (term) {
1749 .Exited => |code| {
1750 if (code != 0) {
1751 // TODO parse this output and surface with the Compilation API rather than
1752 // directly outputting to stderr here.
1753 std.debug.print("{s}", .{stderr});
1754 return error.LLDReportedFailure;
1755 }
1756 },
1757 else => {
1758 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1759 return error.LLDCrashed;
1760 },
1761 }
17291762
1730 if (stderr.len != 0) {
1731 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1763 if (stderr.len != 0) {
1764 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1765 }
17321766 }
17331767 }
17341768
src/link/MachO.zig+7
......@@ -620,6 +620,13 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
620620 try argv.append("0");
621621 }
622622
623 if (self.base.options.lto) {
624 switch (self.base.options.optimize_mode) {
625 .Debug => {},
626 .ReleaseSmall => try argv.append("-O2"),
627 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
628 }
629 }
623630 try argv.append("-demangle");
624631
625632 if (self.base.options.rdynamic and !self.base.options.system_linker_hack) {
src/link/Wasm.zig+136-101
......@@ -362,122 +362,157 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
362362 };
363363 }
364364
365 const is_obj = self.base.options.output_mode == .Obj;
366
367 // Create an LLD command line and invoke it.
368 var argv = std.ArrayList([]const u8).init(self.base.allocator);
369 defer argv.deinit();
370 // We will invoke ourselves as a child process to gain access to LLD.
371 // This is necessary because LLD does not behave properly as a library -
372 // it calls exit() and does not reset all global data between invocations.
373 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" });
374 if (is_obj) {
375 try argv.append("-r");
376 }
377
378 try argv.append("-error-limit=0");
365 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
379366
380 if (self.base.options.output_mode == .Exe) {
381 // Increase the default stack size to a more reasonable value of 1MB instead of
382 // the default of 1 Wasm page being 64KB, unless overriden by the user.
383 try argv.append("-z");
384 const stack_size = self.base.options.stack_size_override orelse 1048576;
385 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
386 try argv.append(arg);
387
388 // Put stack before globals so that stack overflow results in segfault immediately
389 // before corrupting globals. See https://github.com/ziglang/zig/issues/4496
390 try argv.append("--stack-first");
391 } else {
392 try argv.append("--no-entry"); // So lld doesn't look for _start.
393 try argv.append("--export-all");
394 }
395 try argv.appendSlice(&[_][]const u8{
396 "--allow-undefined",
397 "-o",
398 try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}),
399 });
367 if (self.base.options.output_mode == .Obj) {
368 // LLD's WASM driver does not support the equvialent of `-r` so we do a simple file copy
369 // here. TODO: think carefully about how we can avoid this redundant operation when doing
370 // build-obj. See also the corresponding TODO in linkAsArchive.
371 const the_object_path = blk: {
372 if (self.base.options.objects.len != 0)
373 break :blk self.base.options.objects[0];
400374
401 // Positional arguments to the linker such as object files.
402 try argv.appendSlice(self.base.options.objects);
375 if (comp.c_object_table.count() != 0)
376 break :blk comp.c_object_table.items()[0].key.status.success.object_path;
403377
404 for (comp.c_object_table.items()) |entry| {
405 try argv.append(entry.key.status.success.object_path);
406 }
407 if (module_obj_path) |p| {
408 try argv.append(p);
409 }
378 if (module_obj_path) |p|
379 break :blk p;
410380
411 if (self.base.options.output_mode != .Obj and
412 !self.base.options.skip_linker_dependencies and
413 !self.base.options.link_libc)
414 {
415 try argv.append(comp.libc_static_lib.?.full_object_path);
416 }
381 // TODO I think this is unreachable. Audit this situation when solving the above TODO
382 // regarding eliding redundant object -> object transformations.
383 return error.NoObjectsToLink;
384 };
385 // This can happen when using --enable-cache and using the stage1 backend. In this case
386 // we can skip the file copy.
387 if (!mem.eql(u8, the_object_path, full_out_path)) {
388 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
389 }
390 } else {
391 const is_obj = self.base.options.output_mode == .Obj;
392
393 // Create an LLD command line and invoke it.
394 var argv = std.ArrayList([]const u8).init(self.base.allocator);
395 defer argv.deinit();
396 // We will invoke ourselves as a child process to gain access to LLD.
397 // This is necessary because LLD does not behave properly as a library -
398 // it calls exit() and does not reset all global data between invocations.
399 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" });
400 if (is_obj) {
401 try argv.append("-r");
402 }
417403
418 if (compiler_rt_path) |p| {
419 try argv.append(p);
420 }
404 try argv.append("-error-limit=0");
421405
422 if (self.base.options.verbose_link) {
423 // Skip over our own name so that the LLD linker name is the first argv item.
424 Compilation.dump_argv(argv.items[1..]);
425 }
406 if (self.base.options.lto) {
407 switch (self.base.options.optimize_mode) {
408 .Debug => {},
409 .ReleaseSmall => try argv.append("-O2"),
410 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
411 }
412 }
426413
427 // Sadly, we must run LLD as a child process because it does not behave
428 // properly as a library.
429 const child = try std.ChildProcess.init(argv.items, arena);
430 defer child.deinit();
414 if (self.base.options.output_mode == .Exe) {
415 // Increase the default stack size to a more reasonable value of 1MB instead of
416 // the default of 1 Wasm page being 64KB, unless overriden by the user.
417 try argv.append("-z");
418 const stack_size = self.base.options.stack_size_override orelse 1048576;
419 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
420 try argv.append(arg);
421
422 // Put stack before globals so that stack overflow results in segfault immediately
423 // before corrupting globals. See https://github.com/ziglang/zig/issues/4496
424 try argv.append("--stack-first");
425 } else {
426 try argv.append("--no-entry"); // So lld doesn't look for _start.
427 try argv.append("--export-all");
428 }
429 try argv.appendSlice(&[_][]const u8{
430 "--allow-undefined",
431 "-o",
432 full_out_path,
433 });
431434
432 if (comp.clang_passthrough_mode) {
433 child.stdin_behavior = .Inherit;
434 child.stdout_behavior = .Inherit;
435 child.stderr_behavior = .Inherit;
435 // Positional arguments to the linker such as object files.
436 try argv.appendSlice(self.base.options.objects);
436437
437 const term = child.spawnAndWait() catch |err| {
438 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
439 return error.UnableToSpawnSelf;
440 };
441 switch (term) {
442 .Exited => |code| {
443 if (code != 0) {
444 // TODO https://github.com/ziglang/zig/issues/6342
445 std.process.exit(1);
446 }
447 },
448 else => std.process.abort(),
438 for (comp.c_object_table.items()) |entry| {
439 try argv.append(entry.key.status.success.object_path);
440 }
441 if (module_obj_path) |p| {
442 try argv.append(p);
449443 }
450 } else {
451 child.stdin_behavior = .Ignore;
452 child.stdout_behavior = .Ignore;
453 child.stderr_behavior = .Pipe;
454
455 try child.spawn();
456444
457 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
445 if (self.base.options.output_mode != .Obj and
446 !self.base.options.skip_linker_dependencies and
447 !self.base.options.link_libc)
448 {
449 try argv.append(comp.libc_static_lib.?.full_object_path);
450 }
458451
459 const term = child.wait() catch |err| {
460 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
461 return error.UnableToSpawnSelf;
462 };
452 if (compiler_rt_path) |p| {
453 try argv.append(p);
454 }
463455
464 switch (term) {
465 .Exited => |code| {
466 if (code != 0) {
467 // TODO parse this output and surface with the Compilation API rather than
468 // directly outputting to stderr here.
469 std.debug.print("{s}", .{stderr});
470 return error.LLDReportedFailure;
471 }
472 },
473 else => {
474 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
475 return error.LLDCrashed;
476 },
456 if (self.base.options.verbose_link) {
457 // Skip over our own name so that the LLD linker name is the first argv item.
458 Compilation.dump_argv(argv.items[1..]);
477459 }
478460
479 if (stderr.len != 0) {
480 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
461 // Sadly, we must run LLD as a child process because it does not behave
462 // properly as a library.
463 const child = try std.ChildProcess.init(argv.items, arena);
464 defer child.deinit();
465
466 if (comp.clang_passthrough_mode) {
467 child.stdin_behavior = .Inherit;
468 child.stdout_behavior = .Inherit;
469 child.stderr_behavior = .Inherit;
470
471 const term = child.spawnAndWait() catch |err| {
472 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
473 return error.UnableToSpawnSelf;
474 };
475 switch (term) {
476 .Exited => |code| {
477 if (code != 0) {
478 // TODO https://github.com/ziglang/zig/issues/6342
479 std.process.exit(1);
480 }
481 },
482 else => std.process.abort(),
483 }
484 } else {
485 child.stdin_behavior = .Ignore;
486 child.stdout_behavior = .Ignore;
487 child.stderr_behavior = .Pipe;
488
489 try child.spawn();
490
491 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
492
493 const term = child.wait() catch |err| {
494 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
495 return error.UnableToSpawnSelf;
496 };
497
498 switch (term) {
499 .Exited => |code| {
500 if (code != 0) {
501 // TODO parse this output and surface with the Compilation API rather than
502 // directly outputting to stderr here.
503 std.debug.print("{s}", .{stderr});
504 return error.LLDReportedFailure;
505 }
506 },
507 else => {
508 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
509 return error.LLDCrashed;
510 },
511 }
512
513 if (stderr.len != 0) {
514 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
515 }
481516 }
482517 }
483518
src/main.zig+12
......@@ -287,6 +287,8 @@ const usage_build_generic =
287287 \\ -fno-PIC Force-disable Position Independent Code
288288 \\ -fPIE Force-enable Position Independent Executable
289289 \\ -fno-PIE Force-disable Position Independent Executable
290 \\ -flto Force-enable Link Time Optimization (requires LLVM extensions)
291 \\ -fno-lto Force-disable Link Time Optimization
290292 \\ -fstack-check Enable stack probing in unsafe builds
291293 \\ -fno-stack-check Disable stack probing in safe builds
292294 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
......@@ -511,6 +513,7 @@ fn buildOutputType(
511513 var enable_cache: ?bool = null;
512514 var want_pic: ?bool = null;
513515 var want_pie: ?bool = null;
516 var want_lto: ?bool = null;
514517 var want_sanitize_c: ?bool = null;
515518 var want_stack_check: ?bool = null;
516519 var want_red_zone: ?bool = null;
......@@ -852,6 +855,10 @@ fn buildOutputType(
852855 want_pie = true;
853856 } else if (mem.eql(u8, arg, "-fno-PIE")) {
854857 want_pie = false;
858 } else if (mem.eql(u8, arg, "-flto")) {
859 want_lto = true;
860 } else if (mem.eql(u8, arg, "-fno-lto")) {
861 want_lto = false;
855862 } else if (mem.eql(u8, arg, "-fstack-check")) {
856863 want_stack_check = true;
857864 } else if (mem.eql(u8, arg, "-fno-stack-check")) {
......@@ -1085,6 +1092,8 @@ fn buildOutputType(
10851092 .no_pic => want_pic = false,
10861093 .pie => want_pie = true,
10871094 .no_pie => want_pie = false,
1095 .lto => want_lto = true,
1096 .no_lto => want_lto = false,
10881097 .red_zone => want_red_zone = true,
10891098 .no_red_zone => want_red_zone = false,
10901099 .nostdlib => ensure_libc_on_non_freestanding = false,
......@@ -1771,6 +1780,7 @@ fn buildOutputType(
17711780 .link_libcpp = link_libcpp,
17721781 .want_pic = want_pic,
17731782 .want_pie = want_pie,
1783 .want_lto = want_lto,
17741784 .want_sanitize_c = want_sanitize_c,
17751785 .want_stack_check = want_stack_check,
17761786 .want_red_zone = want_red_zone,
......@@ -2952,6 +2962,8 @@ pub const ClangArgIterator = struct {
29522962 no_pic,
29532963 pie,
29542964 no_pie,
2965 lto,
2966 no_lto,
29552967 nostdlib,
29562968 nostdlib_cpp,
29572969 shared,
src/stage1.zig+1
......@@ -109,6 +109,7 @@ pub const Module = extern struct {
109109 err_color: ErrColor,
110110 pic: bool,
111111 pie: bool,
112 lto: bool,
112113 link_libc: bool,
113114 link_libcpp: bool,
114115 strip: bool,
src/stage1/all_types.hpp+1
......@@ -2192,6 +2192,7 @@ struct CodeGen {
21922192 bool is_single_threaded;
21932193 bool have_pic;
21942194 bool have_pie;
2195 bool have_lto;
21952196 bool link_mode_dynamic;
21962197 bool dll_export_fns;
21972198 bool have_stack_probing;
src/stage1/codegen.cpp+6-4
......@@ -8449,8 +8449,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
84498449 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
84508450 // pipeline multiple times if this is requested.
84518451 if (asm_filename != nullptr && bin_filename != nullptr) {
8452 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,
8453 is_small, g->enable_time_report, g->tsan_enabled, nullptr, bin_filename, llvm_ir_filename))
8452 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
8453 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
8454 g->have_lto, nullptr, bin_filename, llvm_ir_filename))
84548455 {
84558456 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
84568457 exit(1);
......@@ -8459,8 +8460,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
84598460 llvm_ir_filename = nullptr;
84608461 }
84618462
8462 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,
8463 is_small, g->enable_time_report, g->tsan_enabled, asm_filename, bin_filename, llvm_ir_filename))
8463 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
8464 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
8465 g->have_lto, asm_filename, bin_filename, llvm_ir_filename))
84648466 {
84658467 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
84668468 exit(1);
src/stage1/stage1.cpp+1
......@@ -90,6 +90,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
9090 g->dll_export_fns = stage1->dll_export_fns;
9191 g->have_pic = stage1->pic;
9292 g->have_pie = stage1->pie;
93 g->have_lto = stage1->lto;
9394 g->have_stack_probing = stage1->enable_stack_probing;
9495 g->red_zone = stage1->red_zone;
9596 g->is_single_threaded = stage1->is_single_threaded;
src/stage1/stage1.h+1
......@@ -178,6 +178,7 @@ struct ZigStage1 {
178178
179179 bool pic;
180180 bool pie;
181 bool lto;
181182 bool link_libc;
182183 bool link_libcpp;
183184 bool strip;
src/zig_llvm.cpp+7-3
......@@ -22,6 +22,7 @@
2222
2323#include <llvm/Analysis/TargetLibraryInfo.h>
2424#include <llvm/Analysis/TargetTransformInfo.h>
25#include <llvm/Bitcode/BitcodeWriter.h>
2526#include <llvm/IR/DIBuilder.h>
2627#include <llvm/IR/DiagnosticInfo.h>
2728#include <llvm/IR/IRBuilder.h>
......@@ -184,7 +185,7 @@ unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD) {
184185
185186bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
186187 char **error_message, bool is_debug,
187 bool is_small, bool time_report, bool tsan,
188 bool is_small, bool time_report, bool tsan, bool lto,
188189 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
189190{
190191 TimePassesIsEnabled = time_report;
......@@ -234,7 +235,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
234235 PMBuilder->VerifyInput = assertions_on;
235236 PMBuilder->VerifyOutput = assertions_on;
236237 PMBuilder->MergeFunctions = !is_debug;
237 PMBuilder->PrepareForLTO = false;
238 PMBuilder->PrepareForLTO = lto;
238239 PMBuilder->PrepareForThinLTO = false;
239240 PMBuilder->PerformThinLTO = false;
240241
......@@ -272,7 +273,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
272273 PMBuilder->populateModulePassManager(MPM);
273274
274275 // Set output passes.
275 if (dest_bin) {
276 if (dest_bin && !lto) {
276277 if (target_machine->addPassesToEmitFile(MPM, *dest_bin, nullptr, CGFT_ObjectFile)) {
277278 *error_message = strdup("TargetMachine can't emit an object file");
278279 return true;
......@@ -299,6 +300,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
299300 return true;
300301 }
301302 }
303 if (dest_bin && lto) {
304 WriteBitcodeToFile(*module, *dest_bin);
305 }
302306
303307 if (time_report) {
304308 TimerGroup::printAll(errs());
src/zig_llvm.h+1-1
......@@ -48,7 +48,7 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
4848
4949ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
5050 char **error_message, bool is_debug,
51 bool is_small, bool time_report, bool tsan,
51 bool is_small, bool time_report, bool tsan, bool lto,
5252 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);
5353
5454
tools/update_clang_options.zig+8
......@@ -62,6 +62,14 @@ const known_options = [_]KnownOpt{
6262 .name = "fno-PIE",
6363 .ident = "no_pie",
6464 },
65 .{
66 .name = "flto",
67 .ident = "lto",
68 },
69 .{
70 .name = "fno-lto",
71 .ident = "no_lto",
72 },
6573 .{
6674 .name = "nolibc",
6775 .ident = "nostdlib",