aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--doc/docgen.zig8
-rw-r--r--lib/std/target.zig100
-rw-r--r--lib/std/zig.zig40
-rw-r--r--lib/std/zig/cross_target.zig4
-rw-r--r--src/Compilation.zig75
-rw-r--r--src/codegen/llvm.zig196
-rw-r--r--src/codegen/llvm/bindings.zig48
-rw-r--r--src/link.zig16
-rw-r--r--src/link/Coff.zig25
-rw-r--r--src/link/Elf.zig20
-rw-r--r--src/link/MachO.zig9
-rw-r--r--src/link/Wasm.zig13
-rw-r--r--src/main.zig62
-rw-r--r--src/stage1.zig4
-rw-r--r--src/stage1/all_types.hpp2
-rw-r--r--src/stage1/codegen.cpp33
-rw-r--r--src/stage1/stage1.cpp2
-rw-r--r--src/stage1/stage1.h4
-rw-r--r--src/stage1/zig0.cpp5
-rw-r--r--src/zig_llvm.cpp19
-rw-r--r--src/zig_llvm.h3
22 files changed, 454 insertions, 235 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0a8da2dd49..2714fa6d6b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -796,6 +796,7 @@ set(BUILD_ZIG1_ARGS
--name zig1
--zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"
"-femit-bin=${ZIG1_OBJECT}"
+ -fcompiler-rt
"${ZIG1_RELEASE_ARG}"
"${ZIG1_SINGLE_THREADED_ARG}"
-lc
diff --git a/doc/docgen.zig b/doc/docgen.zig
index 1261981af3..f2d2e2845b 100644
--- a/doc/docgen.zig
+++ b/doc/docgen.zig
@@ -1,5 +1,5 @@
const std = @import("std");
-const builtin = std.builtin;
+const builtin = @import("builtin");
const io = std.io;
const fs = std.fs;
const process = std.process;
@@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator;
const max_doc_file_size = 10 * 1024 * 1024;
const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt();
-const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt();
+const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
const tmp_dir_name = "docgen_tmp";
const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext;
@@ -281,7 +281,7 @@ const Code = struct {
name: []const u8,
source_token: Token,
is_inline: bool,
- mode: builtin.Mode,
+ mode: std.builtin.Mode,
link_objects: []const []const u8,
target_str: ?[]const u8,
link_libc: bool,
@@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc {
return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
}
- var mode: builtin.Mode = .Debug;
+ var mode: std.builtin.Mode = .Debug;
var link_objects = std.ArrayList([]const u8).init(allocator);
defer link_objects.deinit();
var target_str: ?[]const u8 = null;
diff --git a/lib/std/target.zig b/lib/std/target.zig
index 70626f5051..1b9f0084c8 100644
--- a/lib/std/target.zig
+++ b/lib/std/target.zig
@@ -549,16 +549,36 @@ pub const Target = struct {
};
pub const ObjectFormat = enum {
+ /// Common Object File Format (Windows)
coff,
- pe,
+ /// Executable and Linking Format
elf,
+ /// macOS relocatables
macho,
+ /// WebAssembly
wasm,
+ /// C source code
c,
+ /// Standard, Portable Intermediate Representation V
spirv,
+ /// Intel IHEX
hex,
+ /// Machine code with no metadata.
raw,
+ /// Plan 9 from Bell Labs
plan9,
+
+ pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 {
+ return switch (of) {
+ .coff => ".obj",
+ .elf, .macho, .wasm => ".o",
+ .c => ".c",
+ .spirv => ".spv",
+ .hex => ".ihex",
+ .raw => ".bin",
+ .plan9 => plan9Ext(cpu_arch),
+ };
+ }
};
pub const SubSystem = enum {
@@ -1290,30 +1310,16 @@ pub const Target = struct {
return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
}
- pub fn oFileExt_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 {
- if (abi == .msvc) {
- return ".obj";
- }
- switch (os_tag) {
- .windows, .uefi => return ".obj",
- else => return ".o",
- }
- }
-
- pub fn oFileExt(self: Target) [:0]const u8 {
- return oFileExt_os_abi(self.os.tag, self.abi);
- }
-
pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {
- switch (os_tag) {
- .windows => return ".exe",
- .uefi => return ".efi",
- else => if (cpu_arch.isWasm()) {
- return ".wasm";
- } else {
- return "";
+ return switch (os_tag) {
+ .windows => ".exe",
+ .uefi => ".efi",
+ .plan9 => plan9Ext(cpu_arch),
+ else => switch (cpu_arch) {
+ .wasm32, .wasm64 => ".wasm",
+ else => "",
},
- }
+ };
}
pub fn exeFileExt(self: Target) [:0]const u8 {
@@ -1353,20 +1359,16 @@ pub const Target = struct {
}
pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat {
- if (os_tag == .windows or os_tag == .uefi) {
- return .coff;
- } else if (os_tag.isDarwin()) {
- return .macho;
- }
- if (cpu_arch.isWasm()) {
- return .wasm;
- }
- if (cpu_arch.isSPIRV()) {
- return .spirv;
- }
- if (os_tag == .plan9)
- return .plan9;
- return .elf;
+ return switch (os_tag) {
+ .windows, .uefi => .coff,
+ .ios, .macos, .watchos, .tvos => .macho,
+ .plan9 => .plan9,
+ else => return switch (cpu_arch) {
+ .wasm32, .wasm64 => .wasm,
+ .spirv32, .spirv64 => .spirv,
+ else => .elf,
+ },
+ };
}
pub fn getObjectFormat(self: Target) ObjectFormat {
@@ -1677,6 +1679,30 @@ pub const Target = struct {
return false;
}
+
+ /// 0c spim little-endian MIPS 3000 family
+ /// 1c 68000 Motorola MC68000
+ /// 2c 68020 Motorola MC68020
+ /// 5c arm little-endian ARM
+ /// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
+ /// 7c arm64 ARM64 (ARMv8)
+ /// 8c 386 Intel i386, i486, Pentium, etc.
+ /// kc sparc Sun SPARC
+ /// qc power Power PC
+ /// vc mips big-endian MIPS 3000 family
+ pub fn plan9Ext(cpu_arch: Cpu.Arch) [:0]const u8 {
+ return switch (cpu_arch) {
+ .arm => ".5",
+ .x86_64 => ".6",
+ .aarch64 => ".7",
+ .i386 => ".8",
+ .sparc => ".k",
+ .powerpc, .powerpcle => ".q",
+ .mips, .mipsel => ".v",
+ // ISAs without designated characters get 'X' for lack of a better option.
+ else => ".X",
+ };
+ }
};
test {
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index 595dce77c2..303c930b93 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -108,8 +108,9 @@ pub const BinNameOptions = struct {
pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
const root_name = options.root_name;
const target = options.target;
- switch (options.object_format orelse target.getObjectFormat()) {
- .coff, .pe => switch (options.output_mode) {
+ const ofmt = options.object_format orelse target.getObjectFormat();
+ switch (ofmt) {
+ .coff => switch (options.output_mode) {
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
.Lib => {
const suffix = switch (options.link_mode orelse .Static) {
@@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
};
return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
},
- .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}),
},
.elf => switch (options.output_mode) {
.Exe => return allocator.dupe(u8, root_name),
@@ -140,7 +141,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
},
}
},
- .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
},
.macho => switch (options.output_mode) {
.Exe => return allocator.dupe(u8, root_name),
@@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
}
return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });
},
- .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
},
.wasm => switch (options.output_mode) {
.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
.Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
}
},
- .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
},
.c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
.spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
.hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
.raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
- .plan9 => {
- // copied from 2c(1)
- // 0c spim little-endian MIPS 3000 family
- // 1c 68000 Motorola MC68000
- // 2c 68020 Motorola MC68020
- // 5c arm little-endian ARM
- // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
- // 7c arm64 ARM64 (ARMv8)
- // 8c 386 Intel i386, i486, Pentium, etc.
- // kc sparc Sun SPARC
- // qc power Power PC
- // vc mips big-endian MIPS 3000 family
- const char: u8 = switch (target.cpu.arch) {
- .arm => '5',
- .x86_64 => '6',
- .aarch64 => '7',
- .i386 => '8',
- .sparc => 'k',
- .powerpc, .powerpcle => 'q',
- .mips, .mipsel => 'v',
- else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X
- };
- return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char });
- },
+ .plan9 => return std.fmt.allocPrint(allocator, "{s}{s}", .{
+ root_name, ofmt.fileExt(target.cpu.arch),
+ }),
}
}
diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig
index e6ca0a2baa..1058628633 100644
--- a/lib/std/zig/cross_target.zig
+++ b/lib/std/zig/cross_target.zig
@@ -473,10 +473,6 @@ pub const CrossTarget = struct {
return self.getOsTag() == .windows;
}
- pub fn oFileExt(self: CrossTarget) [:0]const u8 {
- return Target.oFileExt_os_abi(self.getOsTag(), self.getAbi());
- }
-
pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
}
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 6a6a6909c8..3f3a41956c 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -143,6 +143,7 @@ debug_compiler_runtime_libs: bool,
emit_asm: ?EmitLoc,
emit_llvm_ir: ?EmitLoc,
+emit_llvm_bc: ?EmitLoc,
emit_analysis: ?EmitLoc,
emit_docs: ?EmitLoc,
@@ -586,6 +587,17 @@ pub const Directory = struct {
return std.fs.path.join(allocator, paths);
}
}
+
+ pub fn joinZ(self: Directory, allocator: *Allocator, paths: []const []const u8) ![:0]u8 {
+ if (self.path) |p| {
+ // TODO clean way to do this with only 1 allocation
+ const part2 = try std.fs.path.join(allocator, paths);
+ defer allocator.free(part2);
+ return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
+ } else {
+ return std.fs.path.joinZ(allocator, paths);
+ }
+ }
};
pub const EmitLoc = struct {
@@ -623,6 +635,8 @@ pub const InitOptions = struct {
emit_asm: ?EmitLoc = null,
/// `null` means to not emit LLVM IR.
emit_llvm_ir: ?EmitLoc = null,
+ /// `null` means to not emit LLVM module bitcode.
+ emit_llvm_bc: ?EmitLoc = null,
/// `null` means to not emit semantic analysis JSON.
emit_analysis: ?EmitLoc = null,
/// `null` means to not emit docs.
@@ -812,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
const ofmt = options.object_format orelse options.target.getObjectFormat();
const use_stage1 = options.use_stage1 orelse blk: {
+ // Even though we may have no Zig code to compile (depending on `options.root_pkg`),
+ // we may need to use stage1 for building compiler-rt and other dependencies.
+
if (build_options.omit_stage2)
break :blk true;
if (options.use_llvm) |use_llvm| {
@@ -819,6 +836,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
break :blk false;
}
}
+
break :blk build_options.is_stage1;
};
@@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (ofmt == .c)
break :blk false;
+ // If emitting to LLVM bitcode object format, must use LLVM backend.
+ if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
+ break :blk true;
+
// The stage1 compiler depends on the stage1 C++ LLVM backend
// to compile zig code.
if (use_stage1)
@@ -853,6 +875,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (options.machine_code_model != .default) {
return error.MachineCodeModelNotSupportedWithoutLlvm;
}
+ if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
+ return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
+ }
}
const tsan = options.want_tsan orelse false;
@@ -1381,6 +1406,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.bin_file = bin_file,
.emit_asm = options.emit_asm,
.emit_llvm_ir = options.emit_llvm_ir,
+ .emit_llvm_bc = options.emit_llvm_bc,
.emit_analysis = options.emit_analysis,
.emit_docs = options.emit_docs,
.work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
@@ -1513,24 +1539,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
}
// The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt.
- // Once it is capable this condition should be removed.
+ // Once it is capable this condition should be removed. When removing this condition,
+ // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
+ // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
+ // stage1 but not stage2.
if (comp.bin_file.options.use_stage1) {
if (comp.bin_file.options.include_compiler_rt) {
if (is_exe_or_dyn_lib) {
try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
- } else {
+ } else if (options.output_mode != .Obj) {
+ // If build-obj with -fcompiler-rt is requested, that is handled specially
+ // elsewhere. In this case we are making a static library, so we ask
+ // for a compiler-rt object to put in it.
try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
- if (comp.bin_file.options.object_format != .elf and
- comp.bin_file.options.output_mode == .Obj)
- {
- // For ELF we can rely on using -r to link multiple objects together into one,
- // but to truly support `build-obj -fcompiler-rt` will require virtually
- // injecting `_ = @import("compiler_rt.zig")` into the root source file of
- // the compilation.
- fatal("Embedding compiler-rt into {s} objects is not yet implemented.", .{
- @tagName(comp.bin_file.options.object_format),
- });
- }
}
}
if (needs_c_symbols) {
@@ -2728,7 +2749,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
comp.bin_file.options.root_name
else
c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];
- const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, comp.getTarget().oFileExt() });
+ const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{
+ o_basename_noext,
+ comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch),
+ });
const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
var argv = std.ArrayList([]const u8).init(comp.gpa);
@@ -3023,7 +3047,7 @@ pub fn addCCArgs(
if (!comp.bin_file.options.strip) {
try argv.append("-g");
switch (comp.bin_file.options.object_format) {
- .coff, .pe => try argv.append("-gcodeview"),
+ .coff => try argv.append("-gcodeview"),
else => {},
}
}
@@ -3949,6 +3973,16 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
const id_symlink_basename = "stage1.id";
const libs_txt_basename = "libs.txt";
+ // The include_compiler_rt stored in the bin file options here means that we need
+ // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend
+ // we need to tell stage1 to include compiler-rt only if stage1 is the place that
+ // needs to provide those symbols. Otherwise the stage2 infrastructure will take care
+ // of it in the linker, by putting compiler_rt.o into a static archive, or linking
+ // compiler_rt.a against an executable. In other words we only want to set this flag
+ // for stage1 if we are using build-obj.
+ const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and
+ comp.bin_file.options.include_compiler_rt;
+
// We are about to obtain this lock, so here we give other processes a chance first.
comp.releaseStage1Lock();
@@ -3970,6 +4004,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
man.hash.add(target.os.getVersionRange());
man.hash.add(comp.bin_file.options.dll_export_fns);
man.hash.add(comp.bin_file.options.function_sections);
+ man.hash.add(include_compiler_rt);
man.hash.add(comp.bin_file.options.is_test);
man.hash.add(comp.bin_file.options.emit != null);
man.hash.add(mod.emit_h != null);
@@ -3978,6 +4013,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
}
man.hash.addOptionalEmitLoc(comp.emit_asm);
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
+ man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
man.hash.addOptionalEmitLoc(comp.emit_analysis);
man.hash.addOptionalEmitLoc(comp.emit_docs);
man.hash.add(comp.test_evented_io);
@@ -4083,13 +4119,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
) orelse return error.OutOfMemory;
const emit_bin_path = if (comp.bin_file.options.emit != null) blk: {
- const bin_basename = try std.zig.binNameAlloc(arena, .{
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
.root_name = comp.bin_file.options.root_name,
.target = target,
.output_mode = .Obj,
});
- break :blk try directory.join(arena, &[_][]const u8{bin_basename});
+ break :blk try directory.join(arena, &[_][]const u8{obj_basename});
} else "";
+
if (mod.emit_h != null) {
log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
}
@@ -4097,6 +4134,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory);
const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
+ const emit_llvm_bc_path = try stage1LocPath(arena, comp.emit_llvm_bc, directory);
const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);
const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
@@ -4117,6 +4155,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
.emit_asm_len = emit_asm_path.len,
.emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,
.emit_llvm_ir_len = emit_llvm_ir_path.len,
+ .emit_bitcode_ptr = emit_llvm_bc_path.ptr,
+ .emit_bitcode_len = emit_llvm_bc_path.len,
.emit_analysis_json_ptr = emit_analysis_path.ptr,
.emit_analysis_json_len = emit_analysis_path.len,
.emit_docs_ptr = emit_docs_path.ptr,
@@ -4145,6 +4185,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
.valgrind_enabled = comp.bin_file.options.valgrind,
.tsan_enabled = comp.bin_file.options.tsan,
.function_sections = comp.bin_file.options.function_sections,
+ .include_compiler_rt = include_compiler_rt,
.enable_stack_probing = comp.bin_file.options.stack_check,
.red_zone = comp.bin_file.options.red_zone,
.enable_time_report = comp.time_report,
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 81484e93db..92fa32dacf 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -72,9 +72,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
.renderscript32 => "renderscript32",
.renderscript64 => "renderscript64",
.ve => "ve",
- .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
- .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV,
- .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV,
+ .spu_2 => return error.@"LLVM backend does not support SPU Mark II",
+ .spirv32 => return error.@"LLVM backend does not support SPIR-V",
+ .spirv64 => return error.@"LLVM backend does not support SPIR-V",
};
const llvm_os = switch (target.os.tag) {
@@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
.wasi => "wasi",
.emscripten => "emscripten",
.uefi => "windows",
- .opencl => return error.LLVMBackendDoesNotSupportOpenCL,
- .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450,
- .vulkan => return error.LLVMBackendDoesNotSupportVulkan,
- .plan9 => return error.LLVMBackendDoesNotSupportPlan9,
- .other => "unknown",
+
+ .opencl,
+ .glsl450,
+ .vulkan,
+ .plan9,
+ .other,
+ => "unknown",
};
const llvm_abi = switch (target.abi) {
@@ -152,84 +154,105 @@ pub const Object = struct {
llvm_module: *const llvm.Module,
context: *const llvm.Context,
target_machine: *const llvm.TargetMachine,
- object_pathZ: [:0]const u8,
-
- pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
- _ = sub_path;
- const self = try allocator.create(Object);
- errdefer allocator.destroy(self);
-
- const obj_basename = try std.zig.binNameAlloc(allocator, .{
- .root_name = options.root_name,
- .target = options.target,
- .output_mode = .Obj,
- });
- defer allocator.free(obj_basename);
- const o_directory = options.module.?.zig_cache_artifact_directory;
- const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename});
- defer allocator.free(object_path);
-
- const object_pathZ = try allocator.dupeZ(u8, object_path);
- errdefer allocator.free(object_pathZ);
+ pub fn create(gpa: *Allocator, options: link.Options) !*Object {
+ const obj = try gpa.create(Object);
+ errdefer gpa.destroy(obj);
+ obj.* = try Object.init(gpa, options);
+ return obj;
+ }
+ pub fn init(gpa: *Allocator, options: link.Options) !Object {
const context = llvm.Context.create();
errdefer context.dispose();
initializeLLVMTargets();
- const root_nameZ = try allocator.dupeZ(u8, options.root_name);
- defer allocator.free(root_nameZ);
+ const root_nameZ = try gpa.dupeZ(u8, options.root_name);
+ defer gpa.free(root_nameZ);
const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
errdefer llvm_module.dispose();
- const llvm_target_triple = try targetTriple(allocator, options.target);
- defer allocator.free(llvm_target_triple);
+ const llvm_target_triple = try targetTriple(gpa, options.target);
+ defer gpa.free(llvm_target_triple);
var error_message: [*:0]const u8 = undefined;
var target: *const llvm.Target = undefined;
if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
defer llvm.disposeMessage(error_message);
- const stderr = std.io.getStdErr().writer();
- try stderr.print(
- \\Zig is expecting LLVM to understand this target: '{s}'
- \\However LLVM responded with: "{s}"
- \\
- ,
- .{ llvm_target_triple, error_message },
- );
- return error.InvalidLLVMTriple;
+ log.err("LLVM failed to parse '{s}': {s}", .{ llvm_target_triple, error_message });
+ return error.InvalidLlvmTriple;
}
- const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive;
+ const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug)
+ .None
+ else
+ .Aggressive;
+
+ const reloc_mode: llvm.RelocMode = if (options.pic)
+ .PIC
+ else if (options.link_mode == .Dynamic)
+ llvm.RelocMode.DynamicNoPIC
+ else
+ .Static;
+
+ const code_model: llvm.CodeModel = switch (options.machine_code_model) {
+ .default => .Default,
+ .tiny => .Tiny,
+ .small => .Small,
+ .kernel => .Kernel,
+ .medium => .Medium,
+ .large => .Large,
+ };
+
+ // TODO handle float ABI better- it should depend on the ABI portion of std.Target
+ const float_abi: llvm.ABIType = .Default;
+
+ // TODO a way to override this as part of std.Target ABI?
+ const abi_name: ?[*:0]const u8 = switch (options.target.cpu.arch) {
+ .riscv32 => switch (options.target.os.tag) {
+ .linux => "ilp32d",
+ else => "ilp32",
+ },
+ .riscv64 => switch (options.target.os.tag) {
+ .linux => "lp64d",
+ else => "lp64",
+ },
+ else => null,
+ };
+
const target_machine = llvm.TargetMachine.create(
target,
llvm_target_triple.ptr,
- "",
- "",
+ if (options.target.cpu.model.llvm_name) |s| s.ptr else null,
+ options.llvm_cpu_features,
opt_level,
- .Static,
- .Default,
+ reloc_mode,
+ code_model,
+ options.function_sections,
+ float_abi,
+ abi_name,
);
errdefer target_machine.dispose();
- self.* = .{
+ return Object{
.llvm_module = llvm_module,
.context = context,
.target_machine = target_machine,
- .object_pathZ = object_pathZ,
};
- return self;
}
- pub fn deinit(self: *Object, allocator: *Allocator) void {
+ pub fn deinit(self: *Object) void {
self.target_machine.dispose();
self.llvm_module.dispose();
self.context.dispose();
+ self.* = undefined;
+ }
- allocator.free(self.object_pathZ);
- allocator.destroy(self);
+ pub fn destroy(self: *Object, gpa: *Allocator) void {
+ self.deinit();
+ gpa.destroy(self);
}
fn initializeLLVMTargets() void {
@@ -240,38 +263,81 @@ pub const Object = struct {
llvm.initializeAllAsmParsers();
}
+ fn locPath(
+ arena: *Allocator,
+ opt_loc: ?Compilation.EmitLoc,
+ cache_directory: Compilation.Directory,
+ ) !?[*:0]u8 {
+ const loc = opt_loc orelse return null;
+ const directory = loc.directory orelse cache_directory;
+ const slice = try directory.joinZ(arena, &[_][]const u8{loc.basename});
+ return slice.ptr;
+ }
+
pub fn flushModule(self: *Object, comp: *Compilation) !void {
if (comp.verbose_llvm_ir) {
- const dump = self.llvm_module.printToString();
- defer llvm.disposeMessage(dump);
-
- const stderr = std.io.getStdErr().writer();
- try stderr.writeAll(std.mem.spanZ(dump));
+ self.llvm_module.dump();
}
- {
+ if (std.debug.runtime_safety) {
var error_message: [*:0]const u8 = undefined;
// verifyModule always allocs the error_message even if there is no error
defer llvm.disposeMessage(error_message);
if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
- const stderr = std.io.getStdErr().writer();
- try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
- return error.BrokenLLVMModule;
+ std.debug.print("\n{s}\n", .{error_message});
+ @panic("LLVM module verification failed");
}
}
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const mod = comp.bin_file.options.module.?;
+ const cache_dir = mod.zig_cache_artifact_directory;
+
+ const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = comp.bin_file.options.root_name,
+ .target = comp.bin_file.options.target,
+ .output_mode = .Obj,
+ });
+ if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| {
+ break :blk p.ptr;
+ } else |err| {
+ return err;
+ }
+ } else null;
+
+ const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
+ const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
+ const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
+
var error_message: [*:0]const u8 = undefined;
if (self.target_machine.emitToFile(
self.llvm_module,
- self.object_pathZ.ptr,
- .ObjectFile,
&error_message,
- ).toBool()) {
+ comp.bin_file.options.optimize_mode == .Debug,
+ comp.bin_file.options.optimize_mode == .ReleaseSmall,
+ comp.time_report,
+ comp.bin_file.options.tsan,
+ comp.bin_file.options.lto,
+ emit_asm_path,
+ emit_bin_path,
+ emit_llvm_ir_path,
+ emit_llvm_bc_path,
+ )) {
defer llvm.disposeMessage(error_message);
- const stderr = std.io.getStdErr().writer();
- try stderr.print("LLVM failed to emit file: {s}\n", .{error_message});
+ const emit_asm_msg = emit_asm_path orelse "(none)";
+ const emit_bin_msg = emit_bin_path orelse "(none)";
+ const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
+ const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
+ log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
+ emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
+ error_message,
+ });
return error.FailedToEmit;
}
}
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index f3a1a72f3d..f45ea8f979 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -123,6 +123,9 @@ pub const Module = opaque {
pub const getNamedGlobal = LLVMGetNamedGlobal;
extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
+
+ pub const dump = LLVMDumpModule;
+ extern fn LLVMDumpModule(M: *const Module) void;
};
pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
@@ -250,31 +253,41 @@ pub const BasicBlock = opaque {
};
pub const TargetMachine = opaque {
- pub const create = LLVMCreateTargetMachine;
- extern fn LLVMCreateTargetMachine(
+ pub const create = ZigLLVMCreateTargetMachine;
+ extern fn ZigLLVMCreateTargetMachine(
T: *const Target,
Triple: [*:0]const u8,
- CPU: [*:0]const u8,
- Features: [*:0]const u8,
+ CPU: ?[*:0]const u8,
+ Features: ?[*:0]const u8,
Level: CodeGenOptLevel,
Reloc: RelocMode,
- CodeModel: CodeMode,
+ CodeModel: CodeModel,
+ function_sections: bool,
+ float_abi: ABIType,
+ abi_name: ?[*:0]const u8,
) *const TargetMachine;
pub const dispose = LLVMDisposeTargetMachine;
extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
- pub const emitToFile = LLVMTargetMachineEmitToFile;
- extern fn LLVMTargetMachineEmitToFile(
- *const TargetMachine,
+ pub const emitToFile = ZigLLVMTargetMachineEmitToFile;
+ extern fn ZigLLVMTargetMachineEmitToFile(
+ T: *const TargetMachine,
M: *const Module,
- Filename: [*:0]const u8,
- codegen: CodeGenFileType,
ErrorMessage: *[*:0]const u8,
- ) Bool;
+ is_debug: bool,
+ is_small: bool,
+ time_report: bool,
+ tsan: bool,
+ lto: bool,
+ asm_filename: ?[*:0]const u8,
+ bin_filename: ?[*:0]const u8,
+ llvm_ir_filename: ?[*:0]const u8,
+ bitcode_filename: ?[*:0]const u8,
+ ) bool;
};
-pub const CodeMode = enum(c_int) {
+pub const CodeModel = enum(c_int) {
Default,
JITDefault,
Tiny,
@@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) {
Default,
Static,
PIC,
- DynamicNoPic,
+ DynamicNoPIC,
ROPI,
RWPI,
ROPI_RWPI,
@@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) {
ObjectFile,
};
+pub const ABIType = enum(c_int) {
+ /// Target-specific (either soft or hard depending on triple, etc).
+ Default,
+ /// Soft float.
+ Soft,
+ // Hard float.
+ Hard,
+};
+
pub const Target = opaque {
pub const getFromTriple = LLVMGetTargetFromTriple;
extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;
diff --git a/src/link.zig b/src/link.zig
index 2403180ec8..85ff2ca603 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -191,7 +191,7 @@ pub const File = struct {
const use_stage1 = build_options.is_stage1 and options.use_stage1;
if (use_stage1 or options.emit == null) {
return switch (options.object_format) {
- .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
+ .coff => &(try Coff.createEmpty(allocator, options)).base,
.elf => &(try Elf.createEmpty(allocator, options)).base,
.macho => &(try MachO.createEmpty(allocator, options)).base,
.wasm => &(try Wasm.createEmpty(allocator, options)).base,
@@ -206,9 +206,10 @@ pub const File = struct {
const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
const sub_path = if (use_lld) blk: {
if (options.module == null) {
- // No point in opening a file, we would not write anything to it. Initialize with empty.
+ // No point in opening a file, we would not write anything to it.
+ // Initialize with empty.
return switch (options.object_format) {
- .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
+ .coff => &(try Coff.createEmpty(allocator, options)).base,
.elf => &(try Elf.createEmpty(allocator, options)).base,
.macho => &(try MachO.createEmpty(allocator, options)).base,
.plan9 => &(try Plan9.createEmpty(allocator, options)).base,
@@ -219,13 +220,16 @@ pub const File = struct {
.raw => return error.RawObjectFormatUnimplemented,
};
}
- // Open a temporary object file, not the final output file because we want to link with LLD.
- break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() });
+ // Open a temporary object file, not the final output file because we
+ // want to link with LLD.
+ break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{
+ emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),
+ });
} else emit.sub_path;
errdefer if (use_lld) allocator.free(sub_path);
const file: *File = switch (options.object_format) {
- .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
+ .coff => &(try Coff.openPath(allocator, sub_path, options)).base,
.elf => &(try Elf.openPath(allocator, sub_path, options)).base,
.macho => &(try MachO.openPath(allocator, sub_path, options)).base,
.plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
index 50ad6bc1a0..0bae2cc6cc 100644
--- a/src/link/Coff.zig
+++ b/src/link/Coff.zig
@@ -17,9 +17,9 @@ const link = @import("../link.zig");
const build_options = @import("build_options");
const Cache = @import("../Cache.zig");
const mingw = @import("../mingw.zig");
-const llvm_backend = @import("../codegen/llvm.zig");
const Air = @import("../Air.zig");
const Liveness = @import("../Liveness.zig");
+const LlvmObject = @import("../codegen/llvm.zig").Object;
const allocation_padding = 4 / 3;
const minimum_text_block_size = 64 * allocation_padding;
@@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff;
const msdos_stub = @embedFile("msdos-stub.bin");
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
-llvm_object: ?*llvm_backend.Object = null,
+llvm_object: ?*LlvmObject = null,
base: link.File,
ptr_width: PtrWidth,
@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
const self = try createEmpty(allocator, options);
errdefer self.base.destroy();
- self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
+ self.llvm_object = try LlvmObject.create(allocator, options);
return self;
}
@@ -657,10 +657,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
}
pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
- if (build_options.skip_non_native and
- builtin.object_format != .coff and
- builtin.object_format != .pe)
- {
+ if (build_options.skip_non_native and builtin.object_format != .coff) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
if (build_options.have_llvm) {
@@ -697,7 +694,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
}
pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
- if (build_options.skip_non_native and builtin.object_format != .coff and builtin.object_format != .pe) {
+ if (build_options.skip_non_native and builtin.object_format != .coff) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
if (build_options.have_llvm) {
@@ -823,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
- if (build_options.have_llvm)
- if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| {
+ return try llvm_object.flushModule(comp);
+ }
+ }
if (self.text_section_size_dirty) {
// Write the new raw size in the .text header
@@ -1398,8 +1398,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
}
pub fn deinit(self: *Coff) void {
- if (build_options.have_llvm)
- if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator);
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
+ }
self.text_block_free_list.deinit(self.base.allocator);
self.offset_table.deinit(self.base.allocator);
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 315dfb563b..c95af23026 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -25,9 +25,9 @@ const target_util = @import("../target.zig");
const glibc = @import("../glibc.zig");
const musl = @import("../musl.zig");
const Cache = @import("../Cache.zig");
-const llvm_backend = @import("../codegen/llvm.zig");
const Air = @import("../Air.zig");
const Liveness = @import("../Liveness.zig");
+const LlvmObject = @import("../codegen/llvm.zig").Object;
const default_entry_addr = 0x8000000;
@@ -38,7 +38,7 @@ base: File,
ptr_width: PtrWidth,
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
-llvm_object: ?*llvm_backend.Object = null,
+llvm_object: ?*LlvmObject = null,
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
/// Same order as in the file.
@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
const self = try createEmpty(allocator, options);
errdefer self.base.destroy();
- self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
+ self.llvm_object = try LlvmObject.create(allocator, options);
return self;
}
@@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
}
pub fn deinit(self: *Elf) void {
- if (build_options.have_llvm)
- if (self.llvm_object) |ir_module|
- ir_module.deinit(self.base.allocator);
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
+ }
self.sections.deinit(self.base.allocator);
self.program_headers.deinit(self.base.allocator);
@@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
if (build_options.have_llvm)
if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
- // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
- // Zig source code.
+ // TODO This linker code currently assumes there is only 1 compilation unit and it
+ // corresponds to the Zig source code.
const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
const target_endian = self.base.options.target.cpu.arch.endian();
@@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
// TODO: remove when stage2 can build compiler_rt.zig
if (!build_options.is_stage1) break :blk null;
+ // In the case of build-obj we include the compiler-rt symbols directly alongside
+ // the symbols of the root source file, in the same compilation unit.
+ if (is_obj) break :blk null;
+
if (is_exe_or_dyn_lib) {
break :blk comp.compiler_rt_static_lib.?.full_object_path;
} else {
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 02ea5856f4..8675295b2a 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -30,7 +30,7 @@ const DebugSymbols = @import("MachO/DebugSymbols.zig");
const Trie = @import("MachO/Trie.zig");
const CodeSignature = @import("MachO/CodeSignature.zig");
const Zld = @import("MachO/Zld.zig");
-const llvm_backend = @import("../codegen/llvm.zig");
+const LlvmObject = @import("../codegen/llvm.zig").Object;
usingnamespace @import("MachO/commands.zig");
@@ -39,7 +39,7 @@ pub const base_tag: File.Tag = File.Tag.macho;
base: File,
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
-llvm_object: ?*llvm_backend.Object = null,
+llvm_object: ?*LlvmObject = null,
/// Debug symbols bundle (or dSym).
d_sym: ?DebugSymbols = null,
@@ -355,7 +355,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
const self = try createEmpty(allocator, options);
errdefer self.base.destroy();
- self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
+ self.llvm_object = try LlvmObject.create(allocator, options);
return self;
}
@@ -989,6 +989,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
}
pub fn deinit(self: *MachO) void {
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
+ }
if (self.d_sym) |*ds| {
ds.deinit(self.base.allocator);
}
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index f478d2ee47..2ed6576033 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -19,7 +19,7 @@ const build_options = @import("build_options");
const wasi_libc = @import("../wasi_libc.zig");
const Cache = @import("../Cache.zig");
const TypedValue = @import("../TypedValue.zig");
-const llvm_backend = @import("../codegen/llvm.zig");
+const LlvmObject = @import("../codegen/llvm.zig").Object;
const Air = @import("../Air.zig");
const Liveness = @import("../Liveness.zig");
@@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm;
base: link.File,
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
-llvm_object: ?*llvm_backend.Object = null,
+llvm_object: ?*LlvmObject = null,
/// List of all function Decls to be written to the output file. The index of
/// each Decl in this list at the time of writing the binary is used as the
/// 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
const self = try createEmpty(allocator, options);
errdefer self.base.destroy();
- self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
+ self.llvm_object = try LlvmObject.create(allocator, options);
return self;
}
@@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
}
pub fn deinit(self: *Wasm) void {
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
+ }
for (self.symbols.items) |decl| {
decl.fn_link.wasm.functype.deinit(self.base.allocator);
decl.fn_link.wasm.code.deinit(self.base.allocator);
@@ -642,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
break :blk full_obj_path;
} else null;
- const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt)
+ const is_obj = self.base.options.output_mode == .Obj;
+
+ const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj)
comp.compiler_rt_static_lib.?.full_object_path
else
null;
diff --git a/src/main.zig b/src/main.zig
index 3b62bba410..d3c7d024a1 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -287,9 +287,9 @@ const usage_build_generic =
\\ .s Target-specific assembly source code
\\ .S Assembly with C preprocessor (requires LLVM extensions)
\\ .c C source code (requires LLVM extensions)
- \\ .cpp C++ source code (requires LLVM extensions)
- \\ Other C++ extensions: .C .cc .cxx
+ \\ .cxx .cc .C .cpp C++ source code (requires LLVM extensions)
\\ .m Objective-C source code (requires LLVM extensions)
+ \\ .bc LLVM IR Module (requires LLVM extensions)
\\
\\General Options:
\\ -h, --help Print this help and exit
@@ -301,6 +301,8 @@ const usage_build_generic =
\\ -fno-emit-asm (default) Do not output .s (assembly code)
\\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)
\\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR
+ \\ -femit-llvm-bc[=path] Produce a LLVM module as a .bc file (requires LLVM extensions)
+ \\ -fno-emit-llvm-bc (default) Do not produce a LLVM module as a .bc file
\\ -femit-h[=path] Generate a C header file (.h)
\\ -fno-emit-h (default) Do not generate a C header file (.h)
\\ -femit-docs[=path] Create a docs/ dir with html documentation
@@ -359,15 +361,14 @@ const usage_build_generic =
\\ --single-threaded Code assumes it is only used single-threaded
\\ -ofmt=[mode] Override target object format
\\ elf Executable and Linking Format
- \\ c Compile to C source code
+ \\ c C source code
\\ wasm WebAssembly
- \\ pe Portable Executable (Windows)
\\ coff Common Object File Format (Windows)
\\ macho macOS relocatables
\\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
\\ plan9 Plan 9 from Bell Labs object format
- \\ hex (planned) Intel IHEX
- \\ raw (planned) Dump machine code directly
+ \\ hex (planned feature) Intel IHEX
+ \\ raw (planned feature) Dump machine code directly
\\ -dirafter [dir] Add directory to AFTER include search path
\\ -isystem [dir] Add directory to SYSTEM include search path
\\ -I[dir] Add directory to include search path
@@ -384,8 +385,8 @@ const usage_build_generic =
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
\\ --sysroot [path] Set the system root directory (usually /)
\\ --version [ver] Dynamic library semver
- \\ -fsoname[=name] (Linux) Override the default SONAME value
- \\ -fno-soname (Linux) Disable emitting a SONAME
+ \\ -fsoname[=name] Override the default SONAME value
+ \\ -fno-soname Disable emitting a SONAME
\\ -fLLD Force using LLD as the linker
\\ -fno-LLD Prevent using LLD as the linker
\\ -fcompiler-rt Always include compiler-rt symbols in output
@@ -552,6 +553,7 @@ fn buildOutputType(
var emit_bin: EmitBin = .yes_default_path;
var emit_asm: Emit = .no;
var emit_llvm_ir: Emit = .no;
+ var emit_llvm_bc: Emit = .no;
var emit_docs: Emit = .no;
var emit_analysis: Emit = .no;
var target_arch_os_abi: []const u8 = "native";
@@ -1011,6 +1013,12 @@ fn buildOutputType(
emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
} else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
emit_llvm_ir = .no;
+ } else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
+ emit_llvm_bc = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) {
+ emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
+ emit_llvm_bc = .no;
} else if (mem.eql(u8, arg, "-femit-docs")) {
emit_docs = .yes_default_path;
} else if (mem.startsWith(u8, arg, "-femit-docs=")) {
@@ -1708,8 +1716,6 @@ fn buildOutputType(
break :blk .c;
} else if (mem.eql(u8, ofmt, "coff")) {
break :blk .coff;
- } else if (mem.eql(u8, ofmt, "pe")) {
- break :blk .pe;
} else if (mem.eql(u8, ofmt, "macho")) {
break :blk .macho;
} else if (mem.eql(u8, ofmt, "wasm")) {
@@ -1753,7 +1759,7 @@ fn buildOutputType(
};
const a_out_basename = switch (object_format) {
- .pe, .coff => "a.exe",
+ .coff => "a.exe",
else => "a.out",
};
@@ -1818,10 +1824,10 @@ fn buildOutputType(
var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
switch (emit_h) {
.yes => {
- fatal("unable to open directory from argument 'femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });
+ fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });
},
.yes_default_path => {
- fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_h_basename, @errorName(err) });
+ fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_h_basename, @errorName(err) });
},
.no => unreachable,
}
@@ -1832,10 +1838,10 @@ fn buildOutputType(
var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
switch (emit_asm) {
.yes => {
- fatal("unable to open directory from argument 'femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });
+ fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });
},
.yes_default_path => {
- fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });
+ fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });
},
.no => unreachable,
}
@@ -1846,16 +1852,30 @@ fn buildOutputType(
var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
switch (emit_llvm_ir) {
.yes => {
- fatal("unable to open directory from argument 'femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });
+ fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });
},
.yes_default_path => {
- fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });
+ fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });
},
.no => unreachable,
}
};
defer emit_llvm_ir_resolved.deinit();
+ const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
+ var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| {
+ switch (emit_llvm_bc) {
+ .yes => {
+ fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ emit_llvm_bc.yes, @errorName(err) });
+ },
+ .yes_default_path => {
+ fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_bc_basename, @errorName(err) });
+ },
+ .no => unreachable,
+ }
+ };
+ defer emit_llvm_bc_resolved.deinit();
+
const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
switch (emit_analysis) {
@@ -1991,6 +2011,7 @@ fn buildOutputType(
.emit_h = emit_h_resolved.data,
.emit_asm = emit_asm_resolved.data,
.emit_llvm_ir = emit_llvm_ir_resolved.data,
+ .emit_llvm_bc = emit_llvm_bc_resolved.data,
.emit_docs = emit_docs_resolved.data,
.emit_analysis = emit_analysis_resolved.data,
.link_mode = link_mode,
@@ -2396,11 +2417,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
// If a .pdb file is part of the expected output, we must also copy
// it into place here.
- const coff_or_pe = switch (comp.bin_file.options.object_format) {
- .coff, .pe => true,
- else => false,
- };
- const have_pdb = coff_or_pe and !comp.bin_file.options.strip;
+ const is_coff = comp.bin_file.options.object_format == .coff;
+ const have_pdb = is_coff and !comp.bin_file.options.strip;
if (have_pdb) {
// Replace `.out` or `.exe` with `.pdb` on both the source and destination
const src_bin_ext = fs.path.extension(bin_sub_path);
diff --git a/src/stage1.zig b/src/stage1.zig
index 2284e512ec..a00e036964 100644
--- a/src/stage1.zig
+++ b/src/stage1.zig
@@ -21,7 +21,6 @@ comptime {
assert(build_options.is_stage1);
assert(build_options.have_llvm);
if (!builtin.is_test) {
- _ = @import("compiler_rt");
@export(main, .{ .name = "main" });
}
}
@@ -95,6 +94,8 @@ pub const Module = extern struct {
emit_asm_len: usize,
emit_llvm_ir_ptr: [*]const u8,
emit_llvm_ir_len: usize,
+ emit_bitcode_ptr: [*]const u8,
+ emit_bitcode_len: usize,
emit_analysis_json_ptr: [*]const u8,
emit_analysis_json_len: usize,
emit_docs_ptr: [*]const u8,
@@ -124,6 +125,7 @@ pub const Module = extern struct {
valgrind_enabled: bool,
tsan_enabled: bool,
function_sections: bool,
+ include_compiler_rt: bool,
enable_stack_probing: bool,
red_zone: bool,
enable_time_report: bool,
diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp
index c673922335..8dfc8de6f9 100644
--- a/src/stage1/all_types.hpp
+++ b/src/stage1/all_types.hpp
@@ -2090,6 +2090,7 @@ struct CodeGen {
Buf h_file_output_path;
Buf asm_file_output_path;
Buf llvm_ir_file_output_path;
+ Buf bitcode_file_output_path;
Buf analysis_json_output_path;
Buf docs_output_path;
@@ -2149,6 +2150,7 @@ struct CodeGen {
bool have_stack_probing;
bool red_zone;
bool function_sections;
+ bool include_compiler_rt;
bool test_is_evented;
bool valgrind_enabled;
bool tsan_enabled;
diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp
index 9ab0e269a2..67d787427f 100644
--- a/src/stage1/codegen.cpp
+++ b/src/stage1/codegen.cpp
@@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) {
const char *asm_filename = nullptr;
const char *bin_filename = nullptr;
const char *llvm_ir_filename = nullptr;
+ const char *bitcode_filename = nullptr;
if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);
if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);
+ if (buf_len(&g->bitcode_file_output_path) != 0) bitcode_filename = buf_ptr(&g->bitcode_file_output_path);
- // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
- // pipeline multiple times if this is requested.
+ // Unfortunately, LLVM shits the bed when we ask for both binary and assembly.
+ // So we call the entire pipeline multiple times if this is requested.
if (asm_filename != nullptr && bin_filename != nullptr) {
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
- g->have_lto, nullptr, bin_filename, llvm_ir_filename))
+ g->have_lto, nullptr, bin_filename, llvm_ir_filename, nullptr))
{
- fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
+ fprintf(stderr, "LLVM failed to emit bin=%s, ir=%s: %s\n",
+ bin_filename, llvm_ir_filename, err_msg);
exit(1);
}
bin_filename = nullptr;
@@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) {
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
- g->have_lto, asm_filename, bin_filename, llvm_ir_filename))
+ g->have_lto, asm_filename, bin_filename, llvm_ir_filename, bitcode_filename))
{
- fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
+ fprintf(stderr, "LLVM failed to emit asm=%s, bin=%s, ir=%s, bc=%s: %s\n",
+ asm_filename, bin_filename, llvm_ir_filename, bitcode_filename,
+ err_msg);
exit(1);
}
@@ -9537,6 +9542,22 @@ static void gen_root_source(CodeGen *g) {
g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry;
assert(g->panic_fn != nullptr);
+ if (g->include_compiler_rt) {
+ Buf *import_target_path;
+ Buf full_path = BUF_INIT;
+ ZigType *compiler_rt_import;
+ if ((err = analyze_import(g, std_import, buf_create_from_str("./special/compiler_rt.zig"),
+ &compiler_rt_import, &import_target_path, &full_path)))
+ {
+ if (err == ErrorFileNotFound) {
+ fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path));
+ } else {
+ fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err));
+ }
+ exit(1);
+ }
+ }
+
if (!g->error_during_imports) {
semantic_analyze(g);
}
diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp
index e7c316f385..2fbab192a7 100644
--- a/src/stage1/stage1.cpp
+++ b/src/stage1/stage1.cpp
@@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);
buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);
buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len);
+ buf_init_from_mem(&g->bitcode_file_output_path, stage1->emit_bitcode_ptr, stage1->emit_bitcode_len);
buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);
buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len);
@@ -100,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
g->link_libc = stage1->link_libc;
g->link_libcpp = stage1->link_libcpp;
g->function_sections = stage1->function_sections;
+ g->include_compiler_rt = stage1->include_compiler_rt;
g->subsystem = stage1->subsystem;
diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h
index 3bdb5500cf..1e8eef5937 100644
--- a/src/stage1/stage1.h
+++ b/src/stage1/stage1.h
@@ -157,6 +157,9 @@ struct ZigStage1 {
const char *emit_llvm_ir_ptr;
size_t emit_llvm_ir_len;
+ const char *emit_bitcode_ptr;
+ size_t emit_bitcode_len;
+
const char *emit_analysis_json_ptr;
size_t emit_analysis_json_len;
@@ -193,6 +196,7 @@ struct ZigStage1 {
bool valgrind_enabled;
bool tsan_enabled;
bool function_sections;
+ bool include_compiler_rt;
bool enable_stack_probing;
bool red_zone;
bool enable_time_report;
diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp
index f2412a74ef..d07d4f9e37 100644
--- a/src/stage1/zig0.cpp
+++ b/src/stage1/zig0.cpp
@@ -39,6 +39,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --color [auto|off|on] enable or disable colored error messages\n"
" --name [name] override output name\n"
" -femit-bin=[path] Output machine code\n"
+ " -fcompiler-rt Always include compiler-rt symbols in output\n"
" --pkg-begin [name] [path] make pkg available to import and push current pkg\n"
" --pkg-end pop current pkg\n"
" -ODebug build with optimizations off and safety on\n"
@@ -266,6 +267,7 @@ int main(int argc, char **argv) {
const char *mcpu = nullptr;
bool single_threaded = false;
bool is_test_build = false;
+ bool include_compiler_rt = false;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
@@ -334,6 +336,8 @@ int main(int argc, char **argv) {
mcpu = arg + strlen("-mcpu=");
} else if (str_starts_with(arg, "-femit-bin=")) {
emit_bin_path = arg + strlen("-femit-bin=");
+ } else if (strcmp(arg, "-fcompiler-rt") == 0) {
+ include_compiler_rt = true;
} else if (i + 1 >= argc) {
fprintf(stderr, "Expected another argument after %s\n", arg);
return print_error_usage(arg0);
@@ -468,6 +472,7 @@ int main(int argc, char **argv) {
stage1->subsystem = subsystem;
stage1->pic = true;
stage1->is_single_threaded = single_threaded;
+ stage1->include_compiler_rt = include_compiler_rt;
zig_stage1_build_object(stage1);
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 6d40f4089a..d5d6f9f670 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -229,12 +229,14 @@ struct TimeTracerRAII {
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
char **error_message, bool is_debug,
bool is_small, bool time_report, bool tsan, bool lto,
- const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
+ const char *asm_filename, const char *bin_filename,
+ const char *llvm_ir_filename, const char *bitcode_filename)
{
TimePassesIsEnabled = time_report;
raw_fd_ostream *dest_asm_ptr = nullptr;
raw_fd_ostream *dest_bin_ptr = nullptr;
+ raw_fd_ostream *dest_bitcode_ptr = nullptr;
if (asm_filename) {
std::error_code EC;
@@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
return true;
}
}
+ if (bitcode_filename) {
+ std::error_code EC;
+ dest_bitcode_ptr = new(std::nothrow) raw_fd_ostream(bitcode_filename, EC, sys::fs::F_None);
+ if (EC) {
+ *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
+ return true;
+ }
+ }
std::unique_ptr<raw_fd_ostream> dest_asm(dest_asm_ptr),
- dest_bin(dest_bin_ptr);
+ dest_bin(dest_bin_ptr),
+ dest_bitcode(dest_bitcode_ptr);
+
auto PID = sys::Process::getProcessId();
std::string ProcName = "zig-";
@@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
if (dest_bin && lto) {
WriteBitcodeToFile(module, *dest_bin);
}
+ if (dest_bitcode) {
+ WriteBitcodeToFile(module, *dest_bitcode);
+ }
if (time_report) {
TimerGroup::printAll(errs());
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index a2b3c6b92e..a771491138 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
char **error_message, bool is_debug,
bool is_small, bool time_report, bool tsan, bool lto,
- const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);
+ const char *asm_filename, const char *bin_filename,
+ const char *llvm_ir_filename, const char *bitcode_filename);
enum ZigLLVMABIType {