Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stage2: More improvements to self-hosted LLVM backend #7598

Merged
merged 11 commits into from
Jan 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 111 additions & 89 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub fn build(b: *Builder) !void {
exe.addBuildOption(bool, "have_llvm", enable_llvm);
if (enable_llvm) {
const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);

if (is_stage1) {
exe.addIncludeDir("src");
exe.addIncludeDir("deps/SoftFloat-3e/source/include");
Expand All @@ -109,28 +110,8 @@ pub fn build(b: *Builder) !void {
softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" });
exe.linkLibrary(softfloat);

const exe_cflags = [_][]const u8{
"-std=c++14",
"-D__STDC_CONSTANT_MACROS",
"-D__STDC_FORMAT_MACROS",
"-D__STDC_LIMIT_MACROS",
"-D_GNU_SOURCE",
"-fvisibility-inlines-hidden",
"-fno-exceptions",
"-fno-rtti",
"-Werror=type-limits",
"-Wno-missing-braces",
"-Wno-comment",
};
exe.addCSourceFiles(&stage1_sources, &exe_cflags);
exe.addCSourceFiles(&optimized_c_sources, &[_][]const u8{ "-std=c99", "-O3" });
if (cmake_cfg == null) {
// We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
// in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
// unavailable when LLVM is compiled in Release mode.
const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags);
}
}
if (cmake_cfg) |cfg| {
// Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
Expand All @@ -139,79 +120,14 @@ pub fn build(b: *Builder) !void {
if (cfg.cmake_prefix_path.len > 0) {
b.addSearchPrefix(cfg.cmake_prefix_path);
}
exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
cfg.cmake_binary_dir,
"zigcpp",
b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }),
}) catch unreachable);
assert(cfg.lld_include_dir.len != 0);
exe.addIncludeDir(cfg.lld_include_dir);
addCMakeLibraryList(exe, cfg.clang_libraries);
addCMakeLibraryList(exe, cfg.lld_libraries);
addCMakeLibraryList(exe, cfg.llvm_libraries);

const need_cpp_includes = tracy != null;

// System -lc++ must be used because in this code path we are attempting to link
// against system-provided LLVM, Clang, LLD.
if (exe.target.getOsTag() == .linux) {
// First we try to static link against gcc libstdc++. If that doesn't work,
// we fall back to -lc++ and cross our fingers.
addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
error.RequiredLibraryNotFound => {
exe.linkSystemLibrary("c++");
},
else => |e| return e,
};

exe.linkSystemLibrary("pthread");
} else if (exe.target.isFreeBSD()) {
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
exe.linkSystemLibrary("pthread");
} else if (exe.target.getOsTag() == .openbsd) {
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
} else if (exe.target.isDarwin()) {
if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) {
// Compiler is GCC.
try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes);
exe.linkSystemLibrary("pthread");
// TODO LLD cannot perform this link.
// Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead.
// See https://github.com/ziglang/zig/issues/1535
} else |err| switch (err) {
error.RequiredLibraryNotFound => {
// System compiler, not gcc.
exe.linkSystemLibrary("c++");
},
else => |e| return e,
}
}

if (cfg.dia_guids_lib.len != 0) {
exe.addObjectFile(cfg.dia_guids_lib);
}
try addCmakeCfgOptionsToExe(b, cfg, tracy, exe);
try addCmakeCfgOptionsToExe(b, cfg, tracy, test_stage2);
} else {
// Here we are -Denable-llvm but no cmake integration.
for (clang_libs) |lib_name| {
exe.linkSystemLibrary(lib_name);
}

for (lld_libs) |lib_name| {
exe.linkSystemLibrary(lib_name);
}

for (llvm_libs) |lib_name| {
exe.linkSystemLibrary(lib_name);
}

// This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
exe.linkSystemLibrary("c++");

if (target.getOs().tag == .windows) {
exe.linkSystemLibrary("version");
exe.linkSystemLibrary("uuid");
}
try addStaticLlvmOptionsToExe(exe);
try addStaticLlvmOptionsToExe(test_stage2);
}
}
if (link_libc) {
Expand Down Expand Up @@ -357,6 +273,112 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(docs_step);
}

const exe_cflags = [_][]const u8{
"-std=c++14",
"-D__STDC_CONSTANT_MACROS",
"-D__STDC_FORMAT_MACROS",
"-D__STDC_LIMIT_MACROS",
"-D_GNU_SOURCE",
"-fvisibility-inlines-hidden",
"-fno-exceptions",
"-fno-rtti",
"-Werror=type-limits",
"-Wno-missing-braces",
"-Wno-comment",
};

fn addCmakeCfgOptionsToExe(
b: *Builder,
cfg: CMakeConfig,
tracy: ?[]const u8,
exe: *std.build.LibExeObjStep,
) !void {
exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
cfg.cmake_binary_dir,
"zigcpp",
b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }),
}) catch unreachable);
assert(cfg.lld_include_dir.len != 0);
exe.addIncludeDir(cfg.lld_include_dir);
addCMakeLibraryList(exe, cfg.clang_libraries);
addCMakeLibraryList(exe, cfg.lld_libraries);
addCMakeLibraryList(exe, cfg.llvm_libraries);

const need_cpp_includes = tracy != null;

// System -lc++ must be used because in this code path we are attempting to link
// against system-provided LLVM, Clang, LLD.
if (exe.target.getOsTag() == .linux) {
// First we try to static link against gcc libstdc++. If that doesn't work,
// we fall back to -lc++ and cross our fingers.
addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
error.RequiredLibraryNotFound => {
exe.linkSystemLibrary("c++");
},
else => |e| return e,
};

exe.linkSystemLibrary("pthread");
} else if (exe.target.isFreeBSD()) {
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
exe.linkSystemLibrary("pthread");
} else if (exe.target.getOsTag() == .openbsd) {
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
} else if (exe.target.isDarwin()) {
if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) {
// Compiler is GCC.
try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes);
exe.linkSystemLibrary("pthread");
// TODO LLD cannot perform this link.
// Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead.
// See https://github.com/ziglang/zig/issues/1535
} else |err| switch (err) {
error.RequiredLibraryNotFound => {
// System compiler, not gcc.
exe.linkSystemLibrary("c++");
},
else => |e| return e,
}
}

if (cfg.dia_guids_lib.len != 0) {
exe.addObjectFile(cfg.dia_guids_lib);
}
}

fn addStaticLlvmOptionsToExe(
exe: *std.build.LibExeObjStep,
) !void {
// Adds the Zig C++ sources which both stage1 and stage2 need.
//
// We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
// in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
// unavailable when LLVM is compiled in Release mode.
const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags);

for (clang_libs) |lib_name| {
exe.linkSystemLibrary(lib_name);
}

for (lld_libs) |lib_name| {
exe.linkSystemLibrary(lib_name);
}

for (llvm_libs) |lib_name| {
exe.linkSystemLibrary(lib_name);
}

// This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
exe.linkSystemLibrary("c++");

if (exe.target.getOs().tag == .windows) {
exe.linkSystemLibrary("version");
exe.linkSystemLibrary("uuid");
}
}

fn addCxxKnownPath(
b: *Builder,
ctx: CMakeConfig,
Expand Down
3 changes: 2 additions & 1 deletion src/Cache.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const Allocator = std.mem.Allocator;
/// This protection is conditionally compiled depending on `want_debug_deadlock`.
var all_cache_digest_set: std.AutoHashMapUnmanaged(BinDigest, void) = .{};
var all_cache_digest_lock: std.Mutex = .{};
const want_debug_deadlock = std.debug.runtime_safety;
// TODO: Figure out how to make sure that `all_cache_digest_set` does not leak memory!
pub const want_debug_deadlock = false;
Comment on lines +19 to +20
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrewrk To make sure that the compiler doesnt leak memory we have to call deinit on all_cache_digest_set, however when I tried to do that I ran into segfaults. I'm not sure how to solve that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call should go into cleanExit in main.zig. If you show how to repro the segfault I'll take a look

const DebugBinDigest = if (want_debug_deadlock) BinDigest else void;
const null_debug_bin_digest = if (want_debug_deadlock) ([1]u8{0} ** bin_digest_len) else {};

Expand Down
5 changes: 5 additions & 0 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,7 @@ pub fn destroy(self: *Compilation) void {

const gpa = self.gpa;
self.work_queue.deinit();
self.c_object_work_queue.deinit();

{
var it = self.crt_files.iterator();
Expand Down Expand Up @@ -1202,6 +1203,10 @@ pub fn destroy(self: *Compilation) void {
crt_file.deinit(gpa);
}

if (self.glibc_so_files) |*glibc_file| {
glibc_file.deinit(gpa);
}

for (self.c_object_table.items()) |entry| {
entry.key.destroy(gpa);
}
Expand Down
2 changes: 1 addition & 1 deletion src/DepTokenizer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
try out.writeAll("\n");
try printSection(out, "<<<< input", input);
try printSection(out, "==== expect", expect);
try printSection(out, ">>>> got", got);
try printSection(out, ">>>> got", buffer.items);
try printRuler(out);

testing.expect(false);
Expand Down
7 changes: 5 additions & 2 deletions src/link/Coff.zig
Original file line number Diff line number Diff line change
Expand Up @@ -811,8 +811,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
// If there is no Zig code to compile, then we should skip flushing the output file because it
// will not be part of the linker line anyway.
const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
if (use_stage1) {
// Both stage1 and stage2 LLVM backend put the object file in the cache directory.
if (self.base.options.use_llvm) {
// Stage2 has to call flushModule since that outputs the LLVM object file.
if (!build_options.is_stage1) try self.flushModule(comp);

const obj_basename = try std.zig.binNameAlloc(arena, .{
.root_name = self.base.options.root_name,
.target = self.base.options.target,
Expand Down
7 changes: 5 additions & 2 deletions src/link/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1251,8 +1251,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
// If there is no Zig code to compile, then we should skip flushing the output file because it
// will not be part of the linker line anyway.
const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
if (use_stage1) {
// Both stage1 and stage2 LLVM backend put the object file in the cache directory.
if (self.base.options.use_llvm) {
// Stage2 has to call flushModule since that outputs the LLVM object file.
if (!build_options.is_stage1) try self.flushModule(comp);

const obj_basename = try std.zig.binNameAlloc(arena, .{
.root_name = self.base.options.root_name,
.target = self.base.options.target,
Expand Down
Loading