mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 11:13:08 +00:00
stage2: caching system integration & Module/Compilation splitting
* update to the new cache hash API
* std.Target defaultVersionRange moves to std.Target.Os.Tag
* std.Target.Os gains getVersionRange which returns a tagged union
* start the process of splitting Module into Compilation and "zig
module".
- The parts of Module having to do with only compiling zig code are
extracted into ZigModule.zig.
- Next step is to rename Module to Compilation.
- After that rename ZigModule back to Module.
* implement proper cache hash usage when compiling C objects, and
properly manage the file lock of the build artifacts.
* make versions optional to match recent changes to master branch.
* proper cache hash integration for compiling zig code
* proper cache hash integration for linking even when not compiling zig
code.
* ELF LLD linking integrates with the caching system. A comment from
the source code:
Here we want to determine whether we can save time by not invoking LLD when the
output is unchanged. None of the linker options or the object files that are being
linked are in the hash that namespaces the directory we are outputting to. Therefore,
we must hash those now, and the resulting digest will form the "id" of the linking
job we are about to perform.
After a successful link, we store the id in the metadata of a symlink named "id.txt" in
the artifact directory. So, now, we check if this symlink exists, and if it matches
our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
* implement disable_c_depfile option
* add tracy to a few more functions
This commit is contained in:
parent
1baa56a25f
commit
2a8fc1a18e
@ -82,7 +82,7 @@ pub const HashHelper = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
|
pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
|
||||||
hh.add(list_of_bytes.items.len);
|
hh.add(list_of_bytes.len);
|
||||||
for (list_of_bytes) |bytes| hh.addBytes(bytes);
|
for (list_of_bytes) |bytes| hh.addBytes(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,13 @@ pub const Target = struct {
|
|||||||
else => return ".so",
|
else => return ".so",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn defaultVersionRange(tag: Tag) Os {
|
||||||
|
return .{
|
||||||
|
.tag = tag,
|
||||||
|
.version_range = VersionRange.default(tag),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Based on NTDDI version constants from
|
/// Based on NTDDI version constants from
|
||||||
@ -290,11 +297,32 @@ pub const Target = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn defaultVersionRange(tag: Tag) Os {
|
pub const TaggedVersionRange = union(enum) {
|
||||||
return .{
|
none: void,
|
||||||
.tag = tag,
|
semver: Version.Range,
|
||||||
.version_range = VersionRange.default(tag),
|
linux: LinuxVersionRange,
|
||||||
|
windows: WindowsVersion.Range,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Provides a tagged union. `Target` does not store the tag because it is
|
||||||
|
/// redundant with the OS tag; this function abstracts that part away.
|
||||||
|
pub fn getVersionRange(self: Os) TaggedVersionRange {
|
||||||
|
switch (self.tag) {
|
||||||
|
.linux => return TaggedVersionRange{ .linux = self.version_range.linux },
|
||||||
|
.windows => return TaggedVersionRange{ .windows = self.version_range.windows },
|
||||||
|
|
||||||
|
.freebsd,
|
||||||
|
.macosx,
|
||||||
|
.ios,
|
||||||
|
.tvos,
|
||||||
|
.watchos,
|
||||||
|
.netbsd,
|
||||||
|
.openbsd,
|
||||||
|
.dragonfly,
|
||||||
|
=> return TaggedVersionRange{ .semver = self.version_range.semver },
|
||||||
|
|
||||||
|
else => return .none,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if system is guaranteed to be at least `version` or older than `version`.
|
/// Checks if system is guaranteed to be at least `version` or older than `version`.
|
||||||
|
|||||||
@ -375,7 +375,7 @@ pub const CrossTarget = struct {
|
|||||||
// `Target.current.os` works when doing `zig build` because Zig generates a build executable using
|
// `Target.current.os` works when doing `zig build` because Zig generates a build executable using
|
||||||
// native OS version range. However this will not be accurate otherwise, and
|
// native OS version range. However this will not be accurate otherwise, and
|
||||||
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
|
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
|
||||||
var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os;
|
var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange() else Target.current.os;
|
||||||
|
|
||||||
if (self.os_version_min) |min| switch (min) {
|
if (self.os_version_min) |min| switch (min) {
|
||||||
.none => {},
|
.none => {},
|
||||||
|
|||||||
@ -203,7 +203,7 @@ pub const NativeTargetInfo = struct {
|
|||||||
/// deinitialization method.
|
/// deinitialization method.
|
||||||
/// TODO Remove the Allocator requirement from this function.
|
/// TODO Remove the Allocator requirement from this function.
|
||||||
pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
|
pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
|
||||||
var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
|
var os = cross_target.getOsTag().defaultVersionRange();
|
||||||
if (cross_target.os_tag == null) {
|
if (cross_target.os_tag == null) {
|
||||||
switch (Target.current.os.tag) {
|
switch (Target.current.os.tag) {
|
||||||
.linux => {
|
.linux => {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
3238
src-self-hosted/ZigModule.zig
Normal file
3238
src-self-hosted/ZigModule.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ const Type = @import("type.zig").Type;
|
|||||||
const TypedValue = @import("TypedValue.zig");
|
const TypedValue = @import("TypedValue.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const zir = @import("zir.zig");
|
const zir = @import("zir.zig");
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("ZigModule.zig");
|
||||||
const ast = std.zig.ast;
|
const ast = std.zig.ast;
|
||||||
const trace = @import("tracy.zig").trace;
|
const trace = @import("tracy.zig").trace;
|
||||||
const Scope = Module.Scope;
|
const Scope = Module.Scope;
|
||||||
|
|||||||
@ -7,8 +7,9 @@ const Type = @import("type.zig").Type;
|
|||||||
const Value = @import("value.zig").Value;
|
const Value = @import("value.zig").Value;
|
||||||
const TypedValue = @import("TypedValue.zig");
|
const TypedValue = @import("TypedValue.zig");
|
||||||
const link = @import("link.zig");
|
const link = @import("link.zig");
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("ZigModule.zig");
|
||||||
const ErrorMsg = Module.ErrorMsg;
|
const Compilation = @import("Module.zig");
|
||||||
|
const ErrorMsg = Compilation.ErrorMsg;
|
||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
const trace = @import("tracy.zig").trace;
|
const trace = @import("tracy.zig").trace;
|
||||||
@ -50,7 +51,7 @@ pub const Result = union(enum) {
|
|||||||
appended: void,
|
appended: void,
|
||||||
/// The value is available externally, `code` is unused.
|
/// The value is available externally, `code` is unused.
|
||||||
externally_managed: []const u8,
|
externally_managed: []const u8,
|
||||||
fail: *Module.ErrorMsg,
|
fail: *ErrorMsg,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const GenerateSymbolError = error{
|
pub const GenerateSymbolError = error{
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const link = @import("../link.zig");
|
const link = @import("../link.zig");
|
||||||
const Module = @import("../Module.zig");
|
const Module = @import("../ZigModule.zig");
|
||||||
|
|
||||||
const Inst = @import("../ir.zig").Inst;
|
const Inst = @import("../ir.zig").Inst;
|
||||||
const Value = @import("../value.zig").Value;
|
const Value = @import("../value.zig").Value;
|
||||||
|
|||||||
@ -5,7 +5,8 @@ const assert = std.debug.assert;
|
|||||||
const leb = std.debug.leb;
|
const leb = std.debug.leb;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
|
|
||||||
const Decl = @import("../Module.zig").Decl;
|
const Module = @import("../ZigModule.zig");
|
||||||
|
const Decl = Module.Decl;
|
||||||
const Inst = @import("../ir.zig").Inst;
|
const Inst = @import("../ir.zig").Inst;
|
||||||
const Type = @import("../type.zig").Type;
|
const Type = @import("../type.zig").Type;
|
||||||
const Value = @import("../value.zig").Value;
|
const Value = @import("../value.zig").Value;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const mem = std.mem;
|
|||||||
const Module = @import("Module.zig");
|
const Module = @import("Module.zig");
|
||||||
const path = std.fs.path;
|
const path = std.fs.path;
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
const trace = @import("tracy.zig").trace;
|
||||||
|
|
||||||
pub const Lib = struct {
|
pub const Lib = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
@ -54,6 +55,9 @@ pub const LoadMetaDataError = error{
|
|||||||
/// This function will emit a log error when there is a problem with the zig installation and then return
|
/// This function will emit a log error when there is a problem with the zig installation and then return
|
||||||
/// `error.ZigInstallationCorrupt`.
|
/// `error.ZigInstallationCorrupt`.
|
||||||
pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
|
pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||||
errdefer arena_allocator.deinit();
|
errdefer arena_allocator.deinit();
|
||||||
const arena = &arena_allocator.allocator;
|
const arena = &arena_allocator.allocator;
|
||||||
@ -584,6 +588,9 @@ fn lib_path(mod: *Module, arena: *Allocator, sub_path: []const u8) ![]const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.CSourceFile) !void {
|
fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.CSourceFile) !void {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
// TODO: This is extracted into a local variable to work around a stage1 miscompilation.
|
// TODO: This is extracted into a local variable to work around a stage1 miscompilation.
|
||||||
const emit_bin = Module.EmitLoc{
|
const emit_bin = Module.EmitLoc{
|
||||||
.directory = null, // Put it in the cache directory.
|
.directory = null, // Put it in the cache directory.
|
||||||
@ -618,8 +625,11 @@ fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.C
|
|||||||
try sub_module.update();
|
try sub_module.update();
|
||||||
|
|
||||||
try mod.crt_files.ensureCapacity(mod.gpa, mod.crt_files.count() + 1);
|
try mod.crt_files.ensureCapacity(mod.gpa, mod.crt_files.count() + 1);
|
||||||
const artifact_path = try std.fs.path.join(mod.gpa, &[_][]const u8{
|
const artifact_path = if (sub_module.bin_file.options.directory.path) |p|
|
||||||
sub_module.zig_cache_artifact_directory.path.?, basename,
|
try std.fs.path.join(mod.gpa, &[_][]const u8{ p, basename })
|
||||||
});
|
else
|
||||||
|
try mod.gpa.dupe(u8, basename);
|
||||||
|
|
||||||
|
// TODO obtain a lock on the artifact and put that in crt_files as well.
|
||||||
mod.crt_files.putAssumeCapacityNoClobber(basename, artifact_path);
|
mod.crt_files.putAssumeCapacityNoClobber(basename, artifact_path);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Value = @import("value.zig").Value;
|
const Value = @import("value.zig").Value;
|
||||||
const Type = @import("type.zig").Type;
|
const Type = @import("type.zig").Type;
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("ZigModule.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const codegen = @import("codegen.zig");
|
const codegen = @import("codegen.zig");
|
||||||
const ast = std.zig.ast;
|
const ast = std.zig.ast;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Module = @import("Module.zig");
|
const Compilation = @import("Module.zig");
|
||||||
|
const ZigModule = @import("ZigModule.zig");
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const trace = @import("tracy.zig").trace;
|
const trace = @import("tracy.zig").trace;
|
||||||
const Package = @import("Package.zig");
|
const Package = @import("Package.zig");
|
||||||
@ -12,7 +13,7 @@ pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ b
|
|||||||
|
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
/// Where the output will go.
|
/// Where the output will go.
|
||||||
directory: Module.Directory,
|
directory: Compilation.Directory,
|
||||||
/// Path to the output file, relative to `directory`.
|
/// Path to the output file, relative to `directory`.
|
||||||
sub_path: []const u8,
|
sub_path: []const u8,
|
||||||
target: std.Target,
|
target: std.Target,
|
||||||
@ -21,7 +22,9 @@ pub const Options = struct {
|
|||||||
object_format: std.builtin.ObjectFormat,
|
object_format: std.builtin.ObjectFormat,
|
||||||
optimize_mode: std.builtin.Mode,
|
optimize_mode: std.builtin.Mode,
|
||||||
root_name: []const u8,
|
root_name: []const u8,
|
||||||
root_pkg: ?*const Package,
|
/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
|
||||||
|
/// TODO rename Module to Compilation and then (as a separate commit) ZigModule to Module.
|
||||||
|
zig_module: ?*ZigModule,
|
||||||
dynamic_linker: ?[]const u8 = null,
|
dynamic_linker: ?[]const u8 = null,
|
||||||
/// Used for calculating how much space to reserve for symbols in case the binary file
|
/// Used for calculating how much space to reserve for symbols in case the binary file
|
||||||
/// does not already have a symbol table.
|
/// does not already have a symbol table.
|
||||||
@ -71,7 +74,7 @@ pub const Options = struct {
|
|||||||
lib_dirs: []const []const u8 = &[0][]const u8{},
|
lib_dirs: []const []const u8 = &[0][]const u8{},
|
||||||
rpath_list: []const []const u8 = &[0][]const u8{},
|
rpath_list: []const []const u8 = &[0][]const u8{},
|
||||||
|
|
||||||
version: std.builtin.Version,
|
version: ?std.builtin.Version,
|
||||||
libc_installation: ?*const LibCInstallation,
|
libc_installation: ?*const LibCInstallation,
|
||||||
|
|
||||||
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
|
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
|
||||||
@ -184,7 +187,7 @@ pub const File = struct {
|
|||||||
|
|
||||||
/// May be called before or after updateDeclExports but must be called
|
/// May be called before or after updateDeclExports but must be called
|
||||||
/// after allocateDeclIndexes for any given Decl.
|
/// after allocateDeclIndexes for any given Decl.
|
||||||
pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
|
pub fn updateDecl(base: *File, module: *ZigModule, decl: *ZigModule.Decl) !void {
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
|
.coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
|
||||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
|
.elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
|
||||||
@ -194,7 +197,7 @@ pub const File = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
|
pub fn updateDeclLineNumber(base: *File, module: *ZigModule, decl: *ZigModule.Decl) !void {
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
|
.coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
|
||||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
|
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
|
||||||
@ -205,7 +208,7 @@ pub const File = struct {
|
|||||||
|
|
||||||
/// Must be called before any call to updateDecl or updateDeclExports for
|
/// Must be called before any call to updateDecl or updateDeclExports for
|
||||||
/// any given Decl.
|
/// any given Decl.
|
||||||
pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
|
pub fn allocateDeclIndexes(base: *File, decl: *ZigModule.Decl) !void {
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
|
.coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
|
||||||
.elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
|
.elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
|
||||||
@ -256,20 +259,20 @@ pub const File = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(base: *File, module: *Module) !void {
|
pub fn flush(base: *File, comp: *Compilation) !void {
|
||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
try switch (base.tag) {
|
try switch (base.tag) {
|
||||||
.coff => @fieldParentPtr(Coff, "base", base).flush(module),
|
.coff => @fieldParentPtr(Coff, "base", base).flush(comp),
|
||||||
.elf => @fieldParentPtr(Elf, "base", base).flush(module),
|
.elf => @fieldParentPtr(Elf, "base", base).flush(comp),
|
||||||
.macho => @fieldParentPtr(MachO, "base", base).flush(module),
|
.macho => @fieldParentPtr(MachO, "base", base).flush(comp),
|
||||||
.c => @fieldParentPtr(C, "base", base).flush(module),
|
.c => @fieldParentPtr(C, "base", base).flush(comp),
|
||||||
.wasm => @fieldParentPtr(Wasm, "base", base).flush(module),
|
.wasm => @fieldParentPtr(Wasm, "base", base).flush(comp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn freeDecl(base: *File, decl: *Module.Decl) void {
|
pub fn freeDecl(base: *File, decl: *ZigModule.Decl) void {
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
|
.coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
|
||||||
.elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
|
.elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
|
||||||
@ -293,9 +296,9 @@ pub const File = struct {
|
|||||||
/// allocateDeclIndexes for any given Decl.
|
/// allocateDeclIndexes for any given Decl.
|
||||||
pub fn updateDeclExports(
|
pub fn updateDeclExports(
|
||||||
base: *File,
|
base: *File,
|
||||||
module: *Module,
|
module: *ZigModule,
|
||||||
decl: *const Module.Decl,
|
decl: *const ZigModule.Decl,
|
||||||
exports: []const *Module.Export,
|
exports: []const *ZigModule.Export,
|
||||||
) !void {
|
) !void {
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
|
.coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
|
||||||
@ -306,7 +309,7 @@ pub const File = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
|
pub fn getDeclVAddr(base: *File, decl: *const ZigModule.Decl) u64 {
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
|
.coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
|
||||||
.elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
|
.elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
|
||||||
|
|||||||
@ -2,7 +2,8 @@ const std = @import("std");
|
|||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Module = @import("../Module.zig");
|
const Module = @import("../ZigModule.zig");
|
||||||
|
const Compilation = @import("../Module.zig");
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const codegen = @import("../codegen/c.zig");
|
const codegen = @import("../codegen/c.zig");
|
||||||
const link = @import("../link.zig");
|
const link = @import("../link.zig");
|
||||||
@ -20,7 +21,7 @@ main: std.ArrayList(u8),
|
|||||||
called: std.StringHashMap(void),
|
called: std.StringHashMap(void),
|
||||||
need_stddef: bool = false,
|
need_stddef: bool = false,
|
||||||
need_stdint: bool = false,
|
need_stdint: bool = false,
|
||||||
error_msg: *Module.ErrorMsg = undefined,
|
error_msg: *Compilation.ErrorMsg = undefined,
|
||||||
|
|
||||||
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
|
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {
|
||||||
assert(options.object_format == .c);
|
assert(options.object_format == .c);
|
||||||
@ -51,7 +52,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
|
pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
|
||||||
self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
|
self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);
|
||||||
return error.AnalysisFail;
|
return error.AnalysisFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(self: *C, module: *Module) !void {
|
pub fn flush(self: *C, comp: *Compilation) !void {
|
||||||
const writer = self.base.file.?.writer();
|
const writer = self.base.file.?.writer();
|
||||||
try writer.writeAll(@embedFile("cbe.h"));
|
try writer.writeAll(@embedFile("cbe.h"));
|
||||||
var includes = false;
|
var includes = false;
|
||||||
|
|||||||
@ -7,7 +7,8 @@ const assert = std.debug.assert;
|
|||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
|
|
||||||
const trace = @import("../tracy.zig").trace;
|
const trace = @import("../tracy.zig").trace;
|
||||||
const Module = @import("../Module.zig");
|
const Module = @import("../ZigModule.zig");
|
||||||
|
const Compilation = @import("../Module.zig");
|
||||||
const codegen = @import("../codegen.zig");
|
const codegen = @import("../codegen.zig");
|
||||||
const link = @import("../link.zig");
|
const link = @import("../link.zig");
|
||||||
|
|
||||||
@ -732,7 +733,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
|
|||||||
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
||||||
module.failed_exports.putAssumeCapacityNoClobber(
|
module.failed_exports.putAssumeCapacityNoClobber(
|
||||||
exp,
|
exp,
|
||||||
try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
|
try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -743,14 +744,14 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
|
|||||||
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
||||||
module.failed_exports.putAssumeCapacityNoClobber(
|
module.failed_exports.putAssumeCapacityNoClobber(
|
||||||
exp,
|
exp,
|
||||||
try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
|
try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(self: *Coff, module: *Module) !void {
|
pub fn flush(self: *Coff, comp: *Compilation) !void {
|
||||||
if (self.text_section_size_dirty) {
|
if (self.text_section_size_dirty) {
|
||||||
// Write the new raw size in the .text header
|
// Write the new raw size in the .text header
|
||||||
var buf: [4]u8 = undefined;
|
var buf: [4]u8 = undefined;
|
||||||
|
|||||||
@ -3,7 +3,8 @@ const mem = std.mem;
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ir = @import("../ir.zig");
|
const ir = @import("../ir.zig");
|
||||||
const Module = @import("../Module.zig");
|
const Module = @import("../ZigModule.zig");
|
||||||
|
const Compilation = @import("../Module.zig");
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const elf = std.elf;
|
const elf = std.elf;
|
||||||
const codegen = @import("../codegen.zig");
|
const codegen = @import("../codegen.zig");
|
||||||
@ -122,6 +123,9 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
|
|||||||
dbg_info_decl_first: ?*TextBlock = null,
|
dbg_info_decl_first: ?*TextBlock = null,
|
||||||
dbg_info_decl_last: ?*TextBlock = null,
|
dbg_info_decl_last: ?*TextBlock = null,
|
||||||
|
|
||||||
|
/// Prevents other processes from clobbering the output file this is linking.
|
||||||
|
lock: ?std.cache_hash.Lock = null,
|
||||||
|
|
||||||
/// `alloc_num / alloc_den` is the factor of padding when allocating.
|
/// `alloc_num / alloc_den` is the factor of padding when allocating.
|
||||||
const alloc_num = 4;
|
const alloc_num = 4;
|
||||||
const alloc_den = 3;
|
const alloc_den = 3;
|
||||||
@ -285,7 +289,21 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn releaseLock(self: *Elf) void {
|
||||||
|
if (self.lock) |*lock| {
|
||||||
|
lock.release();
|
||||||
|
self.lock = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toOwnedLock(self: *Elf) std.cache_hash.Lock {
|
||||||
|
const lock = self.lock.?;
|
||||||
|
self.lock = null;
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Elf) void {
|
pub fn deinit(self: *Elf) void {
|
||||||
|
self.releaseLock();
|
||||||
self.sections.deinit(self.base.allocator);
|
self.sections.deinit(self.base.allocator);
|
||||||
self.program_headers.deinit(self.base.allocator);
|
self.program_headers.deinit(self.base.allocator);
|
||||||
self.shstrtab.deinit(self.base.allocator);
|
self.shstrtab.deinit(self.base.allocator);
|
||||||
@ -709,20 +727,24 @@ pub const abbrev_base_type = 4;
|
|||||||
pub const abbrev_pad1 = 5;
|
pub const abbrev_pad1 = 5;
|
||||||
pub const abbrev_parameter = 6;
|
pub const abbrev_parameter = 6;
|
||||||
|
|
||||||
pub fn flush(self: *Elf, module: *Module) !void {
|
pub fn flush(self: *Elf, comp: *Compilation) !void {
|
||||||
if (build_options.have_llvm and self.base.options.use_lld) {
|
if (build_options.have_llvm and self.base.options.use_lld) {
|
||||||
return self.linkWithLLD(module);
|
return self.linkWithLLD(comp);
|
||||||
} else {
|
} else {
|
||||||
switch (self.base.options.effectiveOutputMode()) {
|
switch (self.base.options.effectiveOutputMode()) {
|
||||||
.Exe, .Obj => {},
|
.Exe, .Obj => {},
|
||||||
.Lib => return error.TODOImplementWritingLibFiles,
|
.Lib => return error.TODOImplementWritingLibFiles,
|
||||||
}
|
}
|
||||||
return self.flushInner(module);
|
return self.flushInner(comp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit pending changes and write headers.
|
/// Commit pending changes and write headers.
|
||||||
fn flushInner(self: *Elf, module: *Module) !void {
|
fn flushInner(self: *Elf, comp: *Compilation) !void {
|
||||||
|
// TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
|
||||||
|
// Zig source code.
|
||||||
|
const zig_module = self.base.options.zig_module orelse return error.LinkingWithoutZigSourceUnimplemented;
|
||||||
|
|
||||||
const target_endian = self.base.options.target.cpu.arch.endian();
|
const target_endian = self.base.options.target.cpu.arch.endian();
|
||||||
const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
|
const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
|
||||||
const ptr_width_bytes: u8 = self.ptrWidthBytes();
|
const ptr_width_bytes: u8 = self.ptrWidthBytes();
|
||||||
@ -844,8 +866,8 @@ fn flushInner(self: *Elf, module: *Module) !void {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Write the form for the compile unit, which must match the abbrev table above.
|
// Write the form for the compile unit, which must match the abbrev table above.
|
||||||
const name_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_path);
|
const name_strp = try self.makeDebugString(zig_module.root_pkg.root_src_path);
|
||||||
const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_directory.path.?);
|
const comp_dir_strp = try self.makeDebugString(zig_module.root_pkg.root_src_directory.path.?);
|
||||||
const producer_strp = try self.makeDebugString(link.producer_string);
|
const producer_strp = try self.makeDebugString(link.producer_string);
|
||||||
// Currently only one compilation unit is supported, so the address range is simply
|
// Currently only one compilation unit is supported, so the address range is simply
|
||||||
// identical to the main program header virtual address and memory size.
|
// identical to the main program header virtual address and memory size.
|
||||||
@ -1014,7 +1036,7 @@ fn flushInner(self: *Elf, module: *Module) !void {
|
|||||||
0, // include_directories (none except the compilation unit cwd)
|
0, // include_directories (none except the compilation unit cwd)
|
||||||
});
|
});
|
||||||
// file_names[0]
|
// file_names[0]
|
||||||
di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.?.root_src_path); // relative path name
|
di_buf.appendSliceAssumeCapacity(zig_module.root_pkg.root_src_path); // relative path name
|
||||||
di_buf.appendSliceAssumeCapacity(&[_]u8{
|
di_buf.appendSliceAssumeCapacity(&[_]u8{
|
||||||
0, // null byte for the relative path name
|
0, // null byte for the relative path name
|
||||||
0, // directory_index
|
0, // directory_index
|
||||||
@ -1199,11 +1221,105 @@ fn flushInner(self: *Elf, module: *Module) !void {
|
|||||||
assert(!self.debug_strtab_dirty);
|
assert(!self.debug_strtab_dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linkWithLLD(self: *Elf, module: *Module) !void {
|
fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||||
var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
|
var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
|
||||||
defer arena_allocator.deinit();
|
defer arena_allocator.deinit();
|
||||||
const arena = &arena_allocator.allocator;
|
const arena = &arena_allocator.allocator;
|
||||||
|
|
||||||
|
const directory = self.base.options.directory; // Just an alias to make it shorter to type.
|
||||||
|
|
||||||
|
// 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 zig_module_obj_path: ?[]const u8 = if (self.base.options.zig_module) |module| blk: {
|
||||||
|
try self.flushInner(comp);
|
||||||
|
|
||||||
|
const obj_basename = self.base.intermediary_basename.?;
|
||||||
|
const full_obj_path = if (directory.path) |dir_path|
|
||||||
|
try std.fs.path.join(arena, &[_][]const u8{dir_path, obj_basename})
|
||||||
|
else
|
||||||
|
obj_basename;
|
||||||
|
break :blk full_obj_path;
|
||||||
|
} else null;
|
||||||
|
|
||||||
|
// Here we want to determine whether we can save time by not invoking LLD when the
|
||||||
|
// output is unchanged. None of the linker options or the object files that are being
|
||||||
|
// linked are in the hash that namespaces the directory we are outputting to. Therefore,
|
||||||
|
// we must hash those now, and the resulting digest will form the "id" of the linking
|
||||||
|
// job we are about to perform.
|
||||||
|
// After a successful link, we store the id in the metadata of a symlink named "id.txt" in
|
||||||
|
// the artifact directory. So, now, we check if this symlink exists, and if it matches
|
||||||
|
// our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
|
||||||
|
const id_symlink_basename = "id.txt";
|
||||||
|
|
||||||
|
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||||
|
self.releaseLock();
|
||||||
|
|
||||||
|
var ch = comp.cache_parent.obtain();
|
||||||
|
defer ch.deinit();
|
||||||
|
|
||||||
|
const is_lib = self.base.options.output_mode == .Lib;
|
||||||
|
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
|
||||||
|
const have_dynamic_linker = self.base.options.link_libc and
|
||||||
|
self.base.options.link_mode == .Dynamic and (is_dyn_lib or self.base.options.output_mode == .Exe);
|
||||||
|
|
||||||
|
try ch.addOptionalFile(self.base.options.linker_script);
|
||||||
|
try ch.addOptionalFile(self.base.options.version_script);
|
||||||
|
try ch.addListOfFiles(self.base.options.objects);
|
||||||
|
for (comp.c_object_table.items()) |entry| switch (entry.key.status) {
|
||||||
|
.new => unreachable,
|
||||||
|
.failure => return error.NotAllCSourceFilesAvailableToLink,
|
||||||
|
.success => |success| _ = try ch.addFile(success.object_path, null),
|
||||||
|
};
|
||||||
|
try ch.addOptionalFile(zig_module_obj_path);
|
||||||
|
// We can skip hashing libc and libc++ components that we are in charge of building from Zig
|
||||||
|
// installation sources because they are always a product of the compiler version + target information.
|
||||||
|
ch.hash.addOptional(self.base.options.stack_size_override);
|
||||||
|
ch.hash.addOptional(self.base.options.gc_sections);
|
||||||
|
ch.hash.add(self.base.options.eh_frame_hdr);
|
||||||
|
ch.hash.add(self.base.options.rdynamic);
|
||||||
|
ch.hash.addListOfBytes(self.base.options.extra_lld_args);
|
||||||
|
ch.hash.addListOfBytes(self.base.options.lib_dirs);
|
||||||
|
ch.hash.add(self.base.options.z_nodelete);
|
||||||
|
ch.hash.add(self.base.options.z_defs);
|
||||||
|
if (self.base.options.link_libc) {
|
||||||
|
ch.hash.add(self.base.options.libc_installation != null);
|
||||||
|
if (self.base.options.libc_installation) |libc_installation| {
|
||||||
|
ch.hash.addBytes(libc_installation.crt_dir.?);
|
||||||
|
}
|
||||||
|
if (have_dynamic_linker) {
|
||||||
|
ch.hash.addOptionalBytes(self.base.options.dynamic_linker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_dyn_lib) {
|
||||||
|
ch.hash.addOptionalBytes(self.base.options.override_soname);
|
||||||
|
ch.hash.addOptional(self.base.options.version);
|
||||||
|
}
|
||||||
|
ch.hash.addListOfBytes(self.base.options.system_libs);
|
||||||
|
ch.hash.addOptional(self.base.options.allow_shlib_undefined);
|
||||||
|
ch.hash.add(self.base.options.bind_global_refs_locally);
|
||||||
|
|
||||||
|
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
|
||||||
|
_ = try ch.hit();
|
||||||
|
const digest = ch.final();
|
||||||
|
|
||||||
|
var prev_digest_buf: [digest.len]u8 = undefined;
|
||||||
|
const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch blk: {
|
||||||
|
// Handle this as a cache miss.
|
||||||
|
mem.set(u8, &prev_digest_buf, 0);
|
||||||
|
break :blk &prev_digest_buf;
|
||||||
|
};
|
||||||
|
if (mem.eql(u8, prev_digest, &digest)) {
|
||||||
|
// Hot diggity dog! The output binary is already there.
|
||||||
|
self.lock = ch.toOwnedLock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||||
|
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||||
|
error.FileNotFound => {},
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
|
||||||
const target = self.base.options.target;
|
const target = self.base.options.target;
|
||||||
const is_obj = self.base.options.output_mode == .Obj;
|
const is_obj = self.base.options.output_mode == .Obj;
|
||||||
|
|
||||||
@ -1272,8 +1388,6 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
try argv.append(arg);
|
try argv.append(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_lib = self.base.options.output_mode == .Lib;
|
|
||||||
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
|
|
||||||
if (self.base.options.link_mode == .Static) {
|
if (self.base.options.link_mode == .Static) {
|
||||||
if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
|
if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
|
||||||
try argv.append("-Bstatic");
|
try argv.append("-Bstatic");
|
||||||
@ -1288,7 +1402,7 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
try argv.append("-pie");
|
try argv.append("-pie");
|
||||||
}
|
}
|
||||||
|
|
||||||
const full_out_path = if (self.base.options.directory.path) |dir_path|
|
const full_out_path = if (directory.path) |dir_path|
|
||||||
try std.fs.path.join(arena, &[_][]const u8{dir_path, self.base.options.sub_path})
|
try std.fs.path.join(arena, &[_][]const u8{dir_path, self.base.options.sub_path})
|
||||||
else
|
else
|
||||||
self.base.options.sub_path;
|
self.base.options.sub_path;
|
||||||
@ -1311,13 +1425,14 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
break :o "Scrt1.o";
|
break :o "Scrt1.o";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try argv.append(try module.get_libc_crt_file(arena, crt1o));
|
try argv.append(try comp.get_libc_crt_file(arena, crt1o));
|
||||||
if (target_util.libc_needs_crti_crtn(target)) {
|
if (target_util.libc_needs_crti_crtn(target)) {
|
||||||
try argv.append(try module.get_libc_crt_file(arena, "crti.o"));
|
try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO rpaths
|
// TODO rpaths
|
||||||
|
// TODO add to cache hash above too
|
||||||
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
|
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
|
||||||
// Buf *rpath = g->rpath_list.at(i);
|
// Buf *rpath = g->rpath_list.at(i);
|
||||||
// add_rpath(lj, rpath);
|
// add_rpath(lj, rpath);
|
||||||
@ -1354,7 +1469,7 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
try argv.append(libc_installation.crt_dir.?);
|
try argv.append(libc_installation.crt_dir.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.base.options.link_mode == .Dynamic and (is_dyn_lib or self.base.options.output_mode == .Exe)) {
|
if (have_dynamic_linker) {
|
||||||
if (self.base.options.dynamic_linker) |dynamic_linker| {
|
if (self.base.options.dynamic_linker) |dynamic_linker| {
|
||||||
try argv.append("-dynamic-linker");
|
try argv.append("-dynamic-linker");
|
||||||
try argv.append(dynamic_linker);
|
try argv.append(dynamic_linker);
|
||||||
@ -1363,9 +1478,10 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_dyn_lib) {
|
if (is_dyn_lib) {
|
||||||
const soname = self.base.options.override_soname orelse
|
const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver|
|
||||||
try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name,
|
try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major})
|
||||||
self.base.options.version.major,});
|
else
|
||||||
|
try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name});
|
||||||
try argv.append("-soname");
|
try argv.append("-soname");
|
||||||
try argv.append(soname);
|
try argv.append(soname);
|
||||||
|
|
||||||
@ -1378,28 +1494,14 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
// Positional arguments to the linker such as object files.
|
// Positional arguments to the linker such as object files.
|
||||||
try argv.appendSlice(self.base.options.objects);
|
try argv.appendSlice(self.base.options.objects);
|
||||||
|
|
||||||
for (module.c_object_table.items()) |entry| {
|
for (comp.c_object_table.items()) |entry| switch (entry.key.status) {
|
||||||
const c_object = entry.key;
|
|
||||||
switch (c_object.status) {
|
|
||||||
.new => unreachable,
|
.new => unreachable,
|
||||||
.failure => return error.NotAllCSourceFilesAvailableToLink,
|
.failure => unreachable, // Checked during cache hashing.
|
||||||
.success => |full_obj_path| {
|
.success => |success| try argv.append(success.object_path),
|
||||||
try argv.append(full_obj_path);
|
};
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is no Zig code to compile, then we should skip flushing the output file because it
|
if (zig_module_obj_path) |p| {
|
||||||
// will not be part of the linker line anyway.
|
try argv.append(p);
|
||||||
if (module.root_pkg != null) {
|
|
||||||
try self.flushInner(module);
|
|
||||||
|
|
||||||
const obj_basename = self.base.intermediary_basename.?;
|
|
||||||
const full_obj_path = if (self.base.options.directory.path) |dir_path|
|
|
||||||
try std.fs.path.join(arena, &[_][]const u8{dir_path, obj_basename})
|
|
||||||
else
|
|
||||||
obj_basename;
|
|
||||||
try argv.append(full_obj_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO compiler-rt and libc
|
// TODO compiler-rt and libc
|
||||||
@ -1419,7 +1521,7 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||||
// (the check for that needs to be earlier), but they could be full paths to .so files, in which
|
// (the check for that needs to be earlier), but they could be full paths to .so files, in which
|
||||||
// case we want to avoid prepending "-l".
|
// case we want to avoid prepending "-l".
|
||||||
const ext = Module.classifyFileExt(link_lib);
|
const ext = Compilation.classifyFileExt(link_lib);
|
||||||
const arg = if (ext == .so) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
|
const arg = if (ext == .so) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
|
||||||
argv.appendAssumeCapacity(arg);
|
argv.appendAssumeCapacity(arg);
|
||||||
}
|
}
|
||||||
@ -1427,8 +1529,8 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
if (!is_obj) {
|
if (!is_obj) {
|
||||||
// libc++ dep
|
// libc++ dep
|
||||||
if (self.base.options.link_libcpp) {
|
if (self.base.options.link_libcpp) {
|
||||||
try argv.append(module.libcxxabi_static_lib.?);
|
try argv.append(comp.libcxxabi_static_lib.?);
|
||||||
try argv.append(module.libcxx_static_lib.?);
|
try argv.append(comp.libcxx_static_lib.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// libc dep
|
// libc dep
|
||||||
@ -1448,15 +1550,15 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
try argv.append("-lpthread");
|
try argv.append("-lpthread");
|
||||||
}
|
}
|
||||||
} else if (target.isGnuLibC()) {
|
} else if (target.isGnuLibC()) {
|
||||||
try argv.append(module.libunwind_static_lib.?);
|
try argv.append(comp.libunwind_static_lib.?);
|
||||||
// TODO here we need to iterate over the glibc libs and add the .so files to the linker line.
|
// TODO here we need to iterate over the glibc libs and add the .so files to the linker line.
|
||||||
std.log.warn("TODO port add_glibc_libs to stage2", .{});
|
std.log.warn("TODO port add_glibc_libs to stage2", .{});
|
||||||
try argv.append(try module.get_libc_crt_file(arena, "libc_nonshared.a"));
|
try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
|
||||||
} else if (target.isMusl()) {
|
} else if (target.isMusl()) {
|
||||||
try argv.append(module.libunwind_static_lib.?);
|
try argv.append(comp.libunwind_static_lib.?);
|
||||||
try argv.append(module.libc_static_lib.?);
|
try argv.append(comp.libc_static_lib.?);
|
||||||
} else if (self.base.options.link_libcpp) {
|
} else if (self.base.options.link_libcpp) {
|
||||||
try argv.append(module.libunwind_static_lib.?);
|
try argv.append(comp.libunwind_static_lib.?);
|
||||||
} else {
|
} else {
|
||||||
unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
|
unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
|
||||||
}
|
}
|
||||||
@ -1466,9 +1568,9 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
// crt end
|
// crt end
|
||||||
if (link_in_crt) {
|
if (link_in_crt) {
|
||||||
if (target.isAndroid()) {
|
if (target.isAndroid()) {
|
||||||
try argv.append(try module.get_libc_crt_file(arena, "crtend_android.o"));
|
try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
|
||||||
} else if (target_util.libc_needs_crti_crtn(target)) {
|
} else if (target_util.libc_needs_crti_crtn(target)) {
|
||||||
try argv.append(try module.get_libc_crt_file(arena, "crtn.o"));
|
try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1500,6 +1602,19 @@ fn linkWithLLD(self: *Elf, module: *Module) !void {
|
|||||||
const ZigLLDLink = @import("../llvm.zig").ZigLLDLink;
|
const ZigLLDLink = @import("../llvm.zig").ZigLLDLink;
|
||||||
const ok = ZigLLDLink(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, 0, 0);
|
const ok = ZigLLDLink(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, 0, 0);
|
||||||
if (!ok) return error.LLDReportedFailure;
|
if (!ok) return error.LLDReportedFailure;
|
||||||
|
|
||||||
|
// Update the dangling symlink "id.txt" with the digest. If it fails we can continue; it only
|
||||||
|
// means that the next invocation will have an unnecessary cache miss.
|
||||||
|
directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
|
||||||
|
std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
|
||||||
|
};
|
||||||
|
// Again failure here only means an unnecessary cache miss.
|
||||||
|
ch.writeManifest() catch |err| {
|
||||||
|
std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
|
||||||
|
};
|
||||||
|
// We hang on to this lock so that the output file path can be used without
|
||||||
|
// other processes clobbering it.
|
||||||
|
self.lock = ch.toOwnedLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
|
fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
|
||||||
@ -2396,7 +2511,7 @@ pub fn updateDeclExports(
|
|||||||
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
||||||
module.failed_exports.putAssumeCapacityNoClobber(
|
module.failed_exports.putAssumeCapacityNoClobber(
|
||||||
exp,
|
exp,
|
||||||
try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
|
try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2414,7 +2529,7 @@ pub fn updateDeclExports(
|
|||||||
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
|
||||||
module.failed_exports.putAssumeCapacityNoClobber(
|
module.failed_exports.putAssumeCapacityNoClobber(
|
||||||
exp,
|
exp,
|
||||||
try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
|
try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
@ -2722,8 +2837,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {
|
|||||||
directory_count * 8 + file_name_count * 8 +
|
directory_count * 8 + file_name_count * 8 +
|
||||||
// These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
|
// These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
|
||||||
// because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
|
// because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
|
||||||
self.base.options.root_pkg.?.root_src_directory.path.?.len +
|
self.base.options.zig_module.?.root_pkg.root_src_directory.path.?.len +
|
||||||
self.base.options.root_pkg.?.root_src_path.len);
|
self.base.options.zig_module.?.root_pkg.root_src_path.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
|
fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
|
||||||
|
|||||||
@ -12,7 +12,8 @@ const mem = std.mem;
|
|||||||
const trace = @import("../tracy.zig").trace;
|
const trace = @import("../tracy.zig").trace;
|
||||||
const Type = @import("../type.zig").Type;
|
const Type = @import("../type.zig").Type;
|
||||||
|
|
||||||
const Module = @import("../Module.zig");
|
const Module = @import("../ZigModule.zig");
|
||||||
|
const Compilation = @import("../Module.zig");
|
||||||
const link = @import("../link.zig");
|
const link = @import("../link.zig");
|
||||||
const File = link.File;
|
const File = link.File;
|
||||||
|
|
||||||
@ -205,7 +206,7 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(self: *MachO, module: *Module) !void {
|
pub fn flush(self: *MachO, comp: *Compilation) !void {
|
||||||
switch (self.base.options.output_mode) {
|
switch (self.base.options.output_mode) {
|
||||||
.Exe => {
|
.Exe => {
|
||||||
var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
|
var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
|
||||||
|
|||||||
@ -6,7 +6,8 @@ const assert = std.debug.assert;
|
|||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const leb = std.debug.leb;
|
const leb = std.debug.leb;
|
||||||
|
|
||||||
const Module = @import("../Module.zig");
|
const Module = @import("../ZigModule.zig");
|
||||||
|
const Compilation = @import("../Module.zig");
|
||||||
const codegen = @import("../codegen/wasm.zig");
|
const codegen = @import("../codegen/wasm.zig");
|
||||||
const link = @import("../link.zig");
|
const link = @import("../link.zig");
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
|
|||||||
decl.fn_link.wasm = null;
|
decl.fn_link.wasm = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(self: *Wasm, module: *Module) !void {
|
pub fn flush(self: *Wasm, comp: *Compilation) !void {
|
||||||
const file = self.base.file.?;
|
const file = self.base.file.?;
|
||||||
const header_size = 5 + 1;
|
const header_size = 5 + 1;
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ pub fn flush(self: *Wasm, module: *Module) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Export section
|
// Export section
|
||||||
{
|
if (self.base.options.zig_module) |module| {
|
||||||
const header_offset = try reserveVecSectionHeader(file);
|
const header_offset = try reserveVecSectionHeader(file);
|
||||||
const writer = file.writer();
|
const writer = file.writer();
|
||||||
var count: u32 = 0;
|
var count: u32 = 0;
|
||||||
|
|||||||
@ -268,6 +268,7 @@ pub fn buildOutputType(
|
|||||||
var link_mode: ?std.builtin.LinkMode = null;
|
var link_mode: ?std.builtin.LinkMode = null;
|
||||||
var root_src_file: ?[]const u8 = null;
|
var root_src_file: ?[]const u8 = null;
|
||||||
var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
|
var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
|
||||||
|
var have_version = false;
|
||||||
var strip = false;
|
var strip = false;
|
||||||
var single_threaded = false;
|
var single_threaded = false;
|
||||||
var watch = false;
|
var watch = false;
|
||||||
@ -445,6 +446,7 @@ pub fn buildOutputType(
|
|||||||
version = std.builtin.Version.parse(args[i]) catch |err| {
|
version = std.builtin.Version.parse(args[i]) catch |err| {
|
||||||
fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
|
fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
|
||||||
};
|
};
|
||||||
|
have_version = true;
|
||||||
} else if (mem.eql(u8, arg, "-target")) {
|
} else if (mem.eql(u8, arg, "-target")) {
|
||||||
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
|
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -799,6 +801,7 @@ pub fn buildOutputType(
|
|||||||
version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
|
version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
|
||||||
fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
|
fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
|
||||||
};
|
};
|
||||||
|
have_version = true;
|
||||||
} else if (mem.eql(u8, arg, "--minor-image-version")) {
|
} else if (mem.eql(u8, arg, "--minor-image-version")) {
|
||||||
i += 1;
|
i += 1;
|
||||||
if (i >= linker_args.items.len) {
|
if (i >= linker_args.items.len) {
|
||||||
@ -807,6 +810,7 @@ pub fn buildOutputType(
|
|||||||
version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
|
version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
|
||||||
fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
|
fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
|
||||||
};
|
};
|
||||||
|
have_version = true;
|
||||||
} else if (mem.eql(u8, arg, "--stack")) {
|
} else if (mem.eql(u8, arg, "--stack")) {
|
||||||
i += 1;
|
i += 1;
|
||||||
if (i >= linker_args.items.len) {
|
if (i >= linker_args.items.len) {
|
||||||
@ -1161,7 +1165,7 @@ pub fn buildOutputType(
|
|||||||
.self_exe_path = self_exe_path,
|
.self_exe_path = self_exe_path,
|
||||||
.rand = &default_prng.random,
|
.rand = &default_prng.random,
|
||||||
.clang_passthrough_mode = arg_mode != .build,
|
.clang_passthrough_mode = arg_mode != .build,
|
||||||
.version = version,
|
.version = if (have_version) version else null,
|
||||||
.libc_installation = if (libc_installation) |*lci| lci else null,
|
.libc_installation = if (libc_installation) |*lci| lci else null,
|
||||||
.debug_cc = debug_cc,
|
.debug_cc = debug_cc,
|
||||||
.debug_link = debug_link,
|
.debug_link = debug_link,
|
||||||
@ -1228,7 +1232,9 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (zir_out_path) |zop| {
|
if (zir_out_path) |zop| {
|
||||||
var new_zir_module = try zir.emit(gpa, module);
|
const zig_module = module.bin_file.options.zig_module orelse
|
||||||
|
fatal("-femit-zir with no zig source code", .{});
|
||||||
|
var new_zir_module = try zir.emit(gpa, zig_module);
|
||||||
defer new_zir_module.deinit(gpa);
|
defer new_zir_module.deinit(gpa);
|
||||||
|
|
||||||
const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
|
const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
|
||||||
|
|||||||
@ -549,7 +549,7 @@ pub const TestContext = struct {
|
|||||||
update_node.estimated_total_items = 5;
|
update_node.estimated_total_items = 5;
|
||||||
var emit_node = update_node.start("emit", null);
|
var emit_node = update_node.start("emit", null);
|
||||||
emit_node.activate();
|
emit_node.activate();
|
||||||
var new_zir_module = try zir.emit(allocator, module);
|
var new_zir_module = try zir.emit(allocator, module.bin_file.options.zig_module.?);
|
||||||
defer new_zir_module.deinit(allocator);
|
defer new_zir_module.deinit(allocator);
|
||||||
emit_node.end();
|
emit_node.end();
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ const Value = @import("value.zig").Value;
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("ZigModule.zig");
|
||||||
|
|
||||||
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
|
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
|
||||||
/// It's important for this type to be small.
|
/// It's important for this type to be small.
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const BigIntConst = std.math.big.int.Const;
|
|||||||
const BigIntMutable = std.math.big.int.Mutable;
|
const BigIntMutable = std.math.big.int.Mutable;
|
||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("ZigModule.zig");
|
||||||
|
|
||||||
/// This is the raw data, with no bookkeeping, no memory awareness,
|
/// This is the raw data, with no bookkeeping, no memory awareness,
|
||||||
/// no de-duplication, and no type system awareness.
|
/// no de-duplication, and no type system awareness.
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const Type = @import("type.zig").Type;
|
|||||||
const Value = @import("value.zig").Value;
|
const Value = @import("value.zig").Value;
|
||||||
const TypedValue = @import("TypedValue.zig");
|
const TypedValue = @import("TypedValue.zig");
|
||||||
const ir = @import("ir.zig");
|
const ir = @import("ir.zig");
|
||||||
const IrModule = @import("Module.zig");
|
const IrModule = @import("ZigModule.zig");
|
||||||
|
|
||||||
/// This struct is relevent only for the ZIR Module text format. It is not used for
|
/// This struct is relevent only for the ZIR Module text format. It is not used for
|
||||||
/// semantic analysis of Zig source code.
|
/// semantic analysis of Zig source code.
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const TypedValue = @import("TypedValue.zig");
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const ir = @import("ir.zig");
|
const ir = @import("ir.zig");
|
||||||
const zir = @import("zir.zig");
|
const zir = @import("zir.zig");
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("ZigModule.zig");
|
||||||
const Inst = ir.Inst;
|
const Inst = ir.Inst;
|
||||||
const Body = ir.Body;
|
const Body = ir.Body;
|
||||||
const trace = @import("tracy.zig").trace;
|
const trace = @import("tracy.zig").trace;
|
||||||
@ -199,10 +199,10 @@ pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError
|
|||||||
// We don't fully codegen the decl until later, but we do need to reserve a global
|
// We don't fully codegen the decl until later, but we do need to reserve a global
|
||||||
// offset table index for it. This allows us to codegen decls out of dependency order,
|
// offset table index for it. This allows us to codegen decls out of dependency order,
|
||||||
// increasing how many computations can be done in parallel.
|
// increasing how many computations can be done in parallel.
|
||||||
try mod.bin_file.allocateDeclIndexes(decl);
|
try mod.comp.bin_file.allocateDeclIndexes(decl);
|
||||||
try mod.work_queue.writeItem(.{ .codegen_decl = decl });
|
try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
|
||||||
} else if (prev_type_has_bits) {
|
} else if (prev_type_has_bits) {
|
||||||
mod.bin_file.freeDecl(decl);
|
mod.comp.bin_file.freeDecl(decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return type_changed;
|
return type_changed;
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||||
|
|
||||||
// self-hosted does not yet support PE executable files / COFF object files
|
// Self-hosted has differing levels of support for various architectures. For now we pass explicit
|
||||||
// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
|
// target parameters to each test case. At some point we will take this to the next level and have
|
||||||
|
// a set of targets that all test cases run on unless specifically overridden. For now, each test
|
||||||
|
// case applies to only the specified target.
|
||||||
|
|
||||||
const linux_x64 = std.zig.CrossTarget{
|
const linux_x64 = std.zig.CrossTarget{
|
||||||
.cpu_arch = .x86_64,
|
.cpu_arch = .x86_64,
|
||||||
.os_tag = .linux,
|
.os_tag = .linux,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user