authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-22 14:44:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-22 19:51:32-07:00
loga5fb28070f37c2cad92ac8805bcc704e872fc538
tree05e8432688cd83fcb4989a13087c01c74df172c5
parent36295d712fbd561c3de9b3eb46e776d63e646e9a

add -femit-llvm-bc CLI option and implement it

* 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,5 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = std.builtin;2const builtin = @import("builtin");
3const io = std.io;3const io = std.io;
4const fs = std.fs;4const fs = std.fs;
5const process = std.process;5const process = std.process;
...@@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator;...@@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator;
13const max_doc_file_size = 10 * 1024 * 1024;13const max_doc_file_size = 10 * 1024 * 1024;
1414
15const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt();15const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt();
16const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt();16const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
17const tmp_dir_name = "docgen_tmp";17const tmp_dir_name = "docgen_tmp";
18const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext;18const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext;
1919
...@@ -281,7 +281,7 @@ const Code = struct {...@@ -281,7 +281,7 @@ const Code = struct {
281 name: []const u8,281 name: []const u8,
282 source_token: Token,282 source_token: Token,
283 is_inline: bool,283 is_inline: bool,
284 mode: builtin.Mode,284 mode: std.builtin.Mode,
285 link_objects: []const []const u8,285 link_objects: []const []const u8,
286 target_str: ?[]const u8,286 target_str: ?[]const u8,
287 link_libc: bool,287 link_libc: bool,
...@@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc {...@@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc {
531 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});531 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
532 }532 }
533533
534 var mode: builtin.Mode = .Debug;534 var mode: std.builtin.Mode = .Debug;
535 var link_objects = std.ArrayList([]const u8).init(allocator);535 var link_objects = std.ArrayList([]const u8).init(allocator);
536 defer link_objects.deinit();536 defer link_objects.deinit();
537 var target_str: ?[]const u8 = null;537 var target_str: ?[]const u8 = null;
lib/std/target.zig+63-36
...@@ -549,15 +549,36 @@ pub const Target = struct {...@@ -549,15 +549,36 @@ pub const Target = struct {
549 };549 };
550550
551 pub const ObjectFormat = enum {551 pub const ObjectFormat = enum {
552 /// Common Object File Format (Windows)
552 coff,553 coff,
554 /// Executable and Linking Format
553 elf,555 elf,
556 /// macOS relocatables
554 macho,557 macho,
558 /// WebAssembly
555 wasm,559 wasm,
560 /// C source code
556 c,561 c,
562 /// Standard, Portable Intermediate Representation V
557 spirv,563 spirv,
564 /// Intel IHEX
558 hex,565 hex,
566 /// Machine code with no metadata.
559 raw,567 raw,
568 /// Plan 9 from Bell Labs
560 plan9,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 };
562583
563 pub const SubSystem = enum {584 pub const SubSystem = enum {
...@@ -1289,30 +1310,16 @@ pub const Target = struct {...@@ -1289,30 +1310,16 @@ pub const Target = struct {
1289 return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);1310 return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
1290 }1311 }
12911312
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 pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {1313 pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {
1307 switch (os_tag) {1314 return switch (os_tag) {
1308 .windows => return ".exe",1315 .windows => ".exe",
1309 .uefi => return ".efi",1316 .uefi => ".efi",
1310 else => if (cpu_arch.isWasm()) {1317 .plan9 => plan9Ext(cpu_arch),
1311 return ".wasm";1318 else => switch (cpu_arch) {
1312 } else {1319 .wasm32, .wasm64 => ".wasm",
1313 return "";1320 else => "",
1314 },1321 },
1315 }1322 };
1316 }1323 }
13171324
1318 pub fn exeFileExt(self: Target) [:0]const u8 {1325 pub fn exeFileExt(self: Target) [:0]const u8 {
...@@ -1352,20 +1359,16 @@ pub const Target = struct {...@@ -1352,20 +1359,16 @@ pub const Target = struct {
1352 }1359 }
13531360
1354 pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat {1361 pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat {
1355 if (os_tag == .windows or os_tag == .uefi) {1362 return switch (os_tag) {
1356 return .coff;1363 .windows, .uefi => .coff,
1357 } else if (os_tag.isDarwin()) {1364 .ios, .macos, .watchos, .tvos => .macho,
1358 return .macho;1365 .plan9 => .plan9,
1359 }1366 else => return switch (cpu_arch) {
1360 if (cpu_arch.isWasm()) {1367 .wasm32, .wasm64 => .wasm,
1361 return .wasm;1368 .spirv32, .spirv64 => .spirv,
1362 }1369 else => .elf,
1363 if (cpu_arch.isSPIRV()) {1370 },
1364 return .spirv;1371 };
1365 }
1366 if (os_tag == .plan9)
1367 return .plan9;
1368 return .elf;
1369 }1372 }
13701373
1371 pub fn getObjectFormat(self: Target) ObjectFormat {1374 pub fn getObjectFormat(self: Target) ObjectFormat {
...@@ -1676,6 +1679,30 @@ pub const Target = struct {...@@ -1676,6 +1679,30 @@ pub const Target = struct {
16761679
1677 return false;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};
16801707
1681test {1708test {
lib/std/zig.zig+9-29
...@@ -108,7 +108,8 @@ pub const BinNameOptions = struct {...@@ -108,7 +108,8 @@ pub const BinNameOptions = struct {
108pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {108pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
109 const root_name = options.root_name;109 const root_name = options.root_name;
110 const target = options.target;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 .coff => switch (options.output_mode) {113 .coff => switch (options.output_mode) {
113 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),114 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
114 .Lib => {115 .Lib => {
...@@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro...@@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
118 };119 };
119 return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });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 .elf => switch (options.output_mode) {124 .elf => switch (options.output_mode) {
124 .Exe => return allocator.dupe(u8, root_name),125 .Exe => return allocator.dupe(u8, root_name),
...@@ -140,7 +141,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro...@@ -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 .macho => switch (options.output_mode) {146 .macho => switch (options.output_mode) {
146 .Exe => return allocator.dupe(u8, root_name),147 .Exe => return allocator.dupe(u8, root_name),
...@@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro...@@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
163 }164 }
164 return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });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 .wasm => switch (options.output_mode) {169 .wasm => switch (options.output_mode) {
169 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),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,36 +176,15 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
175 .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),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 .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),181 .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
181 .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),182 .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
182 .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),183 .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
183 .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),184 .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
184 .plan9 => {185 .plan9 => return std.fmt.allocPrint(allocator, "{s}{s}", .{
185 // copied from 2c(1)186 root_name, ofmt.fileExt(target.cpu.arch),
186 // 0c spim little-endian MIPS 3000 family187 }),
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 },
208 }188 }
209}189}
210190
lib/std/zig/cross_target.zig-4
...@@ -473,10 +473,6 @@ pub const CrossTarget = struct {...@@ -473,10 +473,6 @@ pub const CrossTarget = struct {
473 return self.getOsTag() == .windows;473 return self.getOsTag() == .windows;
474 }474 }
475475
476 pub fn oFileExt(self: CrossTarget) [:0]const u8 {
477 return Target.oFileExt_os_abi(self.getOsTag(), self.getAbi());
478 }
479
480 pub fn exeFileExt(self: CrossTarget) [:0]const u8 {476 pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
481 return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());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,6 +143,7 @@ debug_compiler_runtime_libs: bool,
143143
144emit_asm: ?EmitLoc,144emit_asm: ?EmitLoc,
145emit_llvm_ir: ?EmitLoc,145emit_llvm_ir: ?EmitLoc,
146emit_llvm_bc: ?EmitLoc,
146emit_analysis: ?EmitLoc,147emit_analysis: ?EmitLoc,
147emit_docs: ?EmitLoc,148emit_docs: ?EmitLoc,
148149
...@@ -586,6 +587,17 @@ pub const Directory = struct {...@@ -586,6 +587,17 @@ pub const Directory = struct {
586 return std.fs.path.join(allocator, paths);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};
590602
591pub const EmitLoc = struct {603pub const EmitLoc = struct {
...@@ -623,6 +635,8 @@ pub const InitOptions = struct {...@@ -623,6 +635,8 @@ pub const InitOptions = struct {
623 emit_asm: ?EmitLoc = null,635 emit_asm: ?EmitLoc = null,
624 /// `null` means to not emit LLVM IR.636 /// `null` means to not emit LLVM IR.
625 emit_llvm_ir: ?EmitLoc = null,637 emit_llvm_ir: ?EmitLoc = null,
638 /// `null` means to not emit LLVM module bitcode.
639 emit_llvm_bc: ?EmitLoc = null,
626 /// `null` means to not emit semantic analysis JSON.640 /// `null` means to not emit semantic analysis JSON.
627 emit_analysis: ?EmitLoc = null,641 emit_analysis: ?EmitLoc = null,
628 /// `null` means to not emit docs.642 /// `null` means to not emit docs.
...@@ -819,6 +833,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -819,6 +833,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
819 break :blk false;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 break :blk build_options.is_stage1;840 break :blk build_options.is_stage1;
823 };841 };
824842
...@@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
835 if (ofmt == .c)853 if (ofmt == .c)
836 break :blk false;854 break :blk false;
837855
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 // The stage1 compiler depends on the stage1 C++ LLVM backend860 // The stage1 compiler depends on the stage1 C++ LLVM backend
839 // to compile zig code.861 // to compile zig code.
840 if (use_stage1)862 if (use_stage1)
...@@ -853,6 +875,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -853,6 +875,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
853 if (options.machine_code_model != .default) {875 if (options.machine_code_model != .default) {
854 return error.MachineCodeModelNotSupportedWithoutLlvm;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 }
857885
858 const tsan = options.want_tsan orelse false;886 const tsan = options.want_tsan orelse false;
...@@ -1381,6 +1409,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1381,6 +1409,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1381 .bin_file = bin_file,1409 .bin_file = bin_file,
1382 .emit_asm = options.emit_asm,1410 .emit_asm = options.emit_asm,
1383 .emit_llvm_ir = options.emit_llvm_ir,1411 .emit_llvm_ir = options.emit_llvm_ir,
1412 .emit_llvm_bc = options.emit_llvm_bc,
1384 .emit_analysis = options.emit_analysis,1413 .emit_analysis = options.emit_analysis,
1385 .emit_docs = options.emit_docs,1414 .emit_docs = options.emit_docs,
1386 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),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,7 +2757,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2728 comp.bin_file.options.root_name2757 comp.bin_file.options.root_name
2729 else2758 else
2730 c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];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 });
27322764
2733 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {2765 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
2734 var argv = std.ArrayList([]const u8).init(comp.gpa);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,6 +4010,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3978 }4010 }
3979 man.hash.addOptionalEmitLoc(comp.emit_asm);4011 man.hash.addOptionalEmitLoc(comp.emit_asm);
3980 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);4012 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
4013 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
3981 man.hash.addOptionalEmitLoc(comp.emit_analysis);4014 man.hash.addOptionalEmitLoc(comp.emit_analysis);
3982 man.hash.addOptionalEmitLoc(comp.emit_docs);4015 man.hash.addOptionalEmitLoc(comp.emit_docs);
3983 man.hash.add(comp.test_evented_io);4016 man.hash.add(comp.test_evented_io);
...@@ -4083,13 +4116,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -4083,13 +4116,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4083 ) orelse return error.OutOfMemory;4116 ) orelse return error.OutOfMemory;
40844117
4085 const emit_bin_path = if (comp.bin_file.options.emit != null) blk: {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 .root_name = comp.bin_file.options.root_name,4120 .root_name = comp.bin_file.options.root_name,
4088 .target = target,4121 .target = target,
4089 .output_mode = .Obj,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 } else "";4125 } else "";
4126
4093 if (mod.emit_h != null) {4127 if (mod.emit_h != null) {
4094 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});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,6 +4131,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4097 const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory);4131 const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory);
4098 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);4132 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
4099 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);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 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);4135 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
4101 const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);4136 const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);
4102 const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);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,6 +4152,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4117 .emit_asm_len = emit_asm_path.len,4152 .emit_asm_len = emit_asm_path.len,
4118 .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,4153 .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,
4119 .emit_llvm_ir_len = emit_llvm_ir_path.len,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 .emit_analysis_json_ptr = emit_analysis_path.ptr,4157 .emit_analysis_json_ptr = emit_analysis_path.ptr,
4121 .emit_analysis_json_len = emit_analysis_path.len,4158 .emit_analysis_json_len = emit_analysis_path.len,
4122 .emit_docs_ptr = emit_docs_path.ptr,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,9 +72,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
72 .renderscript32 => "renderscript32",72 .renderscript32 => "renderscript32",
73 .renderscript64 => "renderscript64",73 .renderscript64 => "renderscript64",
74 .ve => "ve",74 .ve => "ve",
75 .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,75 .spu_2 => return error.@"LLVM backend does not support SPU Mark II",
76 .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV,76 .spirv32 => return error.@"LLVM backend does not support SPIR-V",
77 .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV,77 .spirv64 => return error.@"LLVM backend does not support SPIR-V",
78 };78 };
7979
80 const llvm_os = switch (target.os.tag) {80 const llvm_os = switch (target.os.tag) {
...@@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {...@@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
114 .wasi => "wasi",114 .wasi => "wasi",
115 .emscripten => "emscripten",115 .emscripten => "emscripten",
116 .uefi => "windows",116 .uefi => "windows",
117 .opencl => return error.LLVMBackendDoesNotSupportOpenCL,117
118 .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450,118 .opencl,
119 .vulkan => return error.LLVMBackendDoesNotSupportVulkan,119 .glsl450,
120 .plan9 => return error.LLVMBackendDoesNotSupportPlan9,120 .vulkan,
121 .other => "unknown",121 .plan9,
122 .other,
123 => "unknown",
122 };124 };
123125
124 const llvm_abi = switch (target.abi) {126 const llvm_abi = switch (target.abi) {
...@@ -152,84 +154,105 @@ pub const Object = struct {...@@ -152,84 +154,105 @@ pub const Object = struct {
152 llvm_module: *const llvm.Module,154 llvm_module: *const llvm.Module,
153 context: *const llvm.Context,155 context: *const llvm.Context,
154 target_machine: *const llvm.TargetMachine,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);
168157
169 const o_directory = options.module.?.zig_cache_artifact_directory;158 pub fn create(gpa: *Allocator, options: link.Options) !*Object {
170 const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename});159 const obj = try gpa.create(Object);
171 defer allocator.free(object_path);160 errdefer gpa.destroy(obj);
172161 obj.* = try Object.init(gpa, options);
173 const object_pathZ = try allocator.dupeZ(u8, object_path);162 return obj;
174 errdefer allocator.free(object_pathZ);163 }
175164
165 pub fn init(gpa: *Allocator, options: link.Options) !Object {
176 const context = llvm.Context.create();166 const context = llvm.Context.create();
177 errdefer context.dispose();167 errdefer context.dispose();
178168
179 initializeLLVMTargets();169 initializeLLVMTargets();
180170
181 const root_nameZ = try allocator.dupeZ(u8, options.root_name);171 const root_nameZ = try gpa.dupeZ(u8, options.root_name);
182 defer allocator.free(root_nameZ);172 defer gpa.free(root_nameZ);
183 const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);173 const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
184 errdefer llvm_module.dispose();174 errdefer llvm_module.dispose();
185175
186 const llvm_target_triple = try targetTriple(allocator, options.target);176 const llvm_target_triple = try targetTriple(gpa, options.target);
187 defer allocator.free(llvm_target_triple);177 defer gpa.free(llvm_target_triple);
188178
189 var error_message: [*:0]const u8 = undefined;179 var error_message: [*:0]const u8 = undefined;
190 var target: *const llvm.Target = undefined;180 var target: *const llvm.Target = undefined;
191 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {181 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
192 defer llvm.disposeMessage(error_message);182 defer llvm.disposeMessage(error_message);
193183
194 const stderr = std.io.getStdErr().writer();184 log.err("LLVM failed to parse '{s}': {s}", .{ llvm_target_triple, error_message });
195 try stderr.print(185 return error.InvalidLlvmTriple;
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;
203 }186 }
204187
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 const target_machine = llvm.TargetMachine.create(225 const target_machine = llvm.TargetMachine.create(
207 target,226 target,
208 llvm_target_triple.ptr,227 llvm_target_triple.ptr,
209 "",228 if (options.target.cpu.model.llvm_name) |s| s.ptr else null,
210 "",229 options.llvm_cpu_features,
211 opt_level,230 opt_level,
212 .Static,231 reloc_mode,
213 .Default,232 code_model,
233 options.function_sections,
234 float_abi,
235 abi_name,
214 );236 );
215 errdefer target_machine.dispose();237 errdefer target_machine.dispose();
216238
217 self.* = .{239 return Object{
218 .llvm_module = llvm_module,240 .llvm_module = llvm_module,
219 .context = context,241 .context = context,
220 .target_machine = target_machine,242 .target_machine = target_machine,
221 .object_pathZ = object_pathZ,
222 };243 };
223 return self;
224 }244 }
225245
226 pub fn deinit(self: *Object, allocator: *Allocator) void {246 pub fn deinit(self: *Object) void {
227 self.target_machine.dispose();247 self.target_machine.dispose();
228 self.llvm_module.dispose();248 self.llvm_module.dispose();
229 self.context.dispose();249 self.context.dispose();
250 self.* = undefined;
251 }
230252
231 allocator.free(self.object_pathZ);253 pub fn destroy(self: *Object, gpa: *Allocator) void {
232 allocator.destroy(self);254 self.deinit();
255 gpa.destroy(self);
233 }256 }
234257
235 fn initializeLLVMTargets() void {258 fn initializeLLVMTargets() void {
...@@ -240,38 +263,81 @@ pub const Object = struct {...@@ -240,38 +263,81 @@ pub const Object = struct {
240 llvm.initializeAllAsmParsers();263 llvm.initializeAllAsmParsers();
241 }264 }
242265
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 pub fn flushModule(self: *Object, comp: *Compilation) !void {277 pub fn flushModule(self: *Object, comp: *Compilation) !void {
244 if (comp.verbose_llvm_ir) {278 if (comp.verbose_llvm_ir) {
245 const dump = self.llvm_module.printToString();279 self.llvm_module.dump();
246 defer llvm.disposeMessage(dump);
247
248 const stderr = std.io.getStdErr().writer();
249 try stderr.writeAll(std.mem.spanZ(dump));
250 }280 }
251281
252 {282 if (std.debug.runtime_safety) {
253 var error_message: [*:0]const u8 = undefined;283 var error_message: [*:0]const u8 = undefined;
254 // verifyModule always allocs the error_message even if there is no error284 // verifyModule always allocs the error_message even if there is no error
255 defer llvm.disposeMessage(error_message);285 defer llvm.disposeMessage(error_message);
256286
257 if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {287 if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
258 const stderr = std.io.getStdErr().writer();288 std.debug.print("\n{s}\n", .{error_message});
259 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});289 @panic("LLVM module verification failed");
260 return error.BrokenLLVMModule;
261 }290 }
262 }291 }
263292
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 var error_message: [*:0]const u8 = undefined;317 var error_message: [*:0]const u8 = undefined;
265 if (self.target_machine.emitToFile(318 if (self.target_machine.emitToFile(
266 self.llvm_module,319 self.llvm_module,
267 self.object_pathZ.ptr,
268 .ObjectFile,
269 &error_message,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 defer llvm.disposeMessage(error_message);331 defer llvm.disposeMessage(error_message);
272332
273 const stderr = std.io.getStdErr().writer();333 const emit_asm_msg = emit_asm_path orelse "(none)";
274 try stderr.print("LLVM failed to emit file: {s}\n", .{error_message});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 return error.FailedToEmit;341 return error.FailedToEmit;
276 }342 }
277 }343 }
src/codegen/llvm/bindings.zig+35-13
...@@ -123,6 +123,9 @@ pub const Module = opaque {...@@ -123,6 +123,9 @@ pub const Module = opaque {
123123
124 pub const getNamedGlobal = LLVMGetNamedGlobal;124 pub const getNamedGlobal = LLVMGetNamedGlobal;
125 extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;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};
127130
128pub const lookupIntrinsicID = LLVMLookupIntrinsicID;131pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
...@@ -250,31 +253,41 @@ pub const BasicBlock = opaque {...@@ -250,31 +253,41 @@ pub const BasicBlock = opaque {
250};253};
251254
252pub const TargetMachine = opaque {255pub const TargetMachine = opaque {
253 pub const create = LLVMCreateTargetMachine;256 pub const create = ZigLLVMCreateTargetMachine;
254 extern fn LLVMCreateTargetMachine(257 extern fn ZigLLVMCreateTargetMachine(
255 T: *const Target,258 T: *const Target,
256 Triple: [*:0]const u8,259 Triple: [*:0]const u8,
257 CPU: [*:0]const u8,260 CPU: ?[*:0]const u8,
258 Features: [*:0]const u8,261 Features: ?[*:0]const u8,
259 Level: CodeGenOptLevel,262 Level: CodeGenOptLevel,
260 Reloc: RelocMode,263 Reloc: RelocMode,
261 CodeModel: CodeMode,264 CodeModel: CodeModel,
265 function_sections: bool,
266 float_abi: ABIType,
267 abi_name: ?[*:0]const u8,
262 ) *const TargetMachine;268 ) *const TargetMachine;
263269
264 pub const dispose = LLVMDisposeTargetMachine;270 pub const dispose = LLVMDisposeTargetMachine;
265 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;271 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
266272
267 pub const emitToFile = LLVMTargetMachineEmitToFile;273 pub const emitToFile = ZigLLVMTargetMachineEmitToFile;
268 extern fn LLVMTargetMachineEmitToFile(274 extern fn ZigLLVMTargetMachineEmitToFile(
269 *const TargetMachine,275 T: *const TargetMachine,
270 M: *const Module,276 M: *const Module,
271 Filename: [*:0]const u8,
272 codegen: CodeGenFileType,
273 ErrorMessage: *[*:0]const u8,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};
276289
277pub const CodeMode = enum(c_int) {290pub const CodeModel = enum(c_int) {
278 Default,291 Default,
279 JITDefault,292 JITDefault,
280 Tiny,293 Tiny,
...@@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) {...@@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) {
295 Default,308 Default,
296 Static,309 Static,
297 PIC,310 PIC,
298 DynamicNoPic,311 DynamicNoPIC,
299 ROPI,312 ROPI,
300 RWPI,313 RWPI,
301 ROPI_RWPI,314 ROPI_RWPI,
...@@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) {...@@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) {
306 ObjectFile,319 ObjectFile,
307};320};
308321
322pub 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
309pub const Target = opaque {331pub const Target = opaque {
310 pub const getFromTriple = LLVMGetTargetFromTriple;332 pub const getFromTriple = LLVMGetTargetFromTriple;
311 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;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,7 +206,8 @@ pub const File = struct {
206 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm206 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
207 const sub_path = if (use_lld) blk: {207 const sub_path = if (use_lld) blk: {
208 if (options.module == null) {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 return switch (options.object_format) {211 return switch (options.object_format) {
211 .coff => &(try Coff.createEmpty(allocator, options)).base,212 .coff => &(try Coff.createEmpty(allocator, options)).base,
212 .elf => &(try Elf.createEmpty(allocator, options)).base,213 .elf => &(try Elf.createEmpty(allocator, options)).base,
...@@ -219,8 +220,11 @@ pub const File = struct {...@@ -219,8 +220,11 @@ pub const File = struct {
219 .raw => return error.RawObjectFormatUnimplemented,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 // Open a temporary object file, not the final output file because we
223 break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() });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 } else emit.sub_path;228 } else emit.sub_path;
225 errdefer if (use_lld) allocator.free(sub_path);229 errdefer if (use_lld) allocator.free(sub_path);
226230
src/link/Coff.zig+11-7
...@@ -17,9 +17,9 @@ const link = @import("../link.zig");...@@ -17,9 +17,9 @@ const link = @import("../link.zig");
17const build_options = @import("build_options");17const build_options = @import("build_options");
18const Cache = @import("../Cache.zig");18const Cache = @import("../Cache.zig");
19const mingw = @import("../mingw.zig");19const mingw = @import("../mingw.zig");
20const llvm_backend = @import("../codegen/llvm.zig");
21const Air = @import("../Air.zig");20const Air = @import("../Air.zig");
22const Liveness = @import("../Liveness.zig");21const Liveness = @import("../Liveness.zig");
22const LlvmObject = @import("../codegen/llvm.zig").Object;
2323
24const allocation_padding = 4 / 3;24const allocation_padding = 4 / 3;
25const minimum_text_block_size = 64 * allocation_padding;25const minimum_text_block_size = 64 * allocation_padding;
...@@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff;...@@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff;
37const msdos_stub = @embedFile("msdos-stub.bin");37const msdos_stub = @embedFile("msdos-stub.bin");
3838
39/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.39/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
40llvm_object: ?*llvm_backend.Object = null,40llvm_object: ?*LlvmObject = null,
4141
42base: link.File,42base: link.File,
43ptr_width: PtrWidth,43ptr_width: PtrWidth,
...@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
132 const self = try createEmpty(allocator, options);132 const self = try createEmpty(allocator, options);
133 errdefer self.base.destroy();133 errdefer self.base.destroy();
134134
135 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);135 self.llvm_object = try LlvmObject.create(allocator, options);
136 return self;136 return self;
137 }137 }
138138
...@@ -820,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {...@@ -820,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
820 const tracy = trace(@src());820 const tracy = trace(@src());
821 defer tracy.end();821 defer tracy.end();
822822
823 if (build_options.have_llvm)823 if (build_options.have_llvm) {
824 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);824 if (self.llvm_object) |llvm_object| {
825 return try llvm_object.flushModule(comp);
826 }
827 }
825828
826 if (self.text_section_size_dirty) {829 if (self.text_section_size_dirty) {
827 // Write the new raw size in the .text header830 // 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,8 +1398,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
1395}1398}
13961399
1397pub fn deinit(self: *Coff) void {1400pub fn deinit(self: *Coff) void {
1398 if (build_options.have_llvm)1401 if (build_options.have_llvm) {
1399 if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator);1402 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
1403 }
14001404
1401 self.text_block_free_list.deinit(self.base.allocator);1405 self.text_block_free_list.deinit(self.base.allocator);
1402 self.offset_table.deinit(self.base.allocator);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,9 +25,9 @@ const target_util = @import("../target.zig");
25const glibc = @import("../glibc.zig");25const glibc = @import("../glibc.zig");
26const musl = @import("../musl.zig");26const musl = @import("../musl.zig");
27const Cache = @import("../Cache.zig");27const Cache = @import("../Cache.zig");
28const llvm_backend = @import("../codegen/llvm.zig");
29const Air = @import("../Air.zig");28const Air = @import("../Air.zig");
30const Liveness = @import("../Liveness.zig");29const Liveness = @import("../Liveness.zig");
30const LlvmObject = @import("../codegen/llvm.zig").Object;
3131
32const default_entry_addr = 0x8000000;32const default_entry_addr = 0x8000000;
3333
...@@ -38,7 +38,7 @@ base: File,...@@ -38,7 +38,7 @@ base: File,
38ptr_width: PtrWidth,38ptr_width: PtrWidth,
3939
40/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.40/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
41llvm_object: ?*llvm_backend.Object = null,41llvm_object: ?*LlvmObject = null,
4242
43/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.43/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
44/// Same order as in the file.44/// Same order as in the file.
...@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
235 const self = try createEmpty(allocator, options);235 const self = try createEmpty(allocator, options);
236 errdefer self.base.destroy();236 errdefer self.base.destroy();
237237
238 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);238 self.llvm_object = try LlvmObject.create(allocator, options);
239 return self;239 return self;
240 }240 }
241241
...@@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {...@@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
301}301}
302302
303pub fn deinit(self: *Elf) void {303pub fn deinit(self: *Elf) void {
304 if (build_options.have_llvm)304 if (build_options.have_llvm) {
305 if (self.llvm_object) |ir_module|305 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
306 ir_module.deinit(self.base.allocator);306 }
307307
308 self.sections.deinit(self.base.allocator);308 self.sections.deinit(self.base.allocator);
309 self.program_headers.deinit(self.base.allocator);309 self.program_headers.deinit(self.base.allocator);
...@@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
750 if (build_options.have_llvm)750 if (build_options.have_llvm)
751 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);751 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
752752
753 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the753 // TODO This linker code currently assumes there is only 1 compilation unit and it
754 // Zig source code.754 // corresponds to the Zig source code.
755 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;755 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
756756
757 const target_endian = self.base.options.target.cpu.arch.endian();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,7 +30,7 @@ const DebugSymbols = @import("MachO/DebugSymbols.zig");
30const Trie = @import("MachO/Trie.zig");30const Trie = @import("MachO/Trie.zig");
31const CodeSignature = @import("MachO/CodeSignature.zig");31const CodeSignature = @import("MachO/CodeSignature.zig");
32const Zld = @import("MachO/Zld.zig");32const Zld = @import("MachO/Zld.zig");
33const llvm_backend = @import("../codegen/llvm.zig");33const LlvmObject = @import("../codegen/llvm.zig").Object;
3434
35usingnamespace @import("MachO/commands.zig");35usingnamespace @import("MachO/commands.zig");
3636
...@@ -39,7 +39,7 @@ pub const base_tag: File.Tag = File.Tag.macho;...@@ -39,7 +39,7 @@ pub const base_tag: File.Tag = File.Tag.macho;
39base: File,39base: File,
4040
41/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.41/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
42llvm_object: ?*llvm_backend.Object = null,42llvm_object: ?*LlvmObject = null,
4343
44/// Debug symbols bundle (or dSym).44/// Debug symbols bundle (or dSym).
45d_sym: ?DebugSymbols = null,45d_sym: ?DebugSymbols = null,
...@@ -355,7 +355,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -355,7 +355,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
355 const self = try createEmpty(allocator, options);355 const self = try createEmpty(allocator, options);
356 errdefer self.base.destroy();356 errdefer self.base.destroy();
357357
358 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);358 self.llvm_object = try LlvmObject.create(allocator, options);
359 return self;359 return self;
360 }360 }
361361
...@@ -989,6 +989,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {...@@ -989,6 +989,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
989}989}
990990
991pub fn deinit(self: *MachO) void {991pub 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 if (self.d_sym) |*ds| {995 if (self.d_sym) |*ds| {
993 ds.deinit(self.base.allocator);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,7 +19,7 @@ const build_options = @import("build_options");
19const wasi_libc = @import("../wasi_libc.zig");19const wasi_libc = @import("../wasi_libc.zig");
20const Cache = @import("../Cache.zig");20const Cache = @import("../Cache.zig");
21const TypedValue = @import("../TypedValue.zig");21const TypedValue = @import("../TypedValue.zig");
22const llvm_backend = @import("../codegen/llvm.zig");22const LlvmObject = @import("../codegen/llvm.zig").Object;
23const Air = @import("../Air.zig");23const Air = @import("../Air.zig");
24const Liveness = @import("../Liveness.zig");24const Liveness = @import("../Liveness.zig");
2525
...@@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm;...@@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm;
2727
28base: link.File,28base: link.File,
29/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.29/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
30llvm_object: ?*llvm_backend.Object = null,30llvm_object: ?*LlvmObject = null,
31/// List of all function Decls to be written to the output file. The index of31/// List of all function Decls to be written to the output file. The index of
32/// each Decl in this list at the time of writing the binary is used as the32/// each Decl in this list at the time of writing the binary is used as the
33/// function index. In the event where ext_funcs' size is not 0, the index of33/// 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,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
121 const self = try createEmpty(allocator, options);121 const self = try createEmpty(allocator, options);
122 errdefer self.base.destroy();122 errdefer self.base.destroy();
123123
124 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);124 self.llvm_object = try LlvmObject.create(allocator, options);
125 return self;125 return self;
126 }126 }
127127
...@@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {...@@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
153}153}
154154
155pub fn deinit(self: *Wasm) void {155pub 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 for (self.symbols.items) |decl| {159 for (self.symbols.items) |decl| {
157 decl.fn_link.wasm.functype.deinit(self.base.allocator);160 decl.fn_link.wasm.functype.deinit(self.base.allocator);
158 decl.fn_link.wasm.code.deinit(self.base.allocator);161 decl.fn_link.wasm.code.deinit(self.base.allocator);
src/main.zig+31-7
...@@ -301,6 +301,8 @@ const usage_build_generic =...@@ -301,6 +301,8 @@ const usage_build_generic =
301 \\ -fno-emit-asm (default) Do not output .s (assembly code)301 \\ -fno-emit-asm (default) Do not output .s (assembly code)
302 \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)302 \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)
303 \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR303 \\ -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 \\ -femit-h[=path] Generate a C header file (.h)306 \\ -femit-h[=path] Generate a C header file (.h)
305 \\ -fno-emit-h (default) Do not generate a C header file (.h)307 \\ -fno-emit-h (default) Do not generate a C header file (.h)
306 \\ -femit-docs[=path] Create a docs/ dir with html documentation308 \\ -femit-docs[=path] Create a docs/ dir with html documentation
...@@ -359,7 +361,7 @@ const usage_build_generic =...@@ -359,7 +361,7 @@ const usage_build_generic =
359 \\ --single-threaded Code assumes it is only used single-threaded361 \\ --single-threaded Code assumes it is only used single-threaded
360 \\ -ofmt=[mode] Override target object format362 \\ -ofmt=[mode] Override target object format
361 \\ elf Executable and Linking Format363 \\ elf Executable and Linking Format
362 \\ c Compile to C source code364 \\ c C source code
363 \\ wasm WebAssembly365 \\ wasm WebAssembly
364 \\ coff Common Object File Format (Windows)366 \\ coff Common Object File Format (Windows)
365 \\ macho macOS relocatables367 \\ macho macOS relocatables
...@@ -551,6 +553,7 @@ fn buildOutputType(...@@ -551,6 +553,7 @@ fn buildOutputType(
551 var emit_bin: EmitBin = .yes_default_path;553 var emit_bin: EmitBin = .yes_default_path;
552 var emit_asm: Emit = .no;554 var emit_asm: Emit = .no;
553 var emit_llvm_ir: Emit = .no;555 var emit_llvm_ir: Emit = .no;
556 var emit_llvm_bc: Emit = .no;
554 var emit_docs: Emit = .no;557 var emit_docs: Emit = .no;
555 var emit_analysis: Emit = .no;558 var emit_analysis: Emit = .no;
556 var target_arch_os_abi: []const u8 = "native";559 var target_arch_os_abi: []const u8 = "native";
...@@ -1010,6 +1013,12 @@ fn buildOutputType(...@@ -1010,6 +1013,12 @@ fn buildOutputType(
1010 emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };1013 emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
1011 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {1014 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
1012 emit_llvm_ir = .no;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 } else if (mem.eql(u8, arg, "-femit-docs")) {1022 } else if (mem.eql(u8, arg, "-femit-docs")) {
1014 emit_docs = .yes_default_path;1023 emit_docs = .yes_default_path;
1015 } else if (mem.startsWith(u8, arg, "-femit-docs=")) {1024 } else if (mem.startsWith(u8, arg, "-femit-docs=")) {
...@@ -1815,10 +1824,10 @@ fn buildOutputType(...@@ -1815,10 +1824,10 @@ fn buildOutputType(
1815 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {1824 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
1816 switch (emit_h) {1825 switch (emit_h) {
1817 .yes => {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 .yes_default_path => {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 .no => unreachable,1832 .no => unreachable,
1824 }1833 }
...@@ -1829,10 +1838,10 @@ fn buildOutputType(...@@ -1829,10 +1838,10 @@ fn buildOutputType(
1829 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {1838 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
1830 switch (emit_asm) {1839 switch (emit_asm) {
1831 .yes => {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 .yes_default_path => {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 .no => unreachable,1846 .no => unreachable,
1838 }1847 }
...@@ -1843,16 +1852,30 @@ fn buildOutputType(...@@ -1843,16 +1852,30 @@ fn buildOutputType(
1843 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {1852 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
1844 switch (emit_llvm_ir) {1853 switch (emit_llvm_ir) {
1845 .yes => {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 .yes_default_path => {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 .no => unreachable,1860 .no => unreachable,
1852 }1861 }
1853 };1862 };
1854 defer emit_llvm_ir_resolved.deinit();1863 defer emit_llvm_ir_resolved.deinit();
18551864
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 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});1879 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
1857 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {1880 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
1858 switch (emit_analysis) {1881 switch (emit_analysis) {
...@@ -1988,6 +2011,7 @@ fn buildOutputType(...@@ -1988,6 +2011,7 @@ fn buildOutputType(
1988 .emit_h = emit_h_resolved.data,2011 .emit_h = emit_h_resolved.data,
1989 .emit_asm = emit_asm_resolved.data,2012 .emit_asm = emit_asm_resolved.data,
1990 .emit_llvm_ir = emit_llvm_ir_resolved.data,2013 .emit_llvm_ir = emit_llvm_ir_resolved.data,
2014 .emit_llvm_bc = emit_llvm_bc_resolved.data,
1991 .emit_docs = emit_docs_resolved.data,2015 .emit_docs = emit_docs_resolved.data,
1992 .emit_analysis = emit_analysis_resolved.data,2016 .emit_analysis = emit_analysis_resolved.data,
1993 .link_mode = link_mode,2017 .link_mode = link_mode,
src/stage1.zig+2
...@@ -95,6 +95,8 @@ pub const Module = extern struct {...@@ -95,6 +95,8 @@ pub const Module = extern struct {
95 emit_asm_len: usize,95 emit_asm_len: usize,
96 emit_llvm_ir_ptr: [*]const u8,96 emit_llvm_ir_ptr: [*]const u8,
97 emit_llvm_ir_len: usize,97 emit_llvm_ir_len: usize,
98 emit_bitcode_ptr: [*]const u8,
99 emit_bitcode_len: usize,
98 emit_analysis_json_ptr: [*]const u8,100 emit_analysis_json_ptr: [*]const u8,
99 emit_analysis_json_len: usize,101 emit_analysis_json_len: usize,
100 emit_docs_ptr: [*]const u8,102 emit_docs_ptr: [*]const u8,
src/stage1/all_types.hpp+1
...@@ -2090,6 +2090,7 @@ struct CodeGen {...@@ -2090,6 +2090,7 @@ struct CodeGen {
2090 Buf h_file_output_path;2090 Buf h_file_output_path;
2091 Buf asm_file_output_path;2091 Buf asm_file_output_path;
2092 Buf llvm_ir_file_output_path;2092 Buf llvm_ir_file_output_path;
2093 Buf bitcode_file_output_path;
2093 Buf analysis_json_output_path;2094 Buf analysis_json_output_path;
2094 Buf docs_output_path;2095 Buf docs_output_path;
20952096
src/stage1/codegen.cpp+11-6
...@@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) {...@@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) {
8506 const char *asm_filename = nullptr;8506 const char *asm_filename = nullptr;
8507 const char *bin_filename = nullptr;8507 const char *bin_filename = nullptr;
8508 const char *llvm_ir_filename = nullptr;8508 const char *llvm_ir_filename = nullptr;
8509 const char *bitcode_filename = nullptr;
85098510
8510 if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);8511 if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
8511 if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);8512 if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);
8512 if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);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);
85138515
8514 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire8516 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly.
8515 // pipeline multiple times if this is requested.8517 // So we call the entire pipeline multiple times if this is requested.
8516 if (asm_filename != nullptr && bin_filename != nullptr) {8518 if (asm_filename != nullptr && bin_filename != nullptr) {
8517 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,8519 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
8518 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,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 exit(1);8525 exit(1);
8523 }8526 }
8524 bin_filename = nullptr;8527 bin_filename = nullptr;
...@@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) {...@@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) {
85278530
8528 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,8531 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
8529 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,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 exit(1);8538 exit(1);
8534 }8539 }
85358540
src/stage1/stage1.cpp+1
...@@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {...@@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
73 buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);73 buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);
74 buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);74 buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);
75 buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len);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 buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);77 buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);
77 buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len);78 buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len);
7879
src/stage1/stage1.h+3
...@@ -157,6 +157,9 @@ struct ZigStage1 {...@@ -157,6 +157,9 @@ struct ZigStage1 {
157 const char *emit_llvm_ir_ptr;157 const char *emit_llvm_ir_ptr;
158 size_t emit_llvm_ir_len;158 size_t emit_llvm_ir_len;
159159
160 const char *emit_bitcode_ptr;
161 size_t emit_bitcode_len;
162
160 const char *emit_analysis_json_ptr;163 const char *emit_analysis_json_ptr;
161 size_t emit_analysis_json_len;164 size_t emit_analysis_json_len;
162165
src/zig_llvm.cpp+17-2
...@@ -229,12 +229,14 @@ struct TimeTracerRAII {...@@ -229,12 +229,14 @@ struct TimeTracerRAII {
229bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,229bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
230 char **error_message, bool is_debug,230 char **error_message, bool is_debug,
231 bool is_small, bool time_report, bool tsan, bool lto,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 TimePassesIsEnabled = time_report;235 TimePassesIsEnabled = time_report;
235236
236 raw_fd_ostream *dest_asm_ptr = nullptr;237 raw_fd_ostream *dest_asm_ptr = nullptr;
237 raw_fd_ostream *dest_bin_ptr = nullptr;238 raw_fd_ostream *dest_bin_ptr = nullptr;
239 raw_fd_ostream *dest_bitcode_ptr = nullptr;
238240
239 if (asm_filename) {241 if (asm_filename) {
240 std::error_code EC;242 std::error_code EC;
...@@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
252 return true;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 }
255265
256 std::unique_ptr<raw_fd_ostream> dest_asm(dest_asm_ptr),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
258270
259 auto PID = sys::Process::getProcessId();271 auto PID = sys::Process::getProcessId();
260 std::string ProcName = "zig-";272 std::string ProcName = "zig-";
...@@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
389 if (dest_bin && lto) {401 if (dest_bin && lto) {
390 WriteBitcodeToFile(module, *dest_bin);402 WriteBitcodeToFile(module, *dest_bin);
391 }403 }
404 if (dest_bitcode) {
405 WriteBitcodeToFile(module, *dest_bitcode);
406 }
392407
393 if (time_report) {408 if (time_report) {
394 TimerGroup::printAll(errs());409 TimerGroup::printAll(errs());
src/zig_llvm.h+2-1
...@@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);...@@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
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, bool lto,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);
5354
5455
55enum ZigLLVMABIType {56enum ZigLLVMABIType {