| author | |
| committer | |
| log | a5fb28070f37c2cad92ac8805bcc704e872fc538 |
| tree | 05e8432688cd83fcb4989a13087c01c74df172c5 |
| parent | 36295d712fbd561c3de9b3eb46e776d63e646e9a |
* Added doc comments for `std.Target.ObjectFormat` enum
* `std.Target.oFileExt` is removed because it is incorrect for Plan-9
targets. Instead, use `std.Target.ObjectFormat.fileExt` and pass a
CPU architecture.
* Added `Compilation.Directory.joinZ` for when a null byte is desired.
* Improvements to `Compilation.create` logic for computing `use_llvm`
and reporting errors in contradictory flags. `-femit-llvm-ir` and
`-femit-llvm-bc` will now imply `-fLLVM`.
* Fix compilation when passing `.bc` files on the command line.
* Improvements to the stage2 LLVM backend:
- cleaned up error messages and error reporting. Properly bubble up
some errors rather than dumping to stderr; others turn into panics.
- properly call ZigLLVMCreateTargetMachine and
ZigLLVMTargetMachineEmitToFile and implement calculation of the
respective parameters (cpu features, code model, abi name, lto,
tsan, etc).
- LLVM module verification only runs in debug builds of the compiler
- use LLVMDumpModule rather than printToString because in the case
that we incorrectly pass a null pointer to LLVM it may crash during
dumping the module and having it partially printed is helpful in
this case.
- support -femit-asm, -fno-emit-bin, -femit-llvm-ir, -femit-llvm-bc
- Support LLVM backend when used with Mach-O and WASM linkers.20 files changed, 388 insertions(+), 194 deletions(-)
doc/docgen.zig+4-4| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = std.builtin; | |
| 2 | const builtin = @import("builtin"); | |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const fs = std.fs; |
| 5 | 5 | const process = std.process; |
| ... | ... | @@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator; |
| 13 | 13 | const max_doc_file_size = 10 * 1024 * 1024; |
| 14 | 14 | |
| 15 | 15 | const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt(); |
| 16 | const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt(); | |
| 16 | const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch); | |
| 17 | 17 | const tmp_dir_name = "docgen_tmp"; |
| 18 | 18 | const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext; |
| 19 | 19 | |
| ... | ... | @@ -281,7 +281,7 @@ const Code = struct { |
| 281 | 281 | name: []const u8, |
| 282 | 282 | source_token: Token, |
| 283 | 283 | is_inline: bool, |
| 284 | mode: builtin.Mode, | |
| 284 | mode: std.builtin.Mode, | |
| 285 | 285 | link_objects: []const []const u8, |
| 286 | 286 | target_str: ?[]const u8, |
| 287 | 287 | link_libc: bool, |
| ... | ... | @@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc { |
| 531 | 531 | return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str}); |
| 532 | 532 | } |
| 533 | 533 | |
| 534 | var mode: builtin.Mode = .Debug; | |
| 534 | var mode: std.builtin.Mode = .Debug; | |
| 535 | 535 | var link_objects = std.ArrayList([]const u8).init(allocator); |
| 536 | 536 | defer link_objects.deinit(); |
| 537 | 537 | var target_str: ?[]const u8 = null; |
lib/std/target.zig+63-36| ... | ... | @@ -549,15 +549,36 @@ pub const Target = struct { |
| 549 | 549 | }; |
| 550 | 550 | |
| 551 | 551 | pub const ObjectFormat = enum { |
| 552 | /// Common Object File Format (Windows) | |
| 552 | 553 | coff, |
| 554 | /// Executable and Linking Format | |
| 553 | 555 | elf, |
| 556 | /// macOS relocatables | |
| 554 | 557 | macho, |
| 558 | /// WebAssembly | |
| 555 | 559 | wasm, |
| 560 | /// C source code | |
| 556 | 561 | c, |
| 562 | /// Standard, Portable Intermediate Representation V | |
| 557 | 563 | spirv, |
| 564 | /// Intel IHEX | |
| 558 | 565 | hex, |
| 566 | /// Machine code with no metadata. | |
| 559 | 567 | raw, |
| 568 | /// Plan 9 from Bell Labs | |
| 560 | 569 | plan9, |
| 570 | ||
| 571 | pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 { | |
| 572 | return switch (of) { | |
| 573 | .coff => ".obj", | |
| 574 | .elf, .macho, .wasm => ".o", | |
| 575 | .c => ".c", | |
| 576 | .spirv => ".spv", | |
| 577 | .hex => ".ihex", | |
| 578 | .raw => ".bin", | |
| 579 | .plan9 => plan9Ext(cpu_arch), | |
| 580 | }; | |
| 581 | } | |
| 561 | 582 | }; |
| 562 | 583 | |
| 563 | 584 | pub const SubSystem = enum { |
| ... | ... | @@ -1289,30 +1310,16 @@ pub const Target = struct { |
| 1289 | 1310 | return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); |
| 1290 | 1311 | } |
| 1291 | 1312 | |
| 1292 | pub fn oFileExt_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 { | |
| 1293 | if (abi == .msvc) { | |
| 1294 | return ".obj"; | |
| 1295 | } | |
| 1296 | switch (os_tag) { | |
| 1297 | .windows, .uefi => return ".obj", | |
| 1298 | else => return ".o", | |
| 1299 | } | |
| 1300 | } | |
| 1301 | ||
| 1302 | pub fn oFileExt(self: Target) [:0]const u8 { | |
| 1303 | return oFileExt_os_abi(self.os.tag, self.abi); | |
| 1304 | } | |
| 1305 | ||
| 1306 | 1313 | pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 { |
| 1307 | switch (os_tag) { | |
| 1308 | .windows => return ".exe", | |
| 1309 | .uefi => return ".efi", | |
| 1310 | else => if (cpu_arch.isWasm()) { | |
| 1311 | return ".wasm"; | |
| 1312 | } else { | |
| 1313 | return ""; | |
| 1314 | return switch (os_tag) { | |
| 1315 | .windows => ".exe", | |
| 1316 | .uefi => ".efi", | |
| 1317 | .plan9 => plan9Ext(cpu_arch), | |
| 1318 | else => switch (cpu_arch) { | |
| 1319 | .wasm32, .wasm64 => ".wasm", | |
| 1320 | else => "", | |
| 1314 | 1321 | }, |
| 1315 | } | |
| 1322 | }; | |
| 1316 | 1323 | } |
| 1317 | 1324 | |
| 1318 | 1325 | pub fn exeFileExt(self: Target) [:0]const u8 { |
| ... | ... | @@ -1352,20 +1359,16 @@ pub const Target = struct { |
| 1352 | 1359 | } |
| 1353 | 1360 | |
| 1354 | 1361 | pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat { |
| 1355 | if (os_tag == .windows or os_tag == .uefi) { | |
| 1356 | return .coff; | |
| 1357 | } else if (os_tag.isDarwin()) { | |
| 1358 | return .macho; | |
| 1359 | } | |
| 1360 | if (cpu_arch.isWasm()) { | |
| 1361 | return .wasm; | |
| 1362 | } | |
| 1363 | if (cpu_arch.isSPIRV()) { | |
| 1364 | return .spirv; | |
| 1365 | } | |
| 1366 | if (os_tag == .plan9) | |
| 1367 | return .plan9; | |
| 1368 | return .elf; | |
| 1362 | return switch (os_tag) { | |
| 1363 | .windows, .uefi => .coff, | |
| 1364 | .ios, .macos, .watchos, .tvos => .macho, | |
| 1365 | .plan9 => .plan9, | |
| 1366 | else => return switch (cpu_arch) { | |
| 1367 | .wasm32, .wasm64 => .wasm, | |
| 1368 | .spirv32, .spirv64 => .spirv, | |
| 1369 | else => .elf, | |
| 1370 | }, | |
| 1371 | }; | |
| 1369 | 1372 | } |
| 1370 | 1373 | |
| 1371 | 1374 | pub fn getObjectFormat(self: Target) ObjectFormat { |
| ... | ... | @@ -1676,6 +1679,30 @@ pub const Target = struct { |
| 1676 | 1679 | |
| 1677 | 1680 | return false; |
| 1678 | 1681 | } |
| 1682 | ||
| 1683 | /// 0c spim little-endian MIPS 3000 family | |
| 1684 | /// 1c 68000 Motorola MC68000 | |
| 1685 | /// 2c 68020 Motorola MC68020 | |
| 1686 | /// 5c arm little-endian ARM | |
| 1687 | /// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) | |
| 1688 | /// 7c arm64 ARM64 (ARMv8) | |
| 1689 | /// 8c 386 Intel i386, i486, Pentium, etc. | |
| 1690 | /// kc sparc Sun SPARC | |
| 1691 | /// qc power Power PC | |
| 1692 | /// vc mips big-endian MIPS 3000 family | |
| 1693 | pub fn plan9Ext(cpu_arch: Cpu.Arch) [:0]const u8 { | |
| 1694 | return switch (cpu_arch) { | |
| 1695 | .arm => ".5", | |
| 1696 | .x86_64 => ".6", | |
| 1697 | .aarch64 => ".7", | |
| 1698 | .i386 => ".8", | |
| 1699 | .sparc => ".k", | |
| 1700 | .powerpc, .powerpcle => ".q", | |
| 1701 | .mips, .mipsel => ".v", | |
| 1702 | // ISAs without designated characters get 'X' for lack of a better option. | |
| 1703 | else => ".X", | |
| 1704 | }; | |
| 1705 | } | |
| 1679 | 1706 | }; |
| 1680 | 1707 | |
| 1681 | 1708 | test { |
lib/std/zig.zig+9-29| ... | ... | @@ -108,7 +108,8 @@ pub const BinNameOptions = struct { |
| 108 | 108 | pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { |
| 109 | 109 | const root_name = options.root_name; |
| 110 | 110 | const target = options.target; |
| 111 | switch (options.object_format orelse target.getObjectFormat()) { | |
| 111 | const ofmt = options.object_format orelse target.getObjectFormat(); | |
| 112 | switch (ofmt) { | |
| 112 | 113 | .coff => switch (options.output_mode) { |
| 113 | 114 | .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }), |
| 114 | 115 | .Lib => { |
| ... | ... | @@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro |
| 118 | 119 | }; |
| 119 | 120 | return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix }); |
| 120 | 121 | }, |
| 121 | .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), | |
| 122 | .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}), | |
| 122 | 123 | }, |
| 123 | 124 | .elf => switch (options.output_mode) { |
| 124 | 125 | .Exe => return allocator.dupe(u8, root_name), |
| ... | ... | @@ -140,7 +141,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro |
| 140 | 141 | }, |
| 141 | 142 | } |
| 142 | 143 | }, |
| 143 | .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), | |
| 144 | .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), | |
| 144 | 145 | }, |
| 145 | 146 | .macho => switch (options.output_mode) { |
| 146 | 147 | .Exe => return allocator.dupe(u8, root_name), |
| ... | ... | @@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro |
| 163 | 164 | } |
| 164 | 165 | return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix }); |
| 165 | 166 | }, |
| 166 | .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), | |
| 167 | .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), | |
| 167 | 168 | }, |
| 168 | 169 | .wasm => switch (options.output_mode) { |
| 169 | 170 | .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }), |
| ... | ... | @@ -175,36 +176,15 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro |
| 175 | 176 | .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}), |
| 176 | 177 | } |
| 177 | 178 | }, |
| 178 | .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }), | |
| 179 | .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), | |
| 179 | 180 | }, |
| 180 | 181 | .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}), |
| 181 | 182 | .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), |
| 182 | 183 | .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), |
| 183 | 184 | .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), |
| 184 | .plan9 => { | |
| 185 | // copied from 2c(1) | |
| 186 | // 0c spim little-endian MIPS 3000 family | |
| 187 | // 1c 68000 Motorola MC68000 | |
| 188 | // 2c 68020 Motorola MC68020 | |
| 189 | // 5c arm little-endian ARM | |
| 190 | // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) | |
| 191 | // 7c arm64 ARM64 (ARMv8) | |
| 192 | // 8c 386 Intel i386, i486, Pentium, etc. | |
| 193 | // kc sparc Sun SPARC | |
| 194 | // qc power Power PC | |
| 195 | // vc mips big-endian MIPS 3000 family | |
| 196 | const char: u8 = switch (target.cpu.arch) { | |
| 197 | .arm => '5', | |
| 198 | .x86_64 => '6', | |
| 199 | .aarch64 => '7', | |
| 200 | .i386 => '8', | |
| 201 | .sparc => 'k', | |
| 202 | .powerpc, .powerpcle => 'q', | |
| 203 | .mips, .mipsel => 'v', | |
| 204 | else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X | |
| 205 | }; | |
| 206 | return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char }); | |
| 207 | }, | |
| 185 | .plan9 => return std.fmt.allocPrint(allocator, "{s}{s}", .{ | |
| 186 | root_name, ofmt.fileExt(target.cpu.arch), | |
| 187 | }), | |
| 208 | 188 | } |
| 209 | 189 | } |
| 210 | 190 |
lib/std/zig/cross_target.zig-4| ... | ... | @@ -473,10 +473,6 @@ pub const CrossTarget = struct { |
| 473 | 473 | return self.getOsTag() == .windows; |
| 474 | 474 | } |
| 475 | 475 | |
| 476 | pub fn oFileExt(self: CrossTarget) [:0]const u8 { | |
| 477 | return Target.oFileExt_os_abi(self.getOsTag(), self.getAbi()); | |
| 478 | } | |
| 479 | ||
| 480 | 476 | pub fn exeFileExt(self: CrossTarget) [:0]const u8 { |
| 481 | 477 | return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag()); |
| 482 | 478 | } |
src/Compilation.zig+40-3| ... | ... | @@ -143,6 +143,7 @@ debug_compiler_runtime_libs: bool, |
| 143 | 143 | |
| 144 | 144 | emit_asm: ?EmitLoc, |
| 145 | 145 | emit_llvm_ir: ?EmitLoc, |
| 146 | emit_llvm_bc: ?EmitLoc, | |
| 146 | 147 | emit_analysis: ?EmitLoc, |
| 147 | 148 | emit_docs: ?EmitLoc, |
| 148 | 149 | |
| ... | ... | @@ -586,6 +587,17 @@ pub const Directory = struct { |
| 586 | 587 | return std.fs.path.join(allocator, paths); |
| 587 | 588 | } |
| 588 | 589 | } |
| 590 | ||
| 591 | pub fn joinZ(self: Directory, allocator: *Allocator, paths: []const []const u8) ![:0]u8 { | |
| 592 | if (self.path) |p| { | |
| 593 | // TODO clean way to do this with only 1 allocation | |
| 594 | const part2 = try std.fs.path.join(allocator, paths); | |
| 595 | defer allocator.free(part2); | |
| 596 | return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); | |
| 597 | } else { | |
| 598 | return std.fs.path.joinZ(allocator, paths); | |
| 599 | } | |
| 600 | } | |
| 589 | 601 | }; |
| 590 | 602 | |
| 591 | 603 | pub const EmitLoc = struct { |
| ... | ... | @@ -623,6 +635,8 @@ pub const InitOptions = struct { |
| 623 | 635 | emit_asm: ?EmitLoc = null, |
| 624 | 636 | /// `null` means to not emit LLVM IR. |
| 625 | 637 | emit_llvm_ir: ?EmitLoc = null, |
| 638 | /// `null` means to not emit LLVM module bitcode. | |
| 639 | emit_llvm_bc: ?EmitLoc = null, | |
| 626 | 640 | /// `null` means to not emit semantic analysis JSON. |
| 627 | 641 | emit_analysis: ?EmitLoc = null, |
| 628 | 642 | /// `null` means to not emit docs. |
| ... | ... | @@ -819,6 +833,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 819 | 833 | break :blk false; |
| 820 | 834 | } |
| 821 | 835 | } |
| 836 | // If we have no zig code to compile, no need for stage1 backend. | |
| 837 | if (options.root_pkg == null) | |
| 838 | break :blk false; | |
| 839 | ||
| 822 | 840 | break :blk build_options.is_stage1; |
| 823 | 841 | }; |
| 824 | 842 | |
| ... | ... | @@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 835 | 853 | if (ofmt == .c) |
| 836 | 854 | break :blk false; |
| 837 | 855 | |
| 856 | // If emitting to LLVM bitcode object format, must use LLVM backend. | |
| 857 | if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) | |
| 858 | break :blk true; | |
| 859 | ||
| 838 | 860 | // The stage1 compiler depends on the stage1 C++ LLVM backend |
| 839 | 861 | // to compile zig code. |
| 840 | 862 | if (use_stage1) |
| ... | ... | @@ -853,6 +875,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 853 | 875 | if (options.machine_code_model != .default) { |
| 854 | 876 | return error.MachineCodeModelNotSupportedWithoutLlvm; |
| 855 | 877 | } |
| 878 | if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { | |
| 879 | return error.EmittingLlvmModuleRequiresUsingLlvmBackend; | |
| 880 | } | |
| 881 | if (use_stage1) { | |
| 882 | return error.@"stage1 only supports LLVM backend"; | |
| 883 | } | |
| 856 | 884 | } |
| 857 | 885 | |
| 858 | 886 | const tsan = options.want_tsan orelse false; |
| ... | ... | @@ -1381,6 +1409,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1381 | 1409 | .bin_file = bin_file, |
| 1382 | 1410 | .emit_asm = options.emit_asm, |
| 1383 | 1411 | .emit_llvm_ir = options.emit_llvm_ir, |
| 1412 | .emit_llvm_bc = options.emit_llvm_bc, | |
| 1384 | 1413 | .emit_analysis = options.emit_analysis, |
| 1385 | 1414 | .emit_docs = options.emit_docs, |
| 1386 | 1415 | .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), |
| ... | ... | @@ -2728,7 +2757,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2728 | 2757 | comp.bin_file.options.root_name |
| 2729 | 2758 | else |
| 2730 | 2759 | c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len]; |
| 2731 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, comp.getTarget().oFileExt() }); | |
| 2760 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ | |
| 2761 | o_basename_noext, | |
| 2762 | comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch), | |
| 2763 | }); | |
| 2732 | 2764 | |
| 2733 | 2765 | const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { |
| 2734 | 2766 | var argv = std.ArrayList([]const u8).init(comp.gpa); |
| ... | ... | @@ -3978,6 +4010,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3978 | 4010 | } |
| 3979 | 4011 | man.hash.addOptionalEmitLoc(comp.emit_asm); |
| 3980 | 4012 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); |
| 4013 | man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); | |
| 3981 | 4014 | man.hash.addOptionalEmitLoc(comp.emit_analysis); |
| 3982 | 4015 | man.hash.addOptionalEmitLoc(comp.emit_docs); |
| 3983 | 4016 | man.hash.add(comp.test_evented_io); |
| ... | ... | @@ -4083,13 +4116,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4083 | 4116 | ) orelse return error.OutOfMemory; |
| 4084 | 4117 | |
| 4085 | 4118 | const emit_bin_path = if (comp.bin_file.options.emit != null) blk: { |
| 4086 | const bin_basename = try std.zig.binNameAlloc(arena, .{ | |
| 4119 | const obj_basename = try std.zig.binNameAlloc(arena, .{ | |
| 4087 | 4120 | .root_name = comp.bin_file.options.root_name, |
| 4088 | 4121 | .target = target, |
| 4089 | 4122 | .output_mode = .Obj, |
| 4090 | 4123 | }); |
| 4091 | break :blk try directory.join(arena, &[_][]const u8{bin_basename}); | |
| 4124 | break :blk try directory.join(arena, &[_][]const u8{obj_basename}); | |
| 4092 | 4125 | } else ""; |
| 4126 | ||
| 4093 | 4127 | if (mod.emit_h != null) { |
| 4094 | 4128 | log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{}); |
| 4095 | 4129 | } |
| ... | ... | @@ -4097,6 +4131,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4097 | 4131 | const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory); |
| 4098 | 4132 | const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory); |
| 4099 | 4133 | const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory); |
| 4134 | const emit_llvm_bc_path = try stage1LocPath(arena, comp.emit_llvm_bc, directory); | |
| 4100 | 4135 | const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory); |
| 4101 | 4136 | const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory); |
| 4102 | 4137 | const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null); |
| ... | ... | @@ -4117,6 +4152,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4117 | 4152 | .emit_asm_len = emit_asm_path.len, |
| 4118 | 4153 | .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr, |
| 4119 | 4154 | .emit_llvm_ir_len = emit_llvm_ir_path.len, |
| 4155 | .emit_bitcode_ptr = emit_llvm_bc_path.ptr, | |
| 4156 | .emit_bitcode_len = emit_llvm_bc_path.len, | |
| 4120 | 4157 | .emit_analysis_json_ptr = emit_analysis_path.ptr, |
| 4121 | 4158 | .emit_analysis_json_len = emit_analysis_path.len, |
| 4122 | 4159 | .emit_docs_ptr = emit_docs_path.ptr, |
src/codegen/llvm.zig+131-65| ... | ... | @@ -72,9 +72,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 72 | 72 | .renderscript32 => "renderscript32", |
| 73 | 73 | .renderscript64 => "renderscript64", |
| 74 | 74 | .ve => "ve", |
| 75 | .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, | |
| 76 | .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV, | |
| 77 | .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV, | |
| 75 | .spu_2 => return error.@"LLVM backend does not support SPU Mark II", | |
| 76 | .spirv32 => return error.@"LLVM backend does not support SPIR-V", | |
| 77 | .spirv64 => return error.@"LLVM backend does not support SPIR-V", | |
| 78 | 78 | }; |
| 79 | 79 | |
| 80 | 80 | const llvm_os = switch (target.os.tag) { |
| ... | ... | @@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 114 | 114 | .wasi => "wasi", |
| 115 | 115 | .emscripten => "emscripten", |
| 116 | 116 | .uefi => "windows", |
| 117 | .opencl => return error.LLVMBackendDoesNotSupportOpenCL, | |
| 118 | .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450, | |
| 119 | .vulkan => return error.LLVMBackendDoesNotSupportVulkan, | |
| 120 | .plan9 => return error.LLVMBackendDoesNotSupportPlan9, | |
| 121 | .other => "unknown", | |
| 117 | ||
| 118 | .opencl, | |
| 119 | .glsl450, | |
| 120 | .vulkan, | |
| 121 | .plan9, | |
| 122 | .other, | |
| 123 | => "unknown", | |
| 122 | 124 | }; |
| 123 | 125 | |
| 124 | 126 | const llvm_abi = switch (target.abi) { |
| ... | ... | @@ -152,84 +154,105 @@ pub const Object = struct { |
| 152 | 154 | llvm_module: *const llvm.Module, |
| 153 | 155 | context: *const llvm.Context, |
| 154 | 156 | target_machine: *const llvm.TargetMachine, |
| 155 | object_pathZ: [:0]const u8, | |
| 156 | ||
| 157 | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object { | |
| 158 | _ = sub_path; | |
| 159 | const self = try allocator.create(Object); | |
| 160 | errdefer allocator.destroy(self); | |
| 161 | ||
| 162 | const obj_basename = try std.zig.binNameAlloc(allocator, .{ | |
| 163 | .root_name = options.root_name, | |
| 164 | .target = options.target, | |
| 165 | .output_mode = .Obj, | |
| 166 | }); | |
| 167 | defer allocator.free(obj_basename); | |
| 168 | 157 | |
| 169 | const o_directory = options.module.?.zig_cache_artifact_directory; | |
| 170 | const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename}); | |
| 171 | defer allocator.free(object_path); | |
| 172 | ||
| 173 | const object_pathZ = try allocator.dupeZ(u8, object_path); | |
| 174 | errdefer allocator.free(object_pathZ); | |
| 158 | pub fn create(gpa: *Allocator, options: link.Options) !*Object { | |
| 159 | const obj = try gpa.create(Object); | |
| 160 | errdefer gpa.destroy(obj); | |
| 161 | obj.* = try Object.init(gpa, options); | |
| 162 | return obj; | |
| 163 | } | |
| 175 | 164 | |
| 165 | pub fn init(gpa: *Allocator, options: link.Options) !Object { | |
| 176 | 166 | const context = llvm.Context.create(); |
| 177 | 167 | errdefer context.dispose(); |
| 178 | 168 | |
| 179 | 169 | initializeLLVMTargets(); |
| 180 | 170 | |
| 181 | const root_nameZ = try allocator.dupeZ(u8, options.root_name); | |
| 182 | defer allocator.free(root_nameZ); | |
| 171 | const root_nameZ = try gpa.dupeZ(u8, options.root_name); | |
| 172 | defer gpa.free(root_nameZ); | |
| 183 | 173 | const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); |
| 184 | 174 | errdefer llvm_module.dispose(); |
| 185 | 175 | |
| 186 | const llvm_target_triple = try targetTriple(allocator, options.target); | |
| 187 | defer allocator.free(llvm_target_triple); | |
| 176 | const llvm_target_triple = try targetTriple(gpa, options.target); | |
| 177 | defer gpa.free(llvm_target_triple); | |
| 188 | 178 | |
| 189 | 179 | var error_message: [*:0]const u8 = undefined; |
| 190 | 180 | var target: *const llvm.Target = undefined; |
| 191 | 181 | if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) { |
| 192 | 182 | defer llvm.disposeMessage(error_message); |
| 193 | 183 | |
| 194 | const stderr = std.io.getStdErr().writer(); | |
| 195 | try stderr.print( | |
| 196 | \\Zig is expecting LLVM to understand this target: '{s}' | |
| 197 | \\However LLVM responded with: "{s}" | |
| 198 | \\ | |
| 199 | , | |
| 200 | .{ llvm_target_triple, error_message }, | |
| 201 | ); | |
| 202 | return error.InvalidLLVMTriple; | |
| 184 | log.err("LLVM failed to parse '{s}': {s}", .{ llvm_target_triple, error_message }); | |
| 185 | return error.InvalidLlvmTriple; | |
| 203 | 186 | } |
| 204 | 187 | |
| 205 | const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive; | |
| 188 | const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) | |
| 189 | .None | |
| 190 | else | |
| 191 | .Aggressive; | |
| 192 | ||
| 193 | const reloc_mode: llvm.RelocMode = if (options.pic) | |
| 194 | .PIC | |
| 195 | else if (options.link_mode == .Dynamic) | |
| 196 | llvm.RelocMode.DynamicNoPIC | |
| 197 | else | |
| 198 | .Static; | |
| 199 | ||
| 200 | const code_model: llvm.CodeModel = switch (options.machine_code_model) { | |
| 201 | .default => .Default, | |
| 202 | .tiny => .Tiny, | |
| 203 | .small => .Small, | |
| 204 | .kernel => .Kernel, | |
| 205 | .medium => .Medium, | |
| 206 | .large => .Large, | |
| 207 | }; | |
| 208 | ||
| 209 | // TODO handle float ABI better- it should depend on the ABI portion of std.Target | |
| 210 | const float_abi: llvm.ABIType = .Default; | |
| 211 | ||
| 212 | // TODO a way to override this as part of std.Target ABI? | |
| 213 | const abi_name: ?[*:0]const u8 = switch (options.target.cpu.arch) { | |
| 214 | .riscv32 => switch (options.target.os.tag) { | |
| 215 | .linux => "ilp32d", | |
| 216 | else => "ilp32", | |
| 217 | }, | |
| 218 | .riscv64 => switch (options.target.os.tag) { | |
| 219 | .linux => "lp64d", | |
| 220 | else => "lp64", | |
| 221 | }, | |
| 222 | else => null, | |
| 223 | }; | |
| 224 | ||
| 206 | 225 | const target_machine = llvm.TargetMachine.create( |
| 207 | 226 | target, |
| 208 | 227 | llvm_target_triple.ptr, |
| 209 | "", | |
| 210 | "", | |
| 228 | if (options.target.cpu.model.llvm_name) |s| s.ptr else null, | |
| 229 | options.llvm_cpu_features, | |
| 211 | 230 | opt_level, |
| 212 | .Static, | |
| 213 | .Default, | |
| 231 | reloc_mode, | |
| 232 | code_model, | |
| 233 | options.function_sections, | |
| 234 | float_abi, | |
| 235 | abi_name, | |
| 214 | 236 | ); |
| 215 | 237 | errdefer target_machine.dispose(); |
| 216 | 238 | |
| 217 | self.* = .{ | |
| 239 | return Object{ | |
| 218 | 240 | .llvm_module = llvm_module, |
| 219 | 241 | .context = context, |
| 220 | 242 | .target_machine = target_machine, |
| 221 | .object_pathZ = object_pathZ, | |
| 222 | 243 | }; |
| 223 | return self; | |
| 224 | 244 | } |
| 225 | 245 | |
| 226 | pub fn deinit(self: *Object, allocator: *Allocator) void { | |
| 246 | pub fn deinit(self: *Object) void { | |
| 227 | 247 | self.target_machine.dispose(); |
| 228 | 248 | self.llvm_module.dispose(); |
| 229 | 249 | self.context.dispose(); |
| 250 | self.* = undefined; | |
| 251 | } | |
| 230 | 252 | |
| 231 | allocator.free(self.object_pathZ); | |
| 232 | allocator.destroy(self); | |
| 253 | pub fn destroy(self: *Object, gpa: *Allocator) void { | |
| 254 | self.deinit(); | |
| 255 | gpa.destroy(self); | |
| 233 | 256 | } |
| 234 | 257 | |
| 235 | 258 | fn initializeLLVMTargets() void { |
| ... | ... | @@ -240,38 +263,81 @@ pub const Object = struct { |
| 240 | 263 | llvm.initializeAllAsmParsers(); |
| 241 | 264 | } |
| 242 | 265 | |
| 266 | fn locPath( | |
| 267 | arena: *Allocator, | |
| 268 | opt_loc: ?Compilation.EmitLoc, | |
| 269 | cache_directory: Compilation.Directory, | |
| 270 | ) !?[*:0]u8 { | |
| 271 | const loc = opt_loc orelse return null; | |
| 272 | const directory = loc.directory orelse cache_directory; | |
| 273 | const slice = try directory.joinZ(arena, &[_][]const u8{loc.basename}); | |
| 274 | return slice.ptr; | |
| 275 | } | |
| 276 | ||
| 243 | 277 | pub fn flushModule(self: *Object, comp: *Compilation) !void { |
| 244 | 278 | if (comp.verbose_llvm_ir) { |
| 245 | const dump = self.llvm_module.printToString(); | |
| 246 | defer llvm.disposeMessage(dump); | |
| 247 | ||
| 248 | const stderr = std.io.getStdErr().writer(); | |
| 249 | try stderr.writeAll(std.mem.spanZ(dump)); | |
| 279 | self.llvm_module.dump(); | |
| 250 | 280 | } |
| 251 | 281 | |
| 252 | { | |
| 282 | if (std.debug.runtime_safety) { | |
| 253 | 283 | var error_message: [*:0]const u8 = undefined; |
| 254 | 284 | // verifyModule always allocs the error_message even if there is no error |
| 255 | 285 | defer llvm.disposeMessage(error_message); |
| 256 | 286 | |
| 257 | 287 | if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) { |
| 258 | const stderr = std.io.getStdErr().writer(); | |
| 259 | try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message}); | |
| 260 | return error.BrokenLLVMModule; | |
| 288 | std.debug.print("\n{s}\n", .{error_message}); | |
| 289 | @panic("LLVM module verification failed"); | |
| 261 | 290 | } |
| 262 | 291 | } |
| 263 | 292 | |
| 293 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); | |
| 294 | defer arena_allocator.deinit(); | |
| 295 | const arena = &arena_allocator.allocator; | |
| 296 | ||
| 297 | const mod = comp.bin_file.options.module.?; | |
| 298 | const cache_dir = mod.zig_cache_artifact_directory; | |
| 299 | ||
| 300 | const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: { | |
| 301 | const obj_basename = try std.zig.binNameAlloc(arena, .{ | |
| 302 | .root_name = comp.bin_file.options.root_name, | |
| 303 | .target = comp.bin_file.options.target, | |
| 304 | .output_mode = .Obj, | |
| 305 | }); | |
| 306 | if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| { | |
| 307 | break :blk p.ptr; | |
| 308 | } else |err| { | |
| 309 | return err; | |
| 310 | } | |
| 311 | } else null; | |
| 312 | ||
| 313 | const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir); | |
| 314 | const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir); | |
| 315 | const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir); | |
| 316 | ||
| 264 | 317 | var error_message: [*:0]const u8 = undefined; |
| 265 | 318 | if (self.target_machine.emitToFile( |
| 266 | 319 | self.llvm_module, |
| 267 | self.object_pathZ.ptr, | |
| 268 | .ObjectFile, | |
| 269 | 320 | &error_message, |
| 270 | ).toBool()) { | |
| 321 | comp.bin_file.options.optimize_mode == .Debug, | |
| 322 | comp.bin_file.options.optimize_mode == .ReleaseSmall, | |
| 323 | comp.time_report, | |
| 324 | comp.bin_file.options.tsan, | |
| 325 | comp.bin_file.options.lto, | |
| 326 | emit_asm_path, | |
| 327 | emit_bin_path, | |
| 328 | emit_llvm_ir_path, | |
| 329 | emit_llvm_bc_path, | |
| 330 | )) { | |
| 271 | 331 | defer llvm.disposeMessage(error_message); |
| 272 | 332 | |
| 273 | const stderr = std.io.getStdErr().writer(); | |
| 274 | try stderr.print("LLVM failed to emit file: {s}\n", .{error_message}); | |
| 333 | const emit_asm_msg = emit_asm_path orelse "(none)"; | |
| 334 | const emit_bin_msg = emit_bin_path orelse "(none)"; | |
| 335 | const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)"; | |
| 336 | const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)"; | |
| 337 | log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ | |
| 338 | emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg, | |
| 339 | error_message, | |
| 340 | }); | |
| 275 | 341 | return error.FailedToEmit; |
| 276 | 342 | } |
| 277 | 343 | } |
src/codegen/llvm/bindings.zig+35-13| ... | ... | @@ -123,6 +123,9 @@ pub const Module = opaque { |
| 123 | 123 | |
| 124 | 124 | pub const getNamedGlobal = LLVMGetNamedGlobal; |
| 125 | 125 | extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value; |
| 126 | ||
| 127 | pub const dump = LLVMDumpModule; | |
| 128 | extern fn LLVMDumpModule(M: *const Module) void; | |
| 126 | 129 | }; |
| 127 | 130 | |
| 128 | 131 | pub const lookupIntrinsicID = LLVMLookupIntrinsicID; |
| ... | ... | @@ -250,31 +253,41 @@ pub const BasicBlock = opaque { |
| 250 | 253 | }; |
| 251 | 254 | |
| 252 | 255 | pub const TargetMachine = opaque { |
| 253 | pub const create = LLVMCreateTargetMachine; | |
| 254 | extern fn LLVMCreateTargetMachine( | |
| 256 | pub const create = ZigLLVMCreateTargetMachine; | |
| 257 | extern fn ZigLLVMCreateTargetMachine( | |
| 255 | 258 | T: *const Target, |
| 256 | 259 | Triple: [*:0]const u8, |
| 257 | CPU: [*:0]const u8, | |
| 258 | Features: [*:0]const u8, | |
| 260 | CPU: ?[*:0]const u8, | |
| 261 | Features: ?[*:0]const u8, | |
| 259 | 262 | Level: CodeGenOptLevel, |
| 260 | 263 | Reloc: RelocMode, |
| 261 | CodeModel: CodeMode, | |
| 264 | CodeModel: CodeModel, | |
| 265 | function_sections: bool, | |
| 266 | float_abi: ABIType, | |
| 267 | abi_name: ?[*:0]const u8, | |
| 262 | 268 | ) *const TargetMachine; |
| 263 | 269 | |
| 264 | 270 | pub const dispose = LLVMDisposeTargetMachine; |
| 265 | 271 | extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void; |
| 266 | 272 | |
| 267 | pub const emitToFile = LLVMTargetMachineEmitToFile; | |
| 268 | extern fn LLVMTargetMachineEmitToFile( | |
| 269 | *const TargetMachine, | |
| 273 | pub const emitToFile = ZigLLVMTargetMachineEmitToFile; | |
| 274 | extern fn ZigLLVMTargetMachineEmitToFile( | |
| 275 | T: *const TargetMachine, | |
| 270 | 276 | M: *const Module, |
| 271 | Filename: [*:0]const u8, | |
| 272 | codegen: CodeGenFileType, | |
| 273 | 277 | ErrorMessage: *[*:0]const u8, |
| 274 | ) Bool; | |
| 278 | is_debug: bool, | |
| 279 | is_small: bool, | |
| 280 | time_report: bool, | |
| 281 | tsan: bool, | |
| 282 | lto: bool, | |
| 283 | asm_filename: ?[*:0]const u8, | |
| 284 | bin_filename: ?[*:0]const u8, | |
| 285 | llvm_ir_filename: ?[*:0]const u8, | |
| 286 | bitcode_filename: ?[*:0]const u8, | |
| 287 | ) bool; | |
| 275 | 288 | }; |
| 276 | 289 | |
| 277 | pub const CodeMode = enum(c_int) { | |
| 290 | pub const CodeModel = enum(c_int) { | |
| 278 | 291 | Default, |
| 279 | 292 | JITDefault, |
| 280 | 293 | Tiny, |
| ... | ... | @@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) { |
| 295 | 308 | Default, |
| 296 | 309 | Static, |
| 297 | 310 | PIC, |
| 298 | DynamicNoPic, | |
| 311 | DynamicNoPIC, | |
| 299 | 312 | ROPI, |
| 300 | 313 | RWPI, |
| 301 | 314 | ROPI_RWPI, |
| ... | ... | @@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) { |
| 306 | 319 | ObjectFile, |
| 307 | 320 | }; |
| 308 | 321 | |
| 322 | pub const ABIType = enum(c_int) { | |
| 323 | /// Target-specific (either soft or hard depending on triple, etc). | |
| 324 | Default, | |
| 325 | /// Soft float. | |
| 326 | Soft, | |
| 327 | // Hard float. | |
| 328 | Hard, | |
| 329 | }; | |
| 330 | ||
| 309 | 331 | pub const Target = opaque { |
| 310 | 332 | pub const getFromTriple = LLVMGetTargetFromTriple; |
| 311 | 333 | extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool; |
src/link.zig+7-3| ... | ... | @@ -206,7 +206,8 @@ pub const File = struct { |
| 206 | 206 | const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm |
| 207 | 207 | const sub_path = if (use_lld) blk: { |
| 208 | 208 | if (options.module == null) { |
| 209 | // No point in opening a file, we would not write anything to it. Initialize with empty. | |
| 209 | // No point in opening a file, we would not write anything to it. | |
| 210 | // Initialize with empty. | |
| 210 | 211 | return switch (options.object_format) { |
| 211 | 212 | .coff => &(try Coff.createEmpty(allocator, options)).base, |
| 212 | 213 | .elf => &(try Elf.createEmpty(allocator, options)).base, |
| ... | ... | @@ -219,8 +220,11 @@ pub const File = struct { |
| 219 | 220 | .raw => return error.RawObjectFormatUnimplemented, |
| 220 | 221 | }; |
| 221 | 222 | } |
| 222 | // Open a temporary object file, not the final output file because we want to link with LLD. | |
| 223 | break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() }); | |
| 223 | // Open a temporary object file, not the final output file because we | |
| 224 | // want to link with LLD. | |
| 225 | break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ | |
| 226 | emit.sub_path, options.object_format.fileExt(options.target.cpu.arch), | |
| 227 | }); | |
| 224 | 228 | } else emit.sub_path; |
| 225 | 229 | errdefer if (use_lld) allocator.free(sub_path); |
| 226 | 230 |
src/link/Coff.zig+11-7| ... | ... | @@ -17,9 +17,9 @@ const link = @import("../link.zig"); |
| 17 | 17 | const build_options = @import("build_options"); |
| 18 | 18 | const Cache = @import("../Cache.zig"); |
| 19 | 19 | const mingw = @import("../mingw.zig"); |
| 20 | const llvm_backend = @import("../codegen/llvm.zig"); | |
| 21 | 20 | const Air = @import("../Air.zig"); |
| 22 | 21 | const Liveness = @import("../Liveness.zig"); |
| 22 | const LlvmObject = @import("../codegen/llvm.zig").Object; | |
| 23 | 23 | |
| 24 | 24 | const allocation_padding = 4 / 3; |
| 25 | 25 | const minimum_text_block_size = 64 * allocation_padding; |
| ... | ... | @@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff; |
| 37 | 37 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 38 | 38 | |
| 39 | 39 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 40 | llvm_object: ?*llvm_backend.Object = null, | |
| 40 | llvm_object: ?*LlvmObject = null, | |
| 41 | 41 | |
| 42 | 42 | base: link.File, |
| 43 | 43 | ptr_width: PtrWidth, |
| ... | ... | @@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 132 | 132 | const self = try createEmpty(allocator, options); |
| 133 | 133 | errdefer self.base.destroy(); |
| 134 | 134 | |
| 135 | self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options); | |
| 135 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 136 | 136 | return self; |
| 137 | 137 | } |
| 138 | 138 | |
| ... | ... | @@ -820,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { |
| 820 | 820 | const tracy = trace(@src()); |
| 821 | 821 | defer tracy.end(); |
| 822 | 822 | |
| 823 | if (build_options.have_llvm) | |
| 824 | if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp); | |
| 823 | if (build_options.have_llvm) { | |
| 824 | if (self.llvm_object) |llvm_object| { | |
| 825 | return try llvm_object.flushModule(comp); | |
| 826 | } | |
| 827 | } | |
| 825 | 828 | |
| 826 | 829 | if (self.text_section_size_dirty) { |
| 827 | 830 | // Write the new raw size in the .text header |
| ... | ... | @@ -1395,8 +1398,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v |
| 1395 | 1398 | } |
| 1396 | 1399 | |
| 1397 | 1400 | pub fn deinit(self: *Coff) void { |
| 1398 | if (build_options.have_llvm) | |
| 1399 | if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator); | |
| 1401 | if (build_options.have_llvm) { | |
| 1402 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 1403 | } | |
| 1400 | 1404 | |
| 1401 | 1405 | self.text_block_free_list.deinit(self.base.allocator); |
| 1402 | 1406 | self.offset_table.deinit(self.base.allocator); |
src/link/Elf.zig+8-8| ... | ... | @@ -25,9 +25,9 @@ const target_util = @import("../target.zig"); |
| 25 | 25 | const glibc = @import("../glibc.zig"); |
| 26 | 26 | const musl = @import("../musl.zig"); |
| 27 | 27 | const Cache = @import("../Cache.zig"); |
| 28 | const llvm_backend = @import("../codegen/llvm.zig"); | |
| 29 | 28 | const Air = @import("../Air.zig"); |
| 30 | 29 | const Liveness = @import("../Liveness.zig"); |
| 30 | const LlvmObject = @import("../codegen/llvm.zig").Object; | |
| 31 | 31 | |
| 32 | 32 | const default_entry_addr = 0x8000000; |
| 33 | 33 | |
| ... | ... | @@ -38,7 +38,7 @@ base: File, |
| 38 | 38 | ptr_width: PtrWidth, |
| 39 | 39 | |
| 40 | 40 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 41 | llvm_object: ?*llvm_backend.Object = null, | |
| 41 | llvm_object: ?*LlvmObject = null, | |
| 42 | 42 | |
| 43 | 43 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 44 | 44 | /// Same order as in the file. |
| ... | ... | @@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 235 | 235 | const self = try createEmpty(allocator, options); |
| 236 | 236 | errdefer self.base.destroy(); |
| 237 | 237 | |
| 238 | self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options); | |
| 238 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 239 | 239 | return self; |
| 240 | 240 | } |
| 241 | 241 | |
| ... | ... | @@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | 303 | pub fn deinit(self: *Elf) void { |
| 304 | if (build_options.have_llvm) | |
| 305 | if (self.llvm_object) |ir_module| | |
| 306 | ir_module.deinit(self.base.allocator); | |
| 304 | if (build_options.have_llvm) { | |
| 305 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 306 | } | |
| 307 | 307 | |
| 308 | 308 | self.sections.deinit(self.base.allocator); |
| 309 | 309 | self.program_headers.deinit(self.base.allocator); |
| ... | ... | @@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 750 | 750 | if (build_options.have_llvm) |
| 751 | 751 | if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp); |
| 752 | 752 | |
| 753 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the | |
| 754 | // Zig source code. | |
| 753 | // TODO This linker code currently assumes there is only 1 compilation unit and it | |
| 754 | // corresponds to the Zig source code. | |
| 755 | 755 | const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; |
| 756 | 756 | |
| 757 | 757 | const target_endian = self.base.options.target.cpu.arch.endian(); |
src/link/MachO.zig+6-3| ... | ... | @@ -30,7 +30,7 @@ const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 30 | 30 | const Trie = @import("MachO/Trie.zig"); |
| 31 | 31 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 32 | 32 | const Zld = @import("MachO/Zld.zig"); |
| 33 | const llvm_backend = @import("../codegen/llvm.zig"); | |
| 33 | const LlvmObject = @import("../codegen/llvm.zig").Object; | |
| 34 | 34 | |
| 35 | 35 | usingnamespace @import("MachO/commands.zig"); |
| 36 | 36 | |
| ... | ... | @@ -39,7 +39,7 @@ pub const base_tag: File.Tag = File.Tag.macho; |
| 39 | 39 | base: File, |
| 40 | 40 | |
| 41 | 41 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 42 | llvm_object: ?*llvm_backend.Object = null, | |
| 42 | llvm_object: ?*LlvmObject = null, | |
| 43 | 43 | |
| 44 | 44 | /// Debug symbols bundle (or dSym). |
| 45 | 45 | d_sym: ?DebugSymbols = null, |
| ... | ... | @@ -355,7 +355,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 355 | 355 | const self = try createEmpty(allocator, options); |
| 356 | 356 | errdefer self.base.destroy(); |
| 357 | 357 | |
| 358 | self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options); | |
| 358 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 359 | 359 | return self; |
| 360 | 360 | } |
| 361 | 361 | |
| ... | ... | @@ -989,6 +989,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 989 | 989 | } |
| 990 | 990 | |
| 991 | 991 | pub fn deinit(self: *MachO) void { |
| 992 | if (build_options.have_llvm) { | |
| 993 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 994 | } | |
| 992 | 995 | if (self.d_sym) |*ds| { |
| 993 | 996 | ds.deinit(self.base.allocator); |
| 994 | 997 | } |
src/link/Wasm.zig+6-3| ... | ... | @@ -19,7 +19,7 @@ const build_options = @import("build_options"); |
| 19 | 19 | const wasi_libc = @import("../wasi_libc.zig"); |
| 20 | 20 | const Cache = @import("../Cache.zig"); |
| 21 | 21 | const TypedValue = @import("../TypedValue.zig"); |
| 22 | const llvm_backend = @import("../codegen/llvm.zig"); | |
| 22 | const LlvmObject = @import("../codegen/llvm.zig").Object; | |
| 23 | 23 | const Air = @import("../Air.zig"); |
| 24 | 24 | const Liveness = @import("../Liveness.zig"); |
| 25 | 25 | |
| ... | ... | @@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm; |
| 27 | 27 | |
| 28 | 28 | base: link.File, |
| 29 | 29 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 30 | llvm_object: ?*llvm_backend.Object = null, | |
| 30 | llvm_object: ?*LlvmObject = null, | |
| 31 | 31 | /// List of all function Decls to be written to the output file. The index of |
| 32 | 32 | /// each Decl in this list at the time of writing the binary is used as the |
| 33 | 33 | /// function index. In the event where ext_funcs' size is not 0, the index of |
| ... | ... | @@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 121 | 121 | const self = try createEmpty(allocator, options); |
| 122 | 122 | errdefer self.base.destroy(); |
| 123 | 123 | |
| 124 | self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options); | |
| 124 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 125 | 125 | return self; |
| 126 | 126 | } |
| 127 | 127 | |
| ... | ... | @@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { |
| 153 | 153 | } |
| 154 | 154 | |
| 155 | 155 | pub fn deinit(self: *Wasm) void { |
| 156 | if (build_options.have_llvm) { | |
| 157 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 158 | } | |
| 156 | 159 | for (self.symbols.items) |decl| { |
| 157 | 160 | decl.fn_link.wasm.functype.deinit(self.base.allocator); |
| 158 | 161 | decl.fn_link.wasm.code.deinit(self.base.allocator); |
src/main.zig+31-7| ... | ... | @@ -301,6 +301,8 @@ const usage_build_generic = |
| 301 | 301 | \\ -fno-emit-asm (default) Do not output .s (assembly code) |
| 302 | 302 | \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) |
| 303 | 303 | \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR |
| 304 | \\ -femit-llvm-bc[=path] Produce a LLVM module as a .bc file (requires LLVM extensions) | |
| 305 | \\ -fno-emit-llvm-bc (default) Do not produce a LLVM module as a .bc file | |
| 304 | 306 | \\ -femit-h[=path] Generate a C header file (.h) |
| 305 | 307 | \\ -fno-emit-h (default) Do not generate a C header file (.h) |
| 306 | 308 | \\ -femit-docs[=path] Create a docs/ dir with html documentation |
| ... | ... | @@ -359,7 +361,7 @@ const usage_build_generic = |
| 359 | 361 | \\ --single-threaded Code assumes it is only used single-threaded |
| 360 | 362 | \\ -ofmt=[mode] Override target object format |
| 361 | 363 | \\ elf Executable and Linking Format |
| 362 | \\ c Compile to C source code | |
| 364 | \\ c C source code | |
| 363 | 365 | \\ wasm WebAssembly |
| 364 | 366 | \\ coff Common Object File Format (Windows) |
| 365 | 367 | \\ macho macOS relocatables |
| ... | ... | @@ -551,6 +553,7 @@ fn buildOutputType( |
| 551 | 553 | var emit_bin: EmitBin = .yes_default_path; |
| 552 | 554 | var emit_asm: Emit = .no; |
| 553 | 555 | var emit_llvm_ir: Emit = .no; |
| 556 | var emit_llvm_bc: Emit = .no; | |
| 554 | 557 | var emit_docs: Emit = .no; |
| 555 | 558 | var emit_analysis: Emit = .no; |
| 556 | 559 | var target_arch_os_abi: []const u8 = "native"; |
| ... | ... | @@ -1010,6 +1013,12 @@ fn buildOutputType( |
| 1010 | 1013 | emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] }; |
| 1011 | 1014 | } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) { |
| 1012 | 1015 | emit_llvm_ir = .no; |
| 1016 | } else if (mem.eql(u8, arg, "-femit-llvm-bc")) { | |
| 1017 | emit_llvm_bc = .yes_default_path; | |
| 1018 | } else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) { | |
| 1019 | emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] }; | |
| 1020 | } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) { | |
| 1021 | emit_llvm_bc = .no; | |
| 1013 | 1022 | } else if (mem.eql(u8, arg, "-femit-docs")) { |
| 1014 | 1023 | emit_docs = .yes_default_path; |
| 1015 | 1024 | } else if (mem.startsWith(u8, arg, "-femit-docs=")) { |
| ... | ... | @@ -1815,10 +1824,10 @@ fn buildOutputType( |
| 1815 | 1824 | var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| { |
| 1816 | 1825 | switch (emit_h) { |
| 1817 | 1826 | .yes => { |
| 1818 | fatal("unable to open directory from argument 'femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) }); | |
| 1827 | fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) }); | |
| 1819 | 1828 | }, |
| 1820 | 1829 | .yes_default_path => { |
| 1821 | fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_h_basename, @errorName(err) }); | |
| 1830 | fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_h_basename, @errorName(err) }); | |
| 1822 | 1831 | }, |
| 1823 | 1832 | .no => unreachable, |
| 1824 | 1833 | } |
| ... | ... | @@ -1829,10 +1838,10 @@ fn buildOutputType( |
| 1829 | 1838 | var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| { |
| 1830 | 1839 | switch (emit_asm) { |
| 1831 | 1840 | .yes => { |
| 1832 | fatal("unable to open directory from argument 'femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) }); | |
| 1841 | fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) }); | |
| 1833 | 1842 | }, |
| 1834 | 1843 | .yes_default_path => { |
| 1835 | fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_asm_basename, @errorName(err) }); | |
| 1844 | fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_asm_basename, @errorName(err) }); | |
| 1836 | 1845 | }, |
| 1837 | 1846 | .no => unreachable, |
| 1838 | 1847 | } |
| ... | ... | @@ -1843,16 +1852,30 @@ fn buildOutputType( |
| 1843 | 1852 | var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| { |
| 1844 | 1853 | switch (emit_llvm_ir) { |
| 1845 | 1854 | .yes => { |
| 1846 | fatal("unable to open directory from argument 'femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) }); | |
| 1855 | fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) }); | |
| 1847 | 1856 | }, |
| 1848 | 1857 | .yes_default_path => { |
| 1849 | fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) }); | |
| 1858 | fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) }); | |
| 1850 | 1859 | }, |
| 1851 | 1860 | .no => unreachable, |
| 1852 | 1861 | } |
| 1853 | 1862 | }; |
| 1854 | 1863 | defer emit_llvm_ir_resolved.deinit(); |
| 1855 | 1864 | |
| 1865 | const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name}); | |
| 1866 | var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| { | |
| 1867 | switch (emit_llvm_bc) { | |
| 1868 | .yes => { | |
| 1869 | fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ emit_llvm_bc.yes, @errorName(err) }); | |
| 1870 | }, | |
| 1871 | .yes_default_path => { | |
| 1872 | fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_bc_basename, @errorName(err) }); | |
| 1873 | }, | |
| 1874 | .no => unreachable, | |
| 1875 | } | |
| 1876 | }; | |
| 1877 | defer emit_llvm_bc_resolved.deinit(); | |
| 1878 | ||
| 1856 | 1879 | const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name}); |
| 1857 | 1880 | var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| { |
| 1858 | 1881 | switch (emit_analysis) { |
| ... | ... | @@ -1988,6 +2011,7 @@ fn buildOutputType( |
| 1988 | 2011 | .emit_h = emit_h_resolved.data, |
| 1989 | 2012 | .emit_asm = emit_asm_resolved.data, |
| 1990 | 2013 | .emit_llvm_ir = emit_llvm_ir_resolved.data, |
| 2014 | .emit_llvm_bc = emit_llvm_bc_resolved.data, | |
| 1991 | 2015 | .emit_docs = emit_docs_resolved.data, |
| 1992 | 2016 | .emit_analysis = emit_analysis_resolved.data, |
| 1993 | 2017 | .link_mode = link_mode, |
src/stage1.zig+2| ... | ... | @@ -95,6 +95,8 @@ pub const Module = extern struct { |
| 95 | 95 | emit_asm_len: usize, |
| 96 | 96 | emit_llvm_ir_ptr: [*]const u8, |
| 97 | 97 | emit_llvm_ir_len: usize, |
| 98 | emit_bitcode_ptr: [*]const u8, | |
| 99 | emit_bitcode_len: usize, | |
| 98 | 100 | emit_analysis_json_ptr: [*]const u8, |
| 99 | 101 | emit_analysis_json_len: usize, |
| 100 | 102 | emit_docs_ptr: [*]const u8, |
src/stage1/all_types.hpp+1| ... | ... | @@ -2090,6 +2090,7 @@ struct CodeGen { |
| 2090 | 2090 | Buf h_file_output_path; |
| 2091 | 2091 | Buf asm_file_output_path; |
| 2092 | 2092 | Buf llvm_ir_file_output_path; |
| 2093 | Buf bitcode_file_output_path; | |
| 2093 | 2094 | Buf analysis_json_output_path; |
| 2094 | 2095 | Buf docs_output_path; |
| 2095 | 2096 |
src/stage1/codegen.cpp+11-6| ... | ... | @@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) { |
| 8506 | 8506 | const char *asm_filename = nullptr; |
| 8507 | 8507 | const char *bin_filename = nullptr; |
| 8508 | 8508 | const char *llvm_ir_filename = nullptr; |
| 8509 | const char *bitcode_filename = nullptr; | |
| 8509 | 8510 | |
| 8510 | 8511 | if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path); |
| 8511 | 8512 | if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path); |
| 8512 | 8513 | if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); |
| 8514 | if (buf_len(&g->bitcode_file_output_path) != 0) bitcode_filename = buf_ptr(&g->bitcode_file_output_path); | |
| 8513 | 8515 | |
| 8514 | // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire | |
| 8515 | // pipeline multiple times if this is requested. | |
| 8516 | // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. | |
| 8517 | // So we call the entire pipeline multiple times if this is requested. | |
| 8516 | 8518 | if (asm_filename != nullptr && bin_filename != nullptr) { |
| 8517 | 8519 | if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, |
| 8518 | 8520 | g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled, |
| 8519 | g->have_lto, nullptr, bin_filename, llvm_ir_filename)) | |
| 8521 | g->have_lto, nullptr, bin_filename, llvm_ir_filename, nullptr)) | |
| 8520 | 8522 | { |
| 8521 | fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); | |
| 8523 | fprintf(stderr, "LLVM failed to emit bin=%s, ir=%s: %s\n", | |
| 8524 | bin_filename, llvm_ir_filename, err_msg); | |
| 8522 | 8525 | exit(1); |
| 8523 | 8526 | } |
| 8524 | 8527 | bin_filename = nullptr; |
| ... | ... | @@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) { |
| 8527 | 8530 | |
| 8528 | 8531 | if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, |
| 8529 | 8532 | g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled, |
| 8530 | g->have_lto, asm_filename, bin_filename, llvm_ir_filename)) | |
| 8533 | g->have_lto, asm_filename, bin_filename, llvm_ir_filename, bitcode_filename)) | |
| 8531 | 8534 | { |
| 8532 | fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); | |
| 8535 | fprintf(stderr, "LLVM failed to emit asm=%s, bin=%s, ir=%s, bc=%s: %s\n", | |
| 8536 | asm_filename, bin_filename, llvm_ir_filename, bitcode_filename, | |
| 8537 | err_msg); | |
| 8533 | 8538 | exit(1); |
| 8534 | 8539 | } |
| 8535 | 8540 |
src/stage1/stage1.cpp+1| ... | ... | @@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { |
| 73 | 73 | buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len); |
| 74 | 74 | buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len); |
| 75 | 75 | buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len); |
| 76 | buf_init_from_mem(&g->bitcode_file_output_path, stage1->emit_bitcode_ptr, stage1->emit_bitcode_len); | |
| 76 | 77 | buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len); |
| 77 | 78 | buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len); |
| 78 | 79 |
src/stage1/stage1.h+3| ... | ... | @@ -157,6 +157,9 @@ struct ZigStage1 { |
| 157 | 157 | const char *emit_llvm_ir_ptr; |
| 158 | 158 | size_t emit_llvm_ir_len; |
| 159 | 159 | |
| 160 | const char *emit_bitcode_ptr; | |
| 161 | size_t emit_bitcode_len; | |
| 162 | ||
| 160 | 163 | const char *emit_analysis_json_ptr; |
| 161 | 164 | size_t emit_analysis_json_len; |
| 162 | 165 |
src/zig_llvm.cpp+17-2| ... | ... | @@ -229,12 +229,14 @@ struct TimeTracerRAII { |
| 229 | 229 | bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, |
| 230 | 230 | char **error_message, bool is_debug, |
| 231 | 231 | bool is_small, bool time_report, bool tsan, bool lto, |
| 232 | const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename) | |
| 232 | const char *asm_filename, const char *bin_filename, | |
| 233 | const char *llvm_ir_filename, const char *bitcode_filename) | |
| 233 | 234 | { |
| 234 | 235 | TimePassesIsEnabled = time_report; |
| 235 | 236 | |
| 236 | 237 | raw_fd_ostream *dest_asm_ptr = nullptr; |
| 237 | 238 | raw_fd_ostream *dest_bin_ptr = nullptr; |
| 239 | raw_fd_ostream *dest_bitcode_ptr = nullptr; | |
| 238 | 240 | |
| 239 | 241 | if (asm_filename) { |
| 240 | 242 | std::error_code EC; |
| ... | ... | @@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM |
| 252 | 254 | return true; |
| 253 | 255 | } |
| 254 | 256 | } |
| 257 | if (bitcode_filename) { | |
| 258 | std::error_code EC; | |
| 259 | dest_bitcode_ptr = new(std::nothrow) raw_fd_ostream(bitcode_filename, EC, sys::fs::F_None); | |
| 260 | if (EC) { | |
| 261 | *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin()); | |
| 262 | return true; | |
| 263 | } | |
| 264 | } | |
| 255 | 265 | |
| 256 | 266 | std::unique_ptr<raw_fd_ostream> dest_asm(dest_asm_ptr), |
| 257 | dest_bin(dest_bin_ptr); | |
| 267 | dest_bin(dest_bin_ptr), | |
| 268 | dest_bitcode(dest_bitcode_ptr); | |
| 269 | ||
| 258 | 270 | |
| 259 | 271 | auto PID = sys::Process::getProcessId(); |
| 260 | 272 | std::string ProcName = "zig-"; |
| ... | ... | @@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM |
| 389 | 401 | if (dest_bin && lto) { |
| 390 | 402 | WriteBitcodeToFile(module, *dest_bin); |
| 391 | 403 | } |
| 404 | if (dest_bitcode) { | |
| 405 | WriteBitcodeToFile(module, *dest_bitcode); | |
| 406 | } | |
| 392 | 407 | |
| 393 | 408 | if (time_report) { |
| 394 | 409 | TimerGroup::printAll(errs()); |
src/zig_llvm.h+2-1| ... | ... | @@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); |
| 49 | 49 | ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, |
| 50 | 50 | char **error_message, bool is_debug, |
| 51 | 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, | |
| 53 | const char *llvm_ir_filename, const char *bitcode_filename); | |
| 53 | 54 | |
| 54 | 55 | |
| 55 | 56 | enum ZigLLVMABIType { |