authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-24 11:09:48-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-24 11:09:48-08:00
log15278b7f4b74e659fd968571cb9b96929da3d82c
tree52afdf98a47470a20067188fcd7b6a85dff61675
parent843d91e75d166ac41d7c9b27b86b236f14865e31
parent0d4b6ac7417d1094ac972981b0241444ce2380ba
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7856 from ziglang/lto

add LTO support

19 files changed, 577 insertions(+), 400 deletions(-)

src/Compilation.zig+37
...@@ -444,6 +444,7 @@ pub const InitOptions = struct {...@@ -444,6 +444,7 @@ pub const InitOptions = struct {
444 want_valgrind: ?bool = null,444 want_valgrind: ?bool = null,
445 want_tsan: ?bool = null,445 want_tsan: ?bool = null,
446 want_compiler_rt: ?bool = null,446 want_compiler_rt: ?bool = null,
447 want_lto: ?bool = null,
447 use_llvm: ?bool = null,448 use_llvm: ?bool = null,
448 use_lld: ?bool = null,449 use_lld: ?bool = null,
449 use_clang: ?bool = null,450 use_clang: ?bool = null,
...@@ -602,6 +603,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -602,6 +603,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
602 if (ofmt == .c)603 if (ofmt == .c)
603 break :blk false;604 break :blk false;
604605
606 if (options.want_lto) |lto| {
607 if (lto) {
608 break :blk true;
609 }
610 }
611
605 // Our linker can't handle objects or most advanced options yet.612 // Our linker can't handle objects or most advanced options yet.
606 if (options.link_objects.len != 0 or613 if (options.link_objects.len != 0 or
607 options.c_source_files.len != 0 or614 options.c_source_files.len != 0 or
...@@ -647,6 +654,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -647,6 +654,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
647 break :outer opts;654 break :outer opts;
648 } else .{};655 } 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
650 const tsan = options.want_tsan orelse false;677 const tsan = options.want_tsan orelse false;
651678
652 const link_libc = options.link_libc or target_util.osRequiresLibC(options.target) or tsan;679 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 {...@@ -821,6 +848,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
821 cache.hash.add(ofmt);848 cache.hash.add(ofmt);
822 cache.hash.add(pic);849 cache.hash.add(pic);
823 cache.hash.add(pie);850 cache.hash.add(pie);
851 cache.hash.add(lto);
824 cache.hash.add(tsan);852 cache.hash.add(tsan);
825 cache.hash.add(stack_check);853 cache.hash.add(stack_check);
826 cache.hash.add(red_zone);854 cache.hash.add(red_zone);
...@@ -1022,6 +1050,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1022,6 +1050,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1022 .libc_installation = libc_dirs.libc_installation,1050 .libc_installation = libc_dirs.libc_installation,
1023 .pic = pic,1051 .pic = pic,
1024 .pie = pie,1052 .pie = pie,
1053 .lto = lto,
1025 .valgrind = valgrind,1054 .valgrind = valgrind,
1026 .tsan = tsan,1055 .tsan = tsan,
1027 .stack_check = stack_check,1056 .stack_check = stack_check,
...@@ -2233,6 +2262,9 @@ pub fn addCCArgs(...@@ -2233,6 +2262,9 @@ pub fn addCCArgs(
2233 "-nostdinc",2262 "-nostdinc",
2234 "-fno-spell-checking",2263 "-fno-spell-checking",
2235 });2264 });
2265 if (comp.bin_file.options.lto) {
2266 try argv.append("-flto");
2267 }
22362268
2237 // According to Rich Felker libc headers are supposed to go before C language headers.2269 // According to Rich Felker libc headers are supposed to go before C language headers.
2238 // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics2270 // 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...@@ -3255,6 +3287,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3255 .err_color = @enumToInt(comp.color),3287 .err_color = @enumToInt(comp.color),
3256 .pic = comp.bin_file.options.pic,3288 .pic = comp.bin_file.options.pic,
3257 .pie = comp.bin_file.options.pie,3289 .pie = comp.bin_file.options.pie,
3290 .lto = comp.bin_file.options.lto,
3258 .link_libc = comp.bin_file.options.link_libc,3291 .link_libc = comp.bin_file.options.link_libc,
3259 .link_libcpp = comp.bin_file.options.link_libcpp,3292 .link_libcpp = comp.bin_file.options.link_libcpp,
3260 .strip = comp.bin_file.options.strip,3293 .strip = comp.bin_file.options.strip,
...@@ -3415,6 +3448,10 @@ pub fn build_crt_file(...@@ -3415,6 +3448,10 @@ pub fn build_crt_file(
3415 .want_tsan = false,3448 .want_tsan = false,
3416 .want_pic = comp.bin_file.options.pic,3449 .want_pic = comp.bin_file.options.pic,
3417 .want_pie = comp.bin_file.options.pie,3450 .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 },
3418 .emit_h = null,3455 .emit_h = null,
3419 .strip = comp.compilerRtStrip(),3456 .strip = comp.compilerRtStrip(),
3420 .is_native_os = comp.bin_file.options.is_native_os,3457 .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"),...@@ -2732,7 +2732,14 @@ flagpd1("fkeep-static-consts"),
2732flagpd1("flat_namespace"),2732flagpd1("flat_namespace"),
2733flagpd1("flax-vector-conversions"),2733flagpd1("flax-vector-conversions"),
2734flagpd1("flimit-debug-info"),2734flagpd1("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},
2736flagpd1("flto-unit"),2743flagpd1("flto-unit"),
2737flagpd1("flto-visibility-public-std"),2744flagpd1("flto-visibility-public-std"),
2738sepd1("fmacro-backtrace-limit"),2745sepd1("fmacro-backtrace-limit"),
...@@ -2942,7 +2949,14 @@ flagpd1("fno-jump-tables"),...@@ -2942,7 +2949,14 @@ flagpd1("fno-jump-tables"),
2942flagpd1("fno-keep-static-consts"),2949flagpd1("fno-keep-static-consts"),
2943flagpd1("fno-lax-vector-conversions"),2950flagpd1("fno-lax-vector-conversions"),
2944flagpd1("fno-limit-debug-info"),2951flagpd1("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},
2946flagpd1("fno-lto-unit"),2960flagpd1("fno-lto-unit"),
2947flagpd1("fno-math-builtin"),2961flagpd1("fno-math-builtin"),
2948flagpd1("fno-math-errno"),2962flagpd1("fno-math-errno"),
...@@ -5638,7 +5652,14 @@ jspd1("Ttext"),...@@ -5638,7 +5652,14 @@ jspd1("Ttext"),
5638 .pd2 = true,5652 .pd2 = true,
5639 .psl = false,5653 .psl = false,
5640},5654},
5641joinpd1("flto="),5655.{
5656 .name = "flto=",
5657 .syntax = .joined,
5658 .zig_equivalent = .lto,
5659 .pd1 = true,
5660 .pd2 = false,
5661 .psl = false,
5662},
5642joinpd1("gcoff"),5663joinpd1("gcoff"),
5643joinpd1("mabi="),5664joinpd1("mabi="),
5644joinpd1("mabs="),5665joinpd1("mabs="),
src/codegen/llvm/bindings.zig+7-1
...@@ -243,7 +243,13 @@ pub const TargetMachine = opaque {...@@ -243,7 +243,13 @@ pub const TargetMachine = opaque {
243 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;243 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
244244
245 pub const emitToFile = LLVMTargetMachineEmitToFile;245 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;
247};253};
248254
249pub const CodeMode = extern enum {255pub const CodeMode = extern enum {
src/link.zig+1
...@@ -74,6 +74,7 @@ pub const Options = struct {...@@ -74,6 +74,7 @@ pub const Options = struct {
74 is_native_abi: bool,74 is_native_abi: bool,
75 pic: bool,75 pic: bool,
76 pie: bool,76 pie: bool,
77 lto: bool,
77 valgrind: bool,78 valgrind: bool,
78 tsan: bool,79 tsan: bool,
79 stack_check: bool,80 stack_check: bool,
src/link/Coff.zig+7
...@@ -945,6 +945,13 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -945,6 +945,13 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
945 if (!self.base.options.strip) {945 if (!self.base.options.strip) {
946 try argv.append("-DEBUG");946 try argv.append("-DEBUG");
947 }947 }
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 }
948 if (self.base.options.output_mode == .Exe) {955 if (self.base.options.output_mode == .Exe) {
949 const stack_size = self.base.options.stack_size_override orelse 16777216;956 const stack_size = self.base.options.stack_size_override orelse 16777216;
950 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));957 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 {...@@ -1384,351 +1384,385 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1384 };1384 };
1385 }1385 }
13861386
1387 // Create an LLD command line and invoke it.1387 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
1388 var argv = std.ArrayList([]const u8).init(self.base.allocator);1388 if (self.base.options.output_mode == .Obj and self.base.options.lto) {
1389 defer argv.deinit();1389 // In this case we must do a simple file copy
1390 // We will invoke ourselves as a child process to gain access to LLD.1390 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1391 // This is necessary because LLD does not behave properly as a library -1391 // build-obj. See also the corresponding TODO in linkAsArchive.
1392 // it calls exit() and does not reset all global data between invocations.1392 const the_object_path = blk: {
1393 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });1393 if (self.base.options.objects.len != 0)
1394 if (is_obj) {1394 break :blk self.base.options.objects[0];
1395 try argv.append("-r");1395
1396 }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) {1424 try argv.append("-error-limit=0");
1401 try argv.append("-z");
1402 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
1403 }
14041425
1405 if (self.base.options.image_base_override) |image_base| {1426 if (self.base.options.lto) {
1406 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));1427 switch (self.base.options.optimize_mode) {
1407 }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| {1434 if (self.base.options.output_mode == .Exe) {
1410 try argv.append("-T");1435 try argv.append("-z");
1411 try argv.append(linker_script);1436 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
1412 }1437 }
14131438
1414 if (gc_sections) {1439 if (self.base.options.image_base_override) |image_base| {
1415 try argv.append("--gc-sections");1440 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
1416 }1441 }
14171442
1418 if (self.base.options.eh_frame_hdr) {1443 if (self.base.options.linker_script) |linker_script| {
1419 try argv.append("--eh-frame-hdr");1444 try argv.append("-T");
1420 }1445 try argv.append(linker_script);
1446 }
14211447
1422 if (self.base.options.emit_relocs) {1448 if (gc_sections) {
1423 try argv.append("--emit-relocs");1449 try argv.append("--gc-sections");
1424 }1450 }
14251451
1426 if (self.base.options.rdynamic) {1452 if (self.base.options.eh_frame_hdr) {
1427 try argv.append("--export-dynamic");1453 try argv.append("--eh-frame-hdr");
1428 }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) {1460 if (self.base.options.rdynamic) {
1433 try argv.append("-z");1461 try argv.append("--export-dynamic");
1434 try argv.append("nodelete");1462 }
1435 }
1436 if (self.base.options.z_defs) {
1437 try argv.append("-z");
1438 try argv.append("defs");
1439 }
14401463
1441 if (getLDMOption(target)) |ldm| {1464 try argv.appendSlice(self.base.options.extra_lld_args);
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 }
14501465
1451 if (self.base.options.link_mode == .Static) {1466 if (self.base.options.z_nodelete) {
1452 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {1467 try argv.append("-z");
1453 try argv.append("-Bstatic");1468 try argv.append("nodelete");
1454 } else {1469 }
1455 try argv.append("-static");1470 if (self.base.options.z_defs) {
1471 try argv.append("-z");
1472 try argv.append("defs");
1456 }1473 }
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) {1475 if (getLDMOption(target)) |ldm| {
1462 try argv.append("-pie");1476 // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
1463 }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});1485 if (self.base.options.link_mode == .Static) {
1466 try argv.append("-o");1486 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
1467 try argv.append(full_out_path);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) {1495 if (self.base.options.pie and self.base.options.output_mode == .Exe) {
1470 const crt1o: []const u8 = o: {1496 try argv.append("-pie");
1471 if (target.os.tag == .netbsd) {1497 }
1472 break :o "crt0.o";1498
1473 } else if (target.os.tag == .openbsd) {1499 try argv.append("-o");
1474 if (self.base.options.link_mode == .Static) {1500 try argv.append(full_out_path);
1475 break :o "rcrt0.o";1501
1476 } else {1502 if (link_in_crt) {
1503 const crt1o: []const u8 = o: {
1504 if (target.os.tag == .netbsd) {
1477 break :o "crt0.o";1505 break :o "crt0.o";
1478 }1506 } else if (target.os.tag == .openbsd) {
1479 } else if (target.isAndroid()) {1507 if (self.base.options.link_mode == .Static) {
1480 if (self.base.options.link_mode == .Dynamic) {1508 break :o "rcrt0.o";
1481 break :o "crtbegin_dynamic.o";1509 } else {
1482 } else {1510 break :o "crt0.o";
1483 break :o "crtbegin_static.o";1511 }
1484 }1512 } else if (target.isAndroid()) {
1485 } else if (self.base.options.link_mode == .Static) {1513 if (self.base.options.link_mode == .Dynamic) {
1486 if (self.base.options.pie) {1514 break :o "crtbegin_dynamic.o";
1487 break :o "rcrt1.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 }
1488 } else {1524 } else {
1489 break :o "crt1.o";1525 break :o "Scrt1.o";
1490 }1526 }
1491 } else {1527 };
1492 break :o "Scrt1.o";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"));
1493 }1534 }
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"));
1501 }1535 }
1502 }
15031536
1504 // rpaths1537 // rpaths
1505 var rpath_table = std.StringHashMap(void).init(self.base.allocator);1538 var rpath_table = std.StringHashMap(void).init(self.base.allocator);
1506 defer rpath_table.deinit();1539 defer rpath_table.deinit();
1507 for (self.base.options.rpath_list) |rpath| {1540 for (self.base.options.rpath_list) |rpath| {
1508 if ((try rpath_table.fetchPut(rpath, {})) == null) {1541 if ((try rpath_table.fetchPut(rpath, {})) == null) {
1509 try argv.append("-rpath");1542 try argv.append("-rpath");
1510 try argv.append(rpath);1543 try argv.append(rpath);
1544 }
1511 }1545 }
1512 }1546 if (self.base.options.each_lib_rpath) {
1513 if (self.base.options.each_lib_rpath) {1547 var test_path = std.ArrayList(u8).init(self.base.allocator);
1514 var test_path = std.ArrayList(u8).init(self.base.allocator);1548 defer test_path.deinit();
1515 defer test_path.deinit();1549 for (self.base.options.lib_dirs) |lib_dir_path| {
1516 for (self.base.options.lib_dirs) |lib_dir_path| {1550 for (self.base.options.system_libs.items()) |entry| {
1517 for (self.base.options.system_libs.items()) |entry| {1551 const link_lib = entry.key;
1518 const link_lib = entry.key;1552 test_path.shrinkRetainingCapacity(0);
1519 test_path.shrinkRetainingCapacity(0);1553 const sep = fs.path.sep_str;
1520 const sep = fs.path.sep_str;1554 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib });
1521 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) {
1522 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {1556 error.FileNotFound => continue,
1523 error.FileNotFound => continue,1557 else => |e| return e,
1524 else => |e| return e,1558 };
1525 };1559 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
1526 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {1560 try argv.append("-rpath");
1527 try argv.append("-rpath");1561 try argv.append(lib_dir_path);
1528 try argv.append(lib_dir_path);1562 }
1529 }1563 }
1530 }1564 }
1531 }1565 }
1532 }
15331566
1534 for (self.base.options.lib_dirs) |lib_dir| {1567 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| {
1541 try argv.append("-L");1568 try argv.append("-L");
1542 try argv.append(libc_installation.crt_dir.?);1569 try argv.append(lib_dir);
1543 }1570 }
15441571
1545 if (have_dynamic_linker) {1572 if (self.base.options.link_libc) {
1546 if (self.base.options.dynamic_linker) |dynamic_linker| {1573 if (self.base.options.libc_installation) |libc_installation| {
1547 try argv.append("-dynamic-linker");1574 try argv.append("-L");
1548 try argv.append(dynamic_linker);1575 try argv.append(libc_installation.crt_dir.?);
1549 }1576 }
1550 }
1551 }
15521577
1553 if (is_dyn_lib) {1578 if (have_dynamic_linker) {
1554 if (self.base.options.soname) |soname| {1579 if (self.base.options.dynamic_linker) |dynamic_linker| {
1555 try argv.append("-soname");1580 try argv.append("-dynamic-linker");
1556 try argv.append(soname);1581 try argv.append(dynamic_linker);
1557 }1582 }
1558 if (self.base.options.version_script) |version_script| {1583 }
1559 try argv.append("-version-script");
1560 try argv.append(version_script);
1561 }1584 }
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| {1586 if (is_dyn_lib) {
1568 try argv.append(entry.key.status.success.object_path);1587 if (self.base.options.soname) |soname| {
1569 }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| {1597 // Positional arguments to the linker such as object files.
1572 try argv.append(p);1598 try argv.appendSlice(self.base.options.objects);
1573 }
15741599
1575 // TSAN1600 for (comp.c_object_table.items()) |entry| {
1576 if (self.base.options.tsan) {1601 try argv.append(entry.key.status.success.object_path);
1577 try argv.append(comp.tsan_static_lib.?.full_object_path);1602 }
1578 }
15791603
1580 // libc1604 if (module_obj_path) |p| {
1581 // TODO: enable when stage2 can build c.zig1605 try argv.append(p);
1582 if (is_exe_or_dyn_lib and1606 }
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 }
15891607
1590 // compiler-rt1608 // TSAN
1591 if (compiler_rt_path) |p| {1609 if (self.base.options.tsan) {
1592 try argv.append(p);1610 try argv.append(comp.tsan_static_lib.?.full_object_path);
1593 }1611 }
15941612
1595 // Shared libraries.1613 // libc
1596 if (is_exe_or_dyn_lib) {1614 // TODO: enable when stage2 can build c.zig
1597 const system_libs = self.base.options.system_libs.items();1615 if (is_exe_or_dyn_lib and
1598 try argv.ensureCapacity(argv.items.len + system_libs.len);1616 !self.base.options.skip_linker_dependencies and
1599 for (system_libs) |entry| {1617 !self.base.options.link_libc and
1600 const link_lib = entry.key;1618 build_options.is_stage1)
1601 // By this time, we depend on these libs being dynamically linked libraries and not static libraries1619 {
1602 // (the check for that needs to be earlier), but they could be full paths to .so files, in which1620 try argv.append(comp.libc_static_lib.?.full_object_path);
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);
1607 }1621 }
16081622
1609 // libc++ dep1623 // compiler-rt
1610 if (self.base.options.link_libcpp) {1624 if (compiler_rt_path) |p| {
1611 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);1625 try argv.append(p);
1612 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1613 }1626 }
16141627
1615 // libc dep1628 // Shared libraries.
1616 if (self.base.options.link_libc) {1629 if (is_exe_or_dyn_lib) {
1617 if (self.base.options.libc_installation != null) {1630 const system_libs = self.base.options.system_libs.items();
1618 if (self.base.options.link_mode == .Static) {1631 try argv.ensureCapacity(argv.items.len + system_libs.len);
1619 try argv.append("--start-group");1632 for (system_libs) |entry| {
1620 try argv.append("-lc");1633 const link_lib = entry.key;
1621 try argv.append("-lm");1634 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
1622 try argv.append("--end-group");1635 // (the check for that needs to be earlier), but they could be full paths to .so files, in which
1623 } else {1636 // case we want to avoid prepending "-l".
1624 try argv.append("-lc");1637 const ext = Compilation.classifyFileExt(link_lib);
1625 try argv.append("-lm");1638 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
1626 }1639 argv.appendAssumeCapacity(arg);
1640 }
16271641
1628 if (target.os.tag == .freebsd or target.os.tag == .netbsd or target.os.tag == .openbsd) {1642 // libc++ dep
1629 try argv.append("-lpthread");1643 if (self.base.options.link_libcpp) {
1630 }1644 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1631 } else if (target.isGnuLibC()) {1645 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1632 try argv.append(comp.libunwind_static_lib.?.full_object_path);1646 }
1633 for (glibc.libs) |lib| {1647
1634 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{1648 // libc dep
1635 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,1649 if (self.base.options.link_libc) {
1636 });1650 if (self.base.options.libc_installation != null) {
1637 try argv.append(lib_path);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.
1638 }1683 }
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.
1650 }1684 }
1651 }1685 }
1652 }
16531686
1654 // crt end1687 // crt end
1655 if (link_in_crt) {1688 if (link_in_crt) {
1656 if (target.isAndroid()) {1689 if (target.isAndroid()) {
1657 try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));1690 try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
1658 } else if (target.os.tag == .openbsd) {1691 } else if (target.os.tag == .openbsd) {
1659 try argv.append(try comp.get_libc_crt_file(arena, "crtend.o"));1692 try argv.append(try comp.get_libc_crt_file(arena, "crtend.o"));
1660 } else if (target_util.libc_needs_crti_crtn(target)) {1693 } else if (target_util.libc_needs_crti_crtn(target)) {
1661 try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));1694 try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
1695 }
1662 }1696 }
1663 }
16641697
1665 if (allow_shlib_undefined) {1698 if (allow_shlib_undefined) {
1666 try argv.append("--allow-shlib-undefined");1699 try argv.append("--allow-shlib-undefined");
1667 }1700 }
16681701
1669 if (self.base.options.bind_global_refs_locally) {1702 if (self.base.options.bind_global_refs_locally) {
1670 try argv.append("-Bsymbolic");1703 try argv.append("-Bsymbolic");
1671 }1704 }
16721705
1673 if (self.base.options.verbose_link) {1706 if (self.base.options.verbose_link) {
1674 // Skip over our own name so that the LLD linker name is the first argv item.1707 // Skip over our own name so that the LLD linker name is the first argv item.
1675 Compilation.dump_argv(argv.items[1..]);1708 Compilation.dump_argv(argv.items[1..]);
1676 }1709 }
16771710
1678 // Sadly, we must run LLD as a child process because it does not behave1711 // Sadly, we must run LLD as a child process because it does not behave
1679 // properly as a library.1712 // properly as a library.
1680 const child = try std.ChildProcess.init(argv.items, arena);1713 const child = try std.ChildProcess.init(argv.items, arena);
1681 defer child.deinit();1714 defer child.deinit();
16821715
1683 if (comp.clang_passthrough_mode) {1716 if (comp.clang_passthrough_mode) {
1684 child.stdin_behavior = .Inherit;1717 child.stdin_behavior = .Inherit;
1685 child.stdout_behavior = .Inherit;1718 child.stdout_behavior = .Inherit;
1686 child.stderr_behavior = .Inherit;1719 child.stderr_behavior = .Inherit;
16871720
1688 const term = child.spawnAndWait() catch |err| {1721 const term = child.spawnAndWait() catch |err| {
1689 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });1722 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1690 return error.UnableToSpawnSelf;1723 return error.UnableToSpawnSelf;
1691 };1724 };
1692 switch (term) {1725 switch (term) {
1693 .Exited => |code| {1726 .Exited => |code| {
1694 if (code != 0) {1727 if (code != 0) {
1695 // TODO https://github.com/ziglang/zig/issues/63421728 // TODO https://github.com/ziglang/zig/issues/6342
1696 std.process.exit(1);1729 std.process.exit(1);
1697 }1730 }
1698 },1731 },
1699 else => std.process.abort(),1732 else => std.process.abort(),
1700 }1733 }
1701 } else {1734 } else {
1702 child.stdin_behavior = .Ignore;1735 child.stdin_behavior = .Ignore;
1703 child.stdout_behavior = .Ignore;1736 child.stdout_behavior = .Ignore;
1704 child.stderr_behavior = .Pipe;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| {1743 const term = child.wait() catch |err| {
1711 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });1744 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1712 return error.UnableToSpawnSelf;1745 return error.UnableToSpawnSelf;
1713 };1746 };
17141747
1715 switch (term) {1748 switch (term) {
1716 .Exited => |code| {1749 .Exited => |code| {
1717 if (code != 0) {1750 if (code != 0) {
1718 // TODO parse this output and surface with the Compilation API rather than1751 // TODO parse this output and surface with the Compilation API rather than
1719 // directly outputting to stderr here.1752 // directly outputting to stderr here.
1720 std.debug.print("{s}", .{stderr});1753 std.debug.print("{s}", .{stderr});
1721 return error.LLDReportedFailure;1754 return error.LLDReportedFailure;
1722 }1755 }
1723 },1756 },
1724 else => {1757 else => {
1725 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });1758 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1726 return error.LLDCrashed;1759 return error.LLDCrashed;
1727 },1760 },
1728 }1761 }
17291762
1730 if (stderr.len != 0) {1763 if (stderr.len != 0) {
1731 log.warn("unexpected LLD stderr:\n{s}", .{stderr});1764 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1765 }
1732 }1766 }
1733 }1767 }
17341768
src/link/MachO.zig+7
...@@ -620,6 +620,13 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -620,6 +620,13 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
620 try argv.append("0");620 try argv.append("0");
621 }621 }
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 }
623 try argv.append("-demangle");630 try argv.append("-demangle");
624631
625 if (self.base.options.rdynamic and !self.base.options.system_linker_hack) {632 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 {...@@ -362,122 +362,157 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
362 };362 };
363 }363 }
364364
365 const is_obj = self.base.options.output_mode == .Obj;365 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
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");
379366
380 if (self.base.options.output_mode == .Exe) {367 if (self.base.options.output_mode == .Obj) {
381 // Increase the default stack size to a more reasonable value of 1MB instead of368 // LLD's WASM driver does not support the equvialent of `-r` so we do a simple file copy
382 // the default of 1 Wasm page being 64KB, unless overriden by the user.369 // here. TODO: think carefully about how we can avoid this redundant operation when doing
383 try argv.append("-z");370 // build-obj. See also the corresponding TODO in linkAsArchive.
384 const stack_size = self.base.options.stack_size_override orelse 1048576;371 const the_object_path = blk: {
385 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});372 if (self.base.options.objects.len != 0)
386 try argv.append(arg);373 break :blk self.base.options.objects[0];
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 });
400374
401 // Positional arguments to the linker such as object files.375 if (comp.c_object_table.count() != 0)
402 try argv.appendSlice(self.base.options.objects);376 break :blk comp.c_object_table.items()[0].key.status.success.object_path;
403377
404 for (comp.c_object_table.items()) |entry| {378 if (module_obj_path) |p|
405 try argv.append(entry.key.status.success.object_path);379 break :blk p;
406 }
407 if (module_obj_path) |p| {
408 try argv.append(p);
409 }
410380
411 if (self.base.options.output_mode != .Obj and381 // TODO I think this is unreachable. Audit this situation when solving the above TODO
412 !self.base.options.skip_linker_dependencies and382 // regarding eliding redundant object -> object transformations.
413 !self.base.options.link_libc)383 return error.NoObjectsToLink;
414 {384 };
415 try argv.append(comp.libc_static_lib.?.full_object_path);385 // This can happen when using --enable-cache and using the stage1 backend. In this case
416 }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| {404 try argv.append("-error-limit=0");
419 try argv.append(p);
420 }
421405
422 if (self.base.options.verbose_link) {406 if (self.base.options.lto) {
423 // Skip over our own name so that the LLD linker name is the first argv item.407 switch (self.base.options.optimize_mode) {
424 Compilation.dump_argv(argv.items[1..]);408 .Debug => {},
425 }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 behave414 if (self.base.options.output_mode == .Exe) {
428 // properly as a library.415 // Increase the default stack size to a more reasonable value of 1MB instead of
429 const child = try std.ChildProcess.init(argv.items, arena);416 // the default of 1 Wasm page being 64KB, unless overriden by the user.
430 defer child.deinit();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) {435 // Positional arguments to the linker such as object files.
433 child.stdin_behavior = .Inherit;436 try argv.appendSlice(self.base.options.objects);
434 child.stdout_behavior = .Inherit;
435 child.stderr_behavior = .Inherit;
436437
437 const term = child.spawnAndWait() catch |err| {438 for (comp.c_object_table.items()) |entry| {
438 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });439 try argv.append(entry.key.status.success.object_path);
439 return error.UnableToSpawnSelf;440 }
440 };441 if (module_obj_path) |p| {
441 switch (term) {442 try argv.append(p);
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(),
449 }443 }
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| {452 if (compiler_rt_path) |p| {
460 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });453 try argv.append(p);
461 return error.UnableToSpawnSelf;454 }
462 };
463455
464 switch (term) {456 if (self.base.options.verbose_link) {
465 .Exited => |code| {457 // Skip over our own name so that the LLD linker name is the first argv item.
466 if (code != 0) {458 Compilation.dump_argv(argv.items[1..]);
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 },
477 }459 }
478460
479 if (stderr.len != 0) {461 // Sadly, we must run LLD as a child process because it does not behave
480 log.warn("unexpected LLD stderr:\n{s}", .{stderr});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 }
481 }516 }
482 }517 }
483518
src/main.zig+12
...@@ -287,6 +287,8 @@ const usage_build_generic =...@@ -287,6 +287,8 @@ const usage_build_generic =
287 \\ -fno-PIC Force-disable Position Independent Code287 \\ -fno-PIC Force-disable Position Independent Code
288 \\ -fPIE Force-enable Position Independent Executable288 \\ -fPIE Force-enable Position Independent Executable
289 \\ -fno-PIE Force-disable Position Independent Executable289 \\ -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
290 \\ -fstack-check Enable stack probing in unsafe builds292 \\ -fstack-check Enable stack probing in unsafe builds
291 \\ -fno-stack-check Disable stack probing in safe builds293 \\ -fno-stack-check Disable stack probing in safe builds
292 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds294 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
...@@ -511,6 +513,7 @@ fn buildOutputType(...@@ -511,6 +513,7 @@ fn buildOutputType(
511 var enable_cache: ?bool = null;513 var enable_cache: ?bool = null;
512 var want_pic: ?bool = null;514 var want_pic: ?bool = null;
513 var want_pie: ?bool = null;515 var want_pie: ?bool = null;
516 var want_lto: ?bool = null;
514 var want_sanitize_c: ?bool = null;517 var want_sanitize_c: ?bool = null;
515 var want_stack_check: ?bool = null;518 var want_stack_check: ?bool = null;
516 var want_red_zone: ?bool = null;519 var want_red_zone: ?bool = null;
...@@ -852,6 +855,10 @@ fn buildOutputType(...@@ -852,6 +855,10 @@ fn buildOutputType(
852 want_pie = true;855 want_pie = true;
853 } else if (mem.eql(u8, arg, "-fno-PIE")) {856 } else if (mem.eql(u8, arg, "-fno-PIE")) {
854 want_pie = false;857 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;
855 } else if (mem.eql(u8, arg, "-fstack-check")) {862 } else if (mem.eql(u8, arg, "-fstack-check")) {
856 want_stack_check = true;863 want_stack_check = true;
857 } else if (mem.eql(u8, arg, "-fno-stack-check")) {864 } else if (mem.eql(u8, arg, "-fno-stack-check")) {
...@@ -1085,6 +1092,8 @@ fn buildOutputType(...@@ -1085,6 +1092,8 @@ fn buildOutputType(
1085 .no_pic => want_pic = false,1092 .no_pic => want_pic = false,
1086 .pie => want_pie = true,1093 .pie => want_pie = true,
1087 .no_pie => want_pie = false,1094 .no_pie => want_pie = false,
1095 .lto => want_lto = true,
1096 .no_lto => want_lto = false,
1088 .red_zone => want_red_zone = true,1097 .red_zone => want_red_zone = true,
1089 .no_red_zone => want_red_zone = false,1098 .no_red_zone => want_red_zone = false,
1090 .nostdlib => ensure_libc_on_non_freestanding = false,1099 .nostdlib => ensure_libc_on_non_freestanding = false,
...@@ -1771,6 +1780,7 @@ fn buildOutputType(...@@ -1771,6 +1780,7 @@ fn buildOutputType(
1771 .link_libcpp = link_libcpp,1780 .link_libcpp = link_libcpp,
1772 .want_pic = want_pic,1781 .want_pic = want_pic,
1773 .want_pie = want_pie,1782 .want_pie = want_pie,
1783 .want_lto = want_lto,
1774 .want_sanitize_c = want_sanitize_c,1784 .want_sanitize_c = want_sanitize_c,
1775 .want_stack_check = want_stack_check,1785 .want_stack_check = want_stack_check,
1776 .want_red_zone = want_red_zone,1786 .want_red_zone = want_red_zone,
...@@ -2952,6 +2962,8 @@ pub const ClangArgIterator = struct {...@@ -2952,6 +2962,8 @@ pub const ClangArgIterator = struct {
2952 no_pic,2962 no_pic,
2953 pie,2963 pie,
2954 no_pie,2964 no_pie,
2965 lto,
2966 no_lto,
2955 nostdlib,2967 nostdlib,
2956 nostdlib_cpp,2968 nostdlib_cpp,
2957 shared,2969 shared,
src/mingw.zig+1
...@@ -707,6 +707,7 @@ const mingwex_generic_src = [_][]const u8{...@@ -707,6 +707,7 @@ const mingwex_generic_src = [_][]const u8{
707 "math" ++ path.sep_str ++ "fpclassifyf.c",707 "math" ++ path.sep_str ++ "fpclassifyf.c",
708 "math" ++ path.sep_str ++ "fpclassifyl.c",708 "math" ++ path.sep_str ++ "fpclassifyl.c",
709 "math" ++ path.sep_str ++ "frexpf.c",709 "math" ++ path.sep_str ++ "frexpf.c",
710 "math" ++ path.sep_str ++ "frexpl.c",
710 "math" ++ path.sep_str ++ "hypot.c",711 "math" ++ path.sep_str ++ "hypot.c",
711 "math" ++ path.sep_str ++ "hypotf.c",712 "math" ++ path.sep_str ++ "hypotf.c",
712 "math" ++ path.sep_str ++ "hypotl.c",713 "math" ++ path.sep_str ++ "hypotl.c",
src/stage1.zig+1
...@@ -109,6 +109,7 @@ pub const Module = extern struct {...@@ -109,6 +109,7 @@ pub const Module = extern struct {
109 err_color: ErrColor,109 err_color: ErrColor,
110 pic: bool,110 pic: bool,
111 pie: bool,111 pie: bool,
112 lto: bool,
112 link_libc: bool,113 link_libc: bool,
113 link_libcpp: bool,114 link_libcpp: bool,
114 strip: bool,115 strip: bool,
src/stage1/all_types.hpp+1
...@@ -2192,6 +2192,7 @@ struct CodeGen {...@@ -2192,6 +2192,7 @@ struct CodeGen {
2192 bool is_single_threaded;2192 bool is_single_threaded;
2193 bool have_pic;2193 bool have_pic;
2194 bool have_pie;2194 bool have_pie;
2195 bool have_lto;
2195 bool link_mode_dynamic;2196 bool link_mode_dynamic;
2196 bool dll_export_fns;2197 bool dll_export_fns;
2197 bool have_stack_probing;2198 bool have_stack_probing;
src/stage1/codegen.cpp+6-4
...@@ -8449,8 +8449,9 @@ static void zig_llvm_emit_output(CodeGen *g) {...@@ -8449,8 +8449,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
8449 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire8449 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
8450 // pipeline multiple times if this is requested.8450 // pipeline multiple times if this is requested.
8451 if (asm_filename != nullptr && bin_filename != nullptr) {8451 if (asm_filename != nullptr && bin_filename != nullptr) {
8452 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,8452 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
8453 is_small, g->enable_time_report, g->tsan_enabled, nullptr, bin_filename, llvm_ir_filename))8453 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
8454 g->have_lto, nullptr, bin_filename, llvm_ir_filename))
8454 {8455 {
8455 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);8456 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
8456 exit(1);8457 exit(1);
...@@ -8459,8 +8460,9 @@ static void zig_llvm_emit_output(CodeGen *g) {...@@ -8459,8 +8460,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
8459 llvm_ir_filename = nullptr;8460 llvm_ir_filename = nullptr;
8460 }8461 }
84618462
8462 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,8463 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
8463 is_small, g->enable_time_report, g->tsan_enabled, asm_filename, bin_filename, llvm_ir_filename))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))
8464 {8466 {
8465 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);8467 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
8466 exit(1);8468 exit(1);
src/stage1/stage1.cpp+1
...@@ -90,6 +90,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {...@@ -90,6 +90,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
90 g->dll_export_fns = stage1->dll_export_fns;90 g->dll_export_fns = stage1->dll_export_fns;
91 g->have_pic = stage1->pic;91 g->have_pic = stage1->pic;
92 g->have_pie = stage1->pie;92 g->have_pie = stage1->pie;
93 g->have_lto = stage1->lto;
93 g->have_stack_probing = stage1->enable_stack_probing;94 g->have_stack_probing = stage1->enable_stack_probing;
94 g->red_zone = stage1->red_zone;95 g->red_zone = stage1->red_zone;
95 g->is_single_threaded = stage1->is_single_threaded;96 g->is_single_threaded = stage1->is_single_threaded;
src/stage1/stage1.h+1
...@@ -178,6 +178,7 @@ struct ZigStage1 {...@@ -178,6 +178,7 @@ struct ZigStage1 {
178178
179 bool pic;179 bool pic;
180 bool pie;180 bool pie;
181 bool lto;
181 bool link_libc;182 bool link_libc;
182 bool link_libcpp;183 bool link_libcpp;
183 bool strip;184 bool strip;
src/target.zig-2
...@@ -354,8 +354,6 @@ pub fn hasRedZone(target: std.Target) bool {...@@ -354,8 +354,6 @@ pub fn hasRedZone(target: std.Target) bool {
354 return switch (target.cpu.arch) {354 return switch (target.cpu.arch) {
355 .x86_64,355 .x86_64,
356 .i386,356 .i386,
357 .wasm32,
358 .wasm64,
359 .powerpc,357 .powerpc,
360 .powerpc64,358 .powerpc64,
361 .powerpc64le,359 .powerpc64le,
src/zig_llvm.cpp+7-3
...@@ -22,6 +22,7 @@...@@ -22,6 +22,7 @@
2222
23#include <llvm/Analysis/TargetLibraryInfo.h>23#include <llvm/Analysis/TargetLibraryInfo.h>
24#include <llvm/Analysis/TargetTransformInfo.h>24#include <llvm/Analysis/TargetTransformInfo.h>
25#include <llvm/Bitcode/BitcodeWriter.h>
25#include <llvm/IR/DIBuilder.h>26#include <llvm/IR/DIBuilder.h>
26#include <llvm/IR/DiagnosticInfo.h>27#include <llvm/IR/DiagnosticInfo.h>
27#include <llvm/IR/IRBuilder.h>28#include <llvm/IR/IRBuilder.h>
...@@ -184,7 +185,7 @@ unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD) {...@@ -184,7 +185,7 @@ unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD) {
184185
185bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,186bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
186 char **error_message, bool is_debug,187 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,
188 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)189 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
189{190{
190 TimePassesIsEnabled = time_report;191 TimePassesIsEnabled = time_report;
...@@ -234,7 +235,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -234,7 +235,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
234 PMBuilder->VerifyInput = assertions_on;235 PMBuilder->VerifyInput = assertions_on;
235 PMBuilder->VerifyOutput = assertions_on;236 PMBuilder->VerifyOutput = assertions_on;
236 PMBuilder->MergeFunctions = !is_debug;237 PMBuilder->MergeFunctions = !is_debug;
237 PMBuilder->PrepareForLTO = false;238 PMBuilder->PrepareForLTO = lto;
238 PMBuilder->PrepareForThinLTO = false;239 PMBuilder->PrepareForThinLTO = false;
239 PMBuilder->PerformThinLTO = false;240 PMBuilder->PerformThinLTO = false;
240241
...@@ -272,7 +273,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -272,7 +273,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
272 PMBuilder->populateModulePassManager(MPM);273 PMBuilder->populateModulePassManager(MPM);
273274
274 // Set output passes.275 // Set output passes.
275 if (dest_bin) {276 if (dest_bin && !lto) {
276 if (target_machine->addPassesToEmitFile(MPM, *dest_bin, nullptr, CGFT_ObjectFile)) {277 if (target_machine->addPassesToEmitFile(MPM, *dest_bin, nullptr, CGFT_ObjectFile)) {
277 *error_message = strdup("TargetMachine can't emit an object file");278 *error_message = strdup("TargetMachine can't emit an object file");
278 return true;279 return true;
...@@ -299,6 +300,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -299,6 +300,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
299 return true;300 return true;
300 }301 }
301 }302 }
303 if (dest_bin && lto) {
304 WriteBitcodeToFile(*module, *dest_bin);
305 }
302306
303 if (time_report) {307 if (time_report) {
304 TimerGroup::printAll(errs());308 TimerGroup::printAll(errs());
src/zig_llvm.h+1-1
...@@ -48,7 +48,7 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);...@@ -48,7 +48,7 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
4848
49ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,49ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
50 char **error_message, bool is_debug,50 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,
52 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);52 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{...@@ -62,6 +62,14 @@ const known_options = [_]KnownOpt{
62 .name = "fno-PIE",62 .name = "fno-PIE",
63 .ident = "no_pie",63 .ident = "no_pie",
64 },64 },
65 .{
66 .name = "flto",
67 .ident = "lto",
68 },
69 .{
70 .name = "fno-lto",
71 .ident = "no_lto",
72 },
65 .{73 .{
66 .name = "nolibc",74 .name = "nolibc",
67 .ident = "nostdlib",75 .ident = "nostdlib",