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 @@
11const std = @import("std");
2const builtin = std.builtin;
2const builtin = @import("builtin");
33const io = std.io;
44const fs = std.fs;
55const process = std.process;
......@@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator;
1313const max_doc_file_size = 10 * 1024 * 1024;
1414
1515const 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);
1717const tmp_dir_name = "docgen_tmp";
1818const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext;
1919
......@@ -281,7 +281,7 @@ const Code = struct {
281281 name: []const u8,
282282 source_token: Token,
283283 is_inline: bool,
284 mode: builtin.Mode,
284 mode: std.builtin.Mode,
285285 link_objects: []const []const u8,
286286 target_str: ?[]const u8,
287287 link_libc: bool,
......@@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc {
531531 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
532532 }
533533
534 var mode: builtin.Mode = .Debug;
534 var mode: std.builtin.Mode = .Debug;
535535 var link_objects = std.ArrayList([]const u8).init(allocator);
536536 defer link_objects.deinit();
537537 var target_str: ?[]const u8 = null;
lib/std/target.zig+63-36
......@@ -549,15 +549,36 @@ pub const Target = struct {
549549 };
550550
551551 pub const ObjectFormat = enum {
552 /// Common Object File Format (Windows)
552553 coff,
554 /// Executable and Linking Format
553555 elf,
556 /// macOS relocatables
554557 macho,
558 /// WebAssembly
555559 wasm,
560 /// C source code
556561 c,
562 /// Standard, Portable Intermediate Representation V
557563 spirv,
564 /// Intel IHEX
558565 hex,
566 /// Machine code with no metadata.
559567 raw,
568 /// Plan 9 from Bell Labs
560569 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 }
561582 };
562583
563584 pub const SubSystem = enum {
......@@ -1289,30 +1310,16 @@ pub const Target = struct {
12891310 return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
12901311 }
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
13061313 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 => "",
13141321 },
1315 }
1322 };
13161323 }
13171324
13181325 pub fn exeFileExt(self: Target) [:0]const u8 {
......@@ -1352,20 +1359,16 @@ pub const Target = struct {
13521359 }
13531360
13541361 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 };
13691372 }
13701373
13711374 pub fn getObjectFormat(self: Target) ObjectFormat {
......@@ -1676,6 +1679,30 @@ pub const Target = struct {
16761679
16771680 return false;
16781681 }
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 }
16791706};
16801707
16811708test {
lib/std/zig.zig+9-29
......@@ -108,7 +108,8 @@ pub const BinNameOptions = struct {
108108pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
109109 const root_name = options.root_name;
110110 const target = options.target;
111 switch (options.object_format orelse target.getObjectFormat()) {
111 const ofmt = options.object_format orelse target.getObjectFormat();
112 switch (ofmt) {
112113 .coff => switch (options.output_mode) {
113114 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
114115 .Lib => {
......@@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
118119 };
119120 return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
120121 },
121 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
122 .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}),
122123 },
123124 .elf => switch (options.output_mode) {
124125 .Exe => return allocator.dupe(u8, root_name),
......@@ -140,7 +141,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
140141 },
141142 }
142143 },
143 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
144 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
144145 },
145146 .macho => switch (options.output_mode) {
146147 .Exe => return allocator.dupe(u8, root_name),
......@@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
163164 }
164165 return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });
165166 },
166 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
167 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
167168 },
168169 .wasm => switch (options.output_mode) {
169170 .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
175176 .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
176177 }
177178 },
178 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
179 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
179180 },
180181 .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
181182 .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
182183 .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
183184 .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 }),
208188 }
209189}
210190
lib/std/zig/cross_target.zig-4
......@@ -473,10 +473,6 @@ pub const CrossTarget = struct {
473473 return self.getOsTag() == .windows;
474474 }
475475
476 pub fn oFileExt(self: CrossTarget) [:0]const u8 {
477 return Target.oFileExt_os_abi(self.getOsTag(), self.getAbi());
478 }
479
480476 pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
481477 return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
482478 }
src/Compilation.zig+40-3
......@@ -143,6 +143,7 @@ debug_compiler_runtime_libs: bool,
143143
144144emit_asm: ?EmitLoc,
145145emit_llvm_ir: ?EmitLoc,
146emit_llvm_bc: ?EmitLoc,
146147emit_analysis: ?EmitLoc,
147148emit_docs: ?EmitLoc,
148149
......@@ -586,6 +587,17 @@ pub const Directory = struct {
586587 return std.fs.path.join(allocator, paths);
587588 }
588589 }
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 }
589601};
590602
591603pub const EmitLoc = struct {
......@@ -623,6 +635,8 @@ pub const InitOptions = struct {
623635 emit_asm: ?EmitLoc = null,
624636 /// `null` means to not emit LLVM IR.
625637 emit_llvm_ir: ?EmitLoc = null,
638 /// `null` means to not emit LLVM module bitcode.
639 emit_llvm_bc: ?EmitLoc = null,
626640 /// `null` means to not emit semantic analysis JSON.
627641 emit_analysis: ?EmitLoc = null,
628642 /// `null` means to not emit docs.
......@@ -819,6 +833,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
819833 break :blk false;
820834 }
821835 }
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
822840 break :blk build_options.is_stage1;
823841 };
824842
......@@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
835853 if (ofmt == .c)
836854 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
838860 // The stage1 compiler depends on the stage1 C++ LLVM backend
839861 // to compile zig code.
840862 if (use_stage1)
......@@ -853,6 +875,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
853875 if (options.machine_code_model != .default) {
854876 return error.MachineCodeModelNotSupportedWithoutLlvm;
855877 }
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 }
856884 }
857885
858886 const tsan = options.want_tsan orelse false;
......@@ -1381,6 +1409,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13811409 .bin_file = bin_file,
13821410 .emit_asm = options.emit_asm,
13831411 .emit_llvm_ir = options.emit_llvm_ir,
1412 .emit_llvm_bc = options.emit_llvm_bc,
13841413 .emit_analysis = options.emit_analysis,
13851414 .emit_docs = options.emit_docs,
13861415 .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
27282757 comp.bin_file.options.root_name
27292758 else
27302759 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
27332765 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
27342766 var argv = std.ArrayList([]const u8).init(comp.gpa);
......@@ -3978,6 +4010,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
39784010 }
39794011 man.hash.addOptionalEmitLoc(comp.emit_asm);
39804012 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
4013 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
39814014 man.hash.addOptionalEmitLoc(comp.emit_analysis);
39824015 man.hash.addOptionalEmitLoc(comp.emit_docs);
39834016 man.hash.add(comp.test_evented_io);
......@@ -4083,13 +4116,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
40834116 ) orelse return error.OutOfMemory;
40844117
40854118 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, .{
40874120 .root_name = comp.bin_file.options.root_name,
40884121 .target = target,
40894122 .output_mode = .Obj,
40904123 });
4091 break :blk try directory.join(arena, &[_][]const u8{bin_basename});
4124 break :blk try directory.join(arena, &[_][]const u8{obj_basename});
40924125 } else "";
4126
40934127 if (mod.emit_h != null) {
40944128 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
40954129 }
......@@ -4097,6 +4131,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
40974131 const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory);
40984132 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
40994133 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);
41004135 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
41014136 const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);
41024137 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
41174152 .emit_asm_len = emit_asm_path.len,
41184153 .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,
41194154 .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,
41204157 .emit_analysis_json_ptr = emit_analysis_path.ptr,
41214158 .emit_analysis_json_len = emit_analysis_path.len,
41224159 .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 {
7272 .renderscript32 => "renderscript32",
7373 .renderscript64 => "renderscript64",
7474 .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",
7878 };
7979
8080 const llvm_os = switch (target.os.tag) {
......@@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
114114 .wasi => "wasi",
115115 .emscripten => "emscripten",
116116 .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",
122124 };
123125
124126 const llvm_abi = switch (target.abi) {
......@@ -152,84 +154,105 @@ pub const Object = struct {
152154 llvm_module: *const llvm.Module,
153155 context: *const llvm.Context,
154156 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;
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 }
175164
165 pub fn init(gpa: *Allocator, options: link.Options) !Object {
176166 const context = llvm.Context.create();
177167 errdefer context.dispose();
178168
179169 initializeLLVMTargets();
180170
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);
183173 const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
184174 errdefer llvm_module.dispose();
185175
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);
188178
189179 var error_message: [*:0]const u8 = undefined;
190180 var target: *const llvm.Target = undefined;
191181 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
192182 defer llvm.disposeMessage(error_message);
193183
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;
203186 }
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
206225 const target_machine = llvm.TargetMachine.create(
207226 target,
208227 llvm_target_triple.ptr,
209 "",
210 "",
228 if (options.target.cpu.model.llvm_name) |s| s.ptr else null,
229 options.llvm_cpu_features,
211230 opt_level,
212 .Static,
213 .Default,
231 reloc_mode,
232 code_model,
233 options.function_sections,
234 float_abi,
235 abi_name,
214236 );
215237 errdefer target_machine.dispose();
216238
217 self.* = .{
239 return Object{
218240 .llvm_module = llvm_module,
219241 .context = context,
220242 .target_machine = target_machine,
221 .object_pathZ = object_pathZ,
222243 };
223 return self;
224244 }
225245
226 pub fn deinit(self: *Object, allocator: *Allocator) void {
246 pub fn deinit(self: *Object) void {
227247 self.target_machine.dispose();
228248 self.llvm_module.dispose();
229249 self.context.dispose();
250 self.* = undefined;
251 }
230252
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);
233256 }
234257
235258 fn initializeLLVMTargets() void {
......@@ -240,38 +263,81 @@ pub const Object = struct {
240263 llvm.initializeAllAsmParsers();
241264 }
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
243277 pub fn flushModule(self: *Object, comp: *Compilation) !void {
244278 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();
250280 }
251281
252 {
282 if (std.debug.runtime_safety) {
253283 var error_message: [*:0]const u8 = undefined;
254284 // verifyModule always allocs the error_message even if there is no error
255285 defer llvm.disposeMessage(error_message);
256286
257287 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");
261290 }
262291 }
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
264317 var error_message: [*:0]const u8 = undefined;
265318 if (self.target_machine.emitToFile(
266319 self.llvm_module,
267 self.object_pathZ.ptr,
268 .ObjectFile,
269320 &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 )) {
271331 defer llvm.disposeMessage(error_message);
272332
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 });
275341 return error.FailedToEmit;
276342 }
277343 }
src/codegen/llvm/bindings.zig+35-13
......@@ -123,6 +123,9 @@ pub const Module = opaque {
123123
124124 pub const getNamedGlobal = LLVMGetNamedGlobal;
125125 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;
126129};
127130
128131pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
......@@ -250,31 +253,41 @@ pub const BasicBlock = opaque {
250253};
251254
252255pub const TargetMachine = opaque {
253 pub const create = LLVMCreateTargetMachine;
254 extern fn LLVMCreateTargetMachine(
256 pub const create = ZigLLVMCreateTargetMachine;
257 extern fn ZigLLVMCreateTargetMachine(
255258 T: *const Target,
256259 Triple: [*:0]const u8,
257 CPU: [*:0]const u8,
258 Features: [*:0]const u8,
260 CPU: ?[*:0]const u8,
261 Features: ?[*:0]const u8,
259262 Level: CodeGenOptLevel,
260263 Reloc: RelocMode,
261 CodeModel: CodeMode,
264 CodeModel: CodeModel,
265 function_sections: bool,
266 float_abi: ABIType,
267 abi_name: ?[*:0]const u8,
262268 ) *const TargetMachine;
263269
264270 pub const dispose = LLVMDisposeTargetMachine;
265271 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
266272
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,
270276 M: *const Module,
271 Filename: [*:0]const u8,
272 codegen: CodeGenFileType,
273277 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;
275288};
276289
277pub const CodeMode = enum(c_int) {
290pub const CodeModel = enum(c_int) {
278291 Default,
279292 JITDefault,
280293 Tiny,
......@@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) {
295308 Default,
296309 Static,
297310 PIC,
298 DynamicNoPic,
311 DynamicNoPIC,
299312 ROPI,
300313 RWPI,
301314 ROPI_RWPI,
......@@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) {
306319 ObjectFile,
307320};
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
309331pub const Target = opaque {
310332 pub const getFromTriple = LLVMGetTargetFromTriple;
311333 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 {
206206 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
207207 const sub_path = if (use_lld) blk: {
208208 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.
210211 return switch (options.object_format) {
211212 .coff => &(try Coff.createEmpty(allocator, options)).base,
212213 .elf => &(try Elf.createEmpty(allocator, options)).base,
......@@ -219,8 +220,11 @@ pub const File = struct {
219220 .raw => return error.RawObjectFormatUnimplemented,
220221 };
221222 }
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 });
224228 } else emit.sub_path;
225229 errdefer if (use_lld) allocator.free(sub_path);
226230
src/link/Coff.zig+11-7
......@@ -17,9 +17,9 @@ const link = @import("../link.zig");
1717const build_options = @import("build_options");
1818const Cache = @import("../Cache.zig");
1919const mingw = @import("../mingw.zig");
20const llvm_backend = @import("../codegen/llvm.zig");
2120const Air = @import("../Air.zig");
2221const Liveness = @import("../Liveness.zig");
22const LlvmObject = @import("../codegen/llvm.zig").Object;
2323
2424const allocation_padding = 4 / 3;
2525const minimum_text_block_size = 64 * allocation_padding;
......@@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff;
3737const msdos_stub = @embedFile("msdos-stub.bin");
3838
3939/// 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
4242base: link.File,
4343ptr_width: PtrWidth,
......@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
132132 const self = try createEmpty(allocator, options);
133133 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);
136136 return self;
137137 }
138138
......@@ -820,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
820820 const tracy = trace(@src());
821821 defer tracy.end();
822822
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 }
825828
826829 if (self.text_section_size_dirty) {
827830 // Write the new raw size in the .text header
......@@ -1395,8 +1398,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
13951398}
13961399
13971400pub 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 }
14001404
14011405 self.text_block_free_list.deinit(self.base.allocator);
14021406 self.offset_table.deinit(self.base.allocator);
src/link/Elf.zig+8-8
......@@ -25,9 +25,9 @@ const target_util = @import("../target.zig");
2525const glibc = @import("../glibc.zig");
2626const musl = @import("../musl.zig");
2727const Cache = @import("../Cache.zig");
28const llvm_backend = @import("../codegen/llvm.zig");
2928const Air = @import("../Air.zig");
3029const Liveness = @import("../Liveness.zig");
30const LlvmObject = @import("../codegen/llvm.zig").Object;
3131
3232const default_entry_addr = 0x8000000;
3333
......@@ -38,7 +38,7 @@ base: File,
3838ptr_width: PtrWidth,
3939
4040/// 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
4343/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
4444/// Same order as in the file.
......@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
235235 const self = try createEmpty(allocator, options);
236236 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);
239239 return self;
240240 }
241241
......@@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
301301}
302302
303303pub 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 }
307307
308308 self.sections.deinit(self.base.allocator);
309309 self.program_headers.deinit(self.base.allocator);
......@@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
750750 if (build_options.have_llvm)
751751 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 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.
755755 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
756756
757757 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");
3030const Trie = @import("MachO/Trie.zig");
3131const CodeSignature = @import("MachO/CodeSignature.zig");
3232const Zld = @import("MachO/Zld.zig");
33const llvm_backend = @import("../codegen/llvm.zig");
33const LlvmObject = @import("../codegen/llvm.zig").Object;
3434
3535usingnamespace @import("MachO/commands.zig");
3636
......@@ -39,7 +39,7 @@ pub const base_tag: File.Tag = File.Tag.macho;
3939base: File,
4040
4141/// 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
4444/// Debug symbols bundle (or dSym).
4545d_sym: ?DebugSymbols = null,
......@@ -355,7 +355,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
355355 const self = try createEmpty(allocator, options);
356356 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);
359359 return self;
360360 }
361361
......@@ -989,6 +989,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
989989}
990990
991991pub 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 }
992995 if (self.d_sym) |*ds| {
993996 ds.deinit(self.base.allocator);
994997 }
src/link/Wasm.zig+6-3
......@@ -19,7 +19,7 @@ const build_options = @import("build_options");
1919const wasi_libc = @import("../wasi_libc.zig");
2020const Cache = @import("../Cache.zig");
2121const TypedValue = @import("../TypedValue.zig");
22const llvm_backend = @import("../codegen/llvm.zig");
22const LlvmObject = @import("../codegen/llvm.zig").Object;
2323const Air = @import("../Air.zig");
2424const Liveness = @import("../Liveness.zig");
2525
......@@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm;
2727
2828base: link.File,
2929/// 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,
3131/// List of all function Decls to be written to the output file. The index of
3232/// each Decl in this list at the time of writing the binary is used as the
3333/// 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
121121 const self = try createEmpty(allocator, options);
122122 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);
125125 return self;
126126 }
127127
......@@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
153153}
154154
155155pub 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 }
156159 for (self.symbols.items) |decl| {
157160 decl.fn_link.wasm.functype.deinit(self.base.allocator);
158161 decl.fn_link.wasm.code.deinit(self.base.allocator);
src/main.zig+31-7
......@@ -301,6 +301,8 @@ const usage_build_generic =
301301 \\ -fno-emit-asm (default) Do not output .s (assembly code)
302302 \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)
303303 \\ -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
304306 \\ -femit-h[=path] Generate a C header file (.h)
305307 \\ -fno-emit-h (default) Do not generate a C header file (.h)
306308 \\ -femit-docs[=path] Create a docs/ dir with html documentation
......@@ -359,7 +361,7 @@ const usage_build_generic =
359361 \\ --single-threaded Code assumes it is only used single-threaded
360362 \\ -ofmt=[mode] Override target object format
361363 \\ elf Executable and Linking Format
362 \\ c Compile to C source code
364 \\ c C source code
363365 \\ wasm WebAssembly
364366 \\ coff Common Object File Format (Windows)
365367 \\ macho macOS relocatables
......@@ -551,6 +553,7 @@ fn buildOutputType(
551553 var emit_bin: EmitBin = .yes_default_path;
552554 var emit_asm: Emit = .no;
553555 var emit_llvm_ir: Emit = .no;
556 var emit_llvm_bc: Emit = .no;
554557 var emit_docs: Emit = .no;
555558 var emit_analysis: Emit = .no;
556559 var target_arch_os_abi: []const u8 = "native";
......@@ -1010,6 +1013,12 @@ fn buildOutputType(
10101013 emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
10111014 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
10121015 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;
10131022 } else if (mem.eql(u8, arg, "-femit-docs")) {
10141023 emit_docs = .yes_default_path;
10151024 } else if (mem.startsWith(u8, arg, "-femit-docs=")) {
......@@ -1815,10 +1824,10 @@ fn buildOutputType(
18151824 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
18161825 switch (emit_h) {
18171826 .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) });
18191828 },
18201829 .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) });
18221831 },
18231832 .no => unreachable,
18241833 }
......@@ -1829,10 +1838,10 @@ fn buildOutputType(
18291838 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
18301839 switch (emit_asm) {
18311840 .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) });
18331842 },
18341843 .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) });
18361845 },
18371846 .no => unreachable,
18381847 }
......@@ -1843,16 +1852,30 @@ fn buildOutputType(
18431852 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
18441853 switch (emit_llvm_ir) {
18451854 .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) });
18471856 },
18481857 .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) });
18501859 },
18511860 .no => unreachable,
18521861 }
18531862 };
18541863 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
18561879 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
18571880 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
18581881 switch (emit_analysis) {
......@@ -1988,6 +2011,7 @@ fn buildOutputType(
19882011 .emit_h = emit_h_resolved.data,
19892012 .emit_asm = emit_asm_resolved.data,
19902013 .emit_llvm_ir = emit_llvm_ir_resolved.data,
2014 .emit_llvm_bc = emit_llvm_bc_resolved.data,
19912015 .emit_docs = emit_docs_resolved.data,
19922016 .emit_analysis = emit_analysis_resolved.data,
19932017 .link_mode = link_mode,
src/stage1.zig+2
......@@ -95,6 +95,8 @@ pub const Module = extern struct {
9595 emit_asm_len: usize,
9696 emit_llvm_ir_ptr: [*]const u8,
9797 emit_llvm_ir_len: usize,
98 emit_bitcode_ptr: [*]const u8,
99 emit_bitcode_len: usize,
98100 emit_analysis_json_ptr: [*]const u8,
99101 emit_analysis_json_len: usize,
100102 emit_docs_ptr: [*]const u8,
src/stage1/all_types.hpp+1
......@@ -2090,6 +2090,7 @@ struct CodeGen {
20902090 Buf h_file_output_path;
20912091 Buf asm_file_output_path;
20922092 Buf llvm_ir_file_output_path;
2093 Buf bitcode_file_output_path;
20932094 Buf analysis_json_output_path;
20942095 Buf docs_output_path;
20952096
src/stage1/codegen.cpp+11-6
......@@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) {
85068506 const char *asm_filename = nullptr;
85078507 const char *bin_filename = nullptr;
85088508 const char *llvm_ir_filename = nullptr;
8509 const char *bitcode_filename = nullptr;
85098510
85108511 if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
85118512 if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);
85128513 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 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.
85168518 if (asm_filename != nullptr && bin_filename != nullptr) {
85178519 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
85188520 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))
85208522 {
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);
85228525 exit(1);
85238526 }
85248527 bin_filename = nullptr;
......@@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) {
85278530
85288531 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
85298532 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))
85318534 {
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);
85338538 exit(1);
85348539 }
85358540
src/stage1/stage1.cpp+1
......@@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
7373 buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);
7474 buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);
7575 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);
7677 buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);
7778 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 {
157157 const char *emit_llvm_ir_ptr;
158158 size_t emit_llvm_ir_len;
159159
160 const char *emit_bitcode_ptr;
161 size_t emit_bitcode_len;
162
160163 const char *emit_analysis_json_ptr;
161164 size_t emit_analysis_json_len;
162165
src/zig_llvm.cpp+17-2
......@@ -229,12 +229,14 @@ struct TimeTracerRAII {
229229bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
230230 char **error_message, bool is_debug,
231231 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)
233234{
234235 TimePassesIsEnabled = time_report;
235236
236237 raw_fd_ostream *dest_asm_ptr = nullptr;
237238 raw_fd_ostream *dest_bin_ptr = nullptr;
239 raw_fd_ostream *dest_bitcode_ptr = nullptr;
238240
239241 if (asm_filename) {
240242 std::error_code EC;
......@@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
252254 return true;
253255 }
254256 }
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
256266 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
259271 auto PID = sys::Process::getProcessId();
260272 std::string ProcName = "zig-";
......@@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
389401 if (dest_bin && lto) {
390402 WriteBitcodeToFile(module, *dest_bin);
391403 }
404 if (dest_bitcode) {
405 WriteBitcodeToFile(module, *dest_bitcode);
406 }
392407
393408 if (time_report) {
394409 TimerGroup::printAll(errs());
src/zig_llvm.h+2-1
......@@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
4949ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
5050 char **error_message, bool is_debug,
5151 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
5556enum ZigLLVMABIType {