authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-08 01:11:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-09 09:28:05-07:00
log4056bb92e6d6ca3d2ab8f49b4ac83c01fb25bd11
tree3992ca209d9759c7a041c1c9f58e8572081051da
parent472ee184862415d1f2651d81d248f3000032932d

stage2: more progress moving `zig cc` to stage2

* std.cache_hash exposes Hasher type * std.cache_hash makes hasher_init a global const * std.cache_hash supports cloning so that clones can share the same open manifest dir handle as well as fork from shared hasher state * start to populate the cache_hash for stage2 builds * remove a footgun from std.cache_hash add function * get rid of std.Target.ObjectFormat.unknown * rework stage2 logic for resolving output artifact names by adding object_format as an optional parameter to std.zig.binNameAlloc * support -Denable-llvm in stage2 tests * Module supports the use case when there are no .zig files * introduce c_object_table and failed_c_objects to Module * propagate many new kinds of data from CLI into Module and into linker.Options * introduce -fLLVM, -fLLD, -fClang and their -fno- counterparts. closes #6251. - add logic for choosing when to use LLD or zig's self-hosted linker * stub code for implementing invoking Clang to build C objects * add -femit-h, -femit-h=foo, and -fno-emit-h CLI options

13 files changed, 595 insertions(+), 129 deletions(-)

build.zig+1
...@@ -139,6 +139,7 @@ pub fn build(b: *Builder) !void {...@@ -139,6 +139,7 @@ pub fn build(b: *Builder) !void {
139 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;139 const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
140 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");140 const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
141141
142 test_stage2.addBuildOption(bool, "have_llvm", enable_llvm);
142 test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);143 test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
143 test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);144 test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
144 test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);145 test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
lib/std/cache_hash.zig+73-35
...@@ -5,7 +5,6 @@...@@ -5,7 +5,6 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std.zig");6const std = @import("std.zig");
7const crypto = std.crypto;7const crypto = std.crypto;
8const Hasher = crypto.auth.siphash.SipHash128(1, 3); // provides enough collision resistance for the CacheHash use cases, while being one of our fastest options right now
9const fs = std.fs;8const fs = std.fs;
10const base64 = std.base64;9const base64 = std.base64;
11const ArrayList = std.ArrayList;10const ArrayList = std.ArrayList;
...@@ -23,6 +22,14 @@ const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN);...@@ -23,6 +22,14 @@ const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN);
2322
24const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024;23const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024;
2524
25/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it
26/// provides enough collision resistance for the CacheHash use cases, while being one of our
27/// fastest options right now.
28pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
29
30/// Initial state, that can be copied.
31pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
32
26pub const File = struct {33pub const File = struct {
27 path: ?[]const u8,34 path: ?[]const u8,
28 max_file_size: ?usize,35 max_file_size: ?usize,
...@@ -45,52 +52,82 @@ pub const File = struct {...@@ -45,52 +52,82 @@ pub const File = struct {
4552
46/// CacheHash manages project-local `zig-cache` directories.53/// CacheHash manages project-local `zig-cache` directories.
47/// This is not a general-purpose cache.54/// This is not a general-purpose cache.
48/// It was designed to be fast and simple, not to withstand attacks using specially-crafted input.55/// It is designed to be fast and simple, not to withstand attacks using specially-crafted input.
49pub const CacheHash = struct {56pub const CacheHash = struct {
50 allocator: *Allocator,57 allocator: *Allocator,
51 hasher_init: Hasher, // initial state, that can be copied58 /// Current state for incremental hashing.
52 hasher: Hasher, // current state for incremental hashing59 hasher: Hasher,
53 manifest_dir: fs.Dir,60 manifest_dir: fs.Dir,
54 manifest_file: ?fs.File,61 manifest_file: ?fs.File,
55 manifest_dirty: bool,62 manifest_dirty: bool,
63 owns_manifest_dir: bool,
56 files: ArrayList(File),64 files: ArrayList(File),
57 b64_digest: [BASE64_DIGEST_LEN]u8,65 b64_digest: [BASE64_DIGEST_LEN]u8,
5866
59 /// Be sure to call release after successful initialization.67 /// Be sure to call release after successful initialization.
60 pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash {68 pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash {
61 const hasher_init = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
62 return CacheHash{69 return CacheHash{
63 .allocator = allocator,70 .allocator = allocator,
64 .hasher_init = hasher_init,
65 .hasher = hasher_init,71 .hasher = hasher_init,
66 .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}),72 .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}),
67 .manifest_file = null,73 .manifest_file = null,
68 .manifest_dirty = false,74 .manifest_dirty = false,
75 .owns_manifest_dir = true,
76 .files = ArrayList(File).init(allocator),
77 .b64_digest = undefined,
78 };
79 }
80
81 /// Allows one to fork a CacheHash instance into another one, which does not require an additional
82 /// directory handle to be opened. The new instance inherits the hash state.
83 pub fn clone(self: CacheHash) CacheHash {
84 assert(self.manifest_file == null);
85 assert(files.items.len == 0);
86 return .{
87 .allocator = self.allocator,
88 .hasher = self.hasher,
89 .manifest_dir = self.manifest_dir,
90 .manifest_file = null,
91 .manifest_dirty = false,
92 .owns_manifest_dir = false,
69 .files = ArrayList(File).init(allocator),93 .files = ArrayList(File).init(allocator),
70 .b64_digest = undefined,94 .b64_digest = undefined,
71 };95 };
72 }96 }
7397
74 /// Record a slice of bytes as an dependency of the process being cached98 /// Record a slice of bytes as an dependency of the process being cached
75 pub fn addSlice(self: *CacheHash, val: []const u8) void {99 pub fn addBytes(self: *CacheHash, bytes: []const u8) void {
76 assert(self.manifest_file == null);100 assert(self.manifest_file == null);
77101
78 self.hasher.update(val);102 self.hasher.update(mem.asBytes(&bytes.len));
79 self.hasher.update(&[_]u8{0});103 self.hasher.update(bytes);
80 }104 }
81105
82 /// Convert the input value into bytes and record it as a dependency of the106 pub fn addListOfBytes(self: *CacheHash, list_of_bytes: []const []const u8) void {
83 /// process being cached
84 pub fn add(self: *CacheHash, val: anytype) void {
85 assert(self.manifest_file == null);107 assert(self.manifest_file == null);
86108
87 const valPtr = switch (@typeInfo(@TypeOf(val))) {109 self.add(list_of_bytes.items.len);
88 .Int => &val,110 for (list_of_bytes) |bytes| self.addBytes(bytes);
89 .Pointer => val,111 }
90 else => &val,112
91 };113 /// Convert the input value into bytes and record it as a dependency of the process being cached.
114 pub fn add(self: *CacheHash, x: anytype) void {
115 assert(self.manifest_file == null);
116
117 switch (@TypeOf(x)) {
118 std.builtin.Version => {
119 self.add(x.major);
120 self.add(x.minor);
121 self.add(x.patch);
122 return;
123 },
124 else => {},
125 }
92126
93 self.addSlice(mem.asBytes(valPtr));127 switch (@typeInfo(@TypeOf(x))) {
128 .Bool, .Int, .Enum, .Array => self.addBytes(mem.asBytes(&x)),
129 else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
130 }
94 }131 }
95132
96 /// Add a file as a dependency of process being cached. When `CacheHash.hit` is133 /// Add a file as a dependency of process being cached. When `CacheHash.hit` is
...@@ -122,7 +159,7 @@ pub const CacheHash = struct {...@@ -122,7 +159,7 @@ pub const CacheHash = struct {
122 .bin_digest = undefined,159 .bin_digest = undefined,
123 };160 };
124161
125 self.addSlice(resolved_path);162 self.addBytes(resolved_path);
126163
127 return idx;164 return idx;
128 }165 }
...@@ -143,7 +180,7 @@ pub const CacheHash = struct {...@@ -143,7 +180,7 @@ pub const CacheHash = struct {
143180
144 base64_encoder.encode(self.b64_digest[0..], &bin_digest);181 base64_encoder.encode(self.b64_digest[0..], &bin_digest);
145182
146 self.hasher = self.hasher_init;183 self.hasher = hasher_init;
147 self.hasher.update(&bin_digest);184 self.hasher.update(&bin_digest);
148185
149 const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest});186 const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest});
...@@ -244,7 +281,7 @@ pub const CacheHash = struct {...@@ -244,7 +281,7 @@ pub const CacheHash = struct {
244 }281 }
245282
246 var actual_digest: [BIN_DIGEST_LEN]u8 = undefined;283 var actual_digest: [BIN_DIGEST_LEN]u8 = undefined;
247 try hashFile(this_file, &actual_digest, self.hasher_init);284 try hashFile(this_file, &actual_digest);
248285
249 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {286 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
250 cache_hash_file.bin_digest = actual_digest;287 cache_hash_file.bin_digest = actual_digest;
...@@ -262,7 +299,7 @@ pub const CacheHash = struct {...@@ -262,7 +299,7 @@ pub const CacheHash = struct {
262 // cache miss299 // cache miss
263 // keep the manifest file open300 // keep the manifest file open
264 // reset the hash301 // reset the hash
265 self.hasher = self.hasher_init;302 self.hasher = hasher_init;
266 self.hasher.update(&bin_digest);303 self.hasher.update(&bin_digest);
267304
268 // Remove files not in the initial hash305 // Remove files not in the initial hash
...@@ -310,7 +347,7 @@ pub const CacheHash = struct {...@@ -310,7 +347,7 @@ pub const CacheHash = struct {
310347
311 // Hash while reading from disk, to keep the contents in the cpu cache while348 // Hash while reading from disk, to keep the contents in the cpu cache while
312 // doing hashing.349 // doing hashing.
313 var hasher = self.hasher_init;350 var hasher = hasher_init;
314 var off: usize = 0;351 var off: usize = 0;
315 while (true) {352 while (true) {
316 // give me everything you've got, captain353 // give me everything you've got, captain
...@@ -323,7 +360,7 @@ pub const CacheHash = struct {...@@ -323,7 +360,7 @@ pub const CacheHash = struct {
323360
324 ch_file.contents = contents;361 ch_file.contents = contents;
325 } else {362 } else {
326 try hashFile(file, &ch_file.bin_digest, self.hasher_init);363 try hashFile(file, &ch_file.bin_digest);
327 }364 }
328365
329 self.hasher.update(&ch_file.bin_digest);366 self.hasher.update(&ch_file.bin_digest);
...@@ -435,11 +472,12 @@ pub const CacheHash = struct {...@@ -435,11 +472,12 @@ pub const CacheHash = struct {
435 file.deinit(self.allocator);472 file.deinit(self.allocator);
436 }473 }
437 self.files.deinit();474 self.files.deinit();
438 self.manifest_dir.close();475 if (self.owns_manifest_dir)
476 self.manifest_dir.close();
439 }477 }
440};478};
441479
442fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void {480fn hashFile(file: fs.File, bin_digest: []u8) !void {
443 var buf: [1024]u8 = undefined;481 var buf: [1024]u8 = undefined;
444482
445 var hasher = hasher_init;483 var hasher = hasher_init;
...@@ -509,7 +547,7 @@ test "cache file and then recall it" {...@@ -509,7 +547,7 @@ test "cache file and then recall it" {
509547
510 ch.add(true);548 ch.add(true);
511 ch.add(@as(u16, 1234));549 ch.add(@as(u16, 1234));
512 ch.add("1234");550 ch.addBytes("1234");
513 _ = try ch.addFile(temp_file, null);551 _ = try ch.addFile(temp_file, null);
514552
515 // There should be nothing in the cache553 // There should be nothing in the cache
...@@ -523,7 +561,7 @@ test "cache file and then recall it" {...@@ -523,7 +561,7 @@ test "cache file and then recall it" {
523561
524 ch.add(true);562 ch.add(true);
525 ch.add(@as(u16, 1234));563 ch.add(@as(u16, 1234));
526 ch.add("1234");564 ch.addBytes("1234");
527 _ = try ch.addFile(temp_file, null);565 _ = try ch.addFile(temp_file, null);
528566
529 // Cache hit! We just "built" the same file567 // Cache hit! We just "built" the same file
...@@ -577,7 +615,7 @@ test "check that changing a file makes cache fail" {...@@ -577,7 +615,7 @@ test "check that changing a file makes cache fail" {
577 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);615 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
578 defer ch.release();616 defer ch.release();
579617
580 ch.add("1234");618 ch.addBytes("1234");
581 const temp_file_idx = try ch.addFile(temp_file, 100);619 const temp_file_idx = try ch.addFile(temp_file, 100);
582620
583 // There should be nothing in the cache621 // There should be nothing in the cache
...@@ -594,7 +632,7 @@ test "check that changing a file makes cache fail" {...@@ -594,7 +632,7 @@ test "check that changing a file makes cache fail" {
594 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);632 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
595 defer ch.release();633 defer ch.release();
596634
597 ch.add("1234");635 ch.addBytes("1234");
598 const temp_file_idx = try ch.addFile(temp_file, 100);636 const temp_file_idx = try ch.addFile(temp_file, 100);
599637
600 // A file that we depend on has been updated, so the cache should not contain an entry for it638 // A file that we depend on has been updated, so the cache should not contain an entry for it
...@@ -628,7 +666,7 @@ test "no file inputs" {...@@ -628,7 +666,7 @@ test "no file inputs" {
628 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);666 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
629 defer ch.release();667 defer ch.release();
630668
631 ch.add("1234");669 ch.addBytes("1234");
632670
633 // There should be nothing in the cache671 // There should be nothing in the cache
634 testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());672 testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
...@@ -639,7 +677,7 @@ test "no file inputs" {...@@ -639,7 +677,7 @@ test "no file inputs" {
639 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);677 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
640 defer ch.release();678 defer ch.release();
641679
642 ch.add("1234");680 ch.addBytes("1234");
643681
644 digest2 = (try ch.hit()).?;682 digest2 = (try ch.hit()).?;
645 }683 }
...@@ -674,7 +712,7 @@ test "CacheHashes with files added after initial hash work" {...@@ -674,7 +712,7 @@ test "CacheHashes with files added after initial hash work" {
674 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);712 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
675 defer ch.release();713 defer ch.release();
676714
677 ch.add("1234");715 ch.addBytes("1234");
678 _ = try ch.addFile(temp_file1, null);716 _ = try ch.addFile(temp_file1, null);
679717
680 // There should be nothing in the cache718 // There should be nothing in the cache
...@@ -688,7 +726,7 @@ test "CacheHashes with files added after initial hash work" {...@@ -688,7 +726,7 @@ test "CacheHashes with files added after initial hash work" {
688 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);726 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
689 defer ch.release();727 defer ch.release();
690728
691 ch.add("1234");729 ch.addBytes("1234");
692 _ = try ch.addFile(temp_file1, null);730 _ = try ch.addFile(temp_file1, null);
693731
694 digest2 = (try ch.hit()).?;732 digest2 = (try ch.hit()).?;
...@@ -707,7 +745,7 @@ test "CacheHashes with files added after initial hash work" {...@@ -707,7 +745,7 @@ test "CacheHashes with files added after initial hash work" {
707 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);745 var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
708 defer ch.release();746 defer ch.release();
709747
710 ch.add("1234");748 ch.addBytes("1234");
711 _ = try ch.addFile(temp_file1, null);749 _ = try ch.addFile(temp_file1, null);
712750
713 // A file that we depend on has been updated, so the cache should not contain an entry for it751 // A file that we depend on has been updated, so the cache should not contain an entry for it
lib/std/target.zig-2
...@@ -465,8 +465,6 @@ pub const Target = struct {...@@ -465,8 +465,6 @@ pub const Target = struct {
465 };465 };
466466
467 pub const ObjectFormat = enum {467 pub const ObjectFormat = enum {
468 /// TODO Get rid of this one.
469 unknown,
470 coff,468 coff,
471 pe,469 pe,
472 elf,470 elf,
lib/std/zig.zig+44-9
...@@ -71,17 +71,52 @@ pub fn binNameAlloc(...@@ -71,17 +71,52 @@ pub fn binNameAlloc(
71 target: std.Target,71 target: std.Target,
72 output_mode: std.builtin.OutputMode,72 output_mode: std.builtin.OutputMode,
73 link_mode: ?std.builtin.LinkMode,73 link_mode: ?std.builtin.LinkMode,
74 object_format: ?std.Target.ObjectFormat,
74) error{OutOfMemory}![]u8 {75) error{OutOfMemory}![]u8 {
75 switch (output_mode) {76 switch (object_format orelse target.getObjectFormat()) {
76 .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }),77 .coff, .pe => switch (output_mode) {
77 .Lib => {78 .Exe => {
78 const suffix = switch (link_mode orelse .Static) {79 const suffix = switch (target.os.tag) {
79 .Static => target.staticLibSuffix(),80 .uefi => ".efi",
80 .Dynamic => target.dynamicLibSuffix(),81 else => ".exe",
81 };82 };
82 return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });83 return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, suffix });
84 },
85 .Lib => {
86 const suffix = switch (link_mode orelse .Static) {
87 .Static => ".lib",
88 .Dynamic => ".dll",
89 };
90 return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
91 },
92 .Obj => return std.fmt.allocPrint(allocator, "{}.obj", .{root_name}),
93 },
94 .elf => switch (output_mode) {
95 .Exe => return allocator.dupe(u8, root_name),
96 .Lib => {
97 const suffix = switch (link_mode orelse .Static) {
98 .Static => ".a",
99 .Dynamic => ".so",
100 };
101 return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
102 },
103 .Obj => return std.fmt.allocPrint(allocator, "{}.o", .{root_name}),
104 },
105 .macho => switch (output_mode) {
106 .Exe => return allocator.dupe(u8, root_name),
107 .Lib => {
108 const suffix = switch (link_mode orelse .Static) {
109 .Static => ".a",
110 .Dynamic => ".dylib",
111 };
112 return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
113 },
114 .Obj => return std.fmt.allocPrint(allocator, "{}.o", .{root_name}),
83 },115 },
84 .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),116 .wasm => return std.fmt.allocPrint(allocator, "{}.wasm", .{root_name}),
117 .c => return std.fmt.allocPrint(allocator, "{}.c", .{root_name}),
118 .hex => return std.fmt.allocPrint(allocator, "{}.ihex", .{root_name}),
119 .raw => return std.fmt.allocPrint(allocator, "{}.bin", .{root_name}),
85 }120 }
86}121}
87122
src-self-hosted/Module.zig+319-34
...@@ -22,11 +22,12 @@ const trace = @import("tracy.zig").trace;...@@ -22,11 +22,12 @@ const trace = @import("tracy.zig").trace;
22const liveness = @import("liveness.zig");22const liveness = @import("liveness.zig");
23const astgen = @import("astgen.zig");23const astgen = @import("astgen.zig");
24const zir_sema = @import("zir_sema.zig");24const zir_sema = @import("zir_sema.zig");
25const build_options = @import("build_options");
2526
26/// General-purpose allocator. Used for both temporary and long-term storage.27/// General-purpose allocator. Used for both temporary and long-term storage.
27gpa: *Allocator,28gpa: *Allocator,
28/// Pointer to externally managed resource.29/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
29root_pkg: *Package,30root_pkg: ?*Package,
30/// Module owns this resource.31/// Module owns this resource.
31/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.32/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
32root_scope: *Scope,33root_scope: *Scope,
...@@ -48,22 +49,26 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},...@@ -48,22 +49,26 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
48/// Maps fully qualified namespaced names to the Decl struct for them.49/// Maps fully qualified namespaced names to the Decl struct for them.
49decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},50decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
5051
52c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
53
51link_error_flags: link.File.ErrorFlags = .{},54link_error_flags: link.File.ErrorFlags = .{},
5255
53work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),56work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
5457
55/// We optimize memory usage for a compilation with no compile errors by storing the58/// We optimize memory usage for a compilation with no compile errors by storing the
56/// error messages and mapping outside of `Decl`.59/// error messages and mapping outside of `Decl`.
57/// The ErrorMsg memory is owned by the decl, using Module's allocator.60/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
58/// Note that a Decl can succeed but the Fn it represents can fail. In this case,61/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
59/// a Decl can have a failed_decls entry but have analysis status of success.62/// a Decl can have a failed_decls entry but have analysis status of success.
60failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},63failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
61/// Using a map here for consistency with the other fields here.64/// Using a map here for consistency with the other fields here.
62/// The ErrorMsg memory is owned by the `Scope`, using Module's allocator.65/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
63failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{},66failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
64/// Using a map here for consistency with the other fields here.67/// Using a map here for consistency with the other fields here.
65/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.68/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
66failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},69failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
70/// The ErrorMsg memory is owned by the `CObject`, using Module's general purpose allocator.
71failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
6772
68/// Incrementing integer used to compare against the corresponding Decl73/// Incrementing integer used to compare against the corresponding Decl
69/// field to determine whether a Decl's status applies to an ongoing update, or a74/// field to determine whether a Decl's status applies to an ongoing update, or a
...@@ -79,10 +84,15 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},...@@ -79,10 +84,15 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
79/// Owned by Module.84/// Owned by Module.
80root_name: []u8,85root_name: []u8,
81keep_source_files_loaded: bool,86keep_source_files_loaded: bool,
87use_clang: bool,
8288
83/// Error tags and their values, tag names are duped with mod.gpa.89/// Error tags and their values, tag names are duped with mod.gpa.
84global_error_set: std.StringHashMapUnmanaged(u16) = .{},90global_error_set: std.StringHashMapUnmanaged(u16) = .{},
8591
92c_source_files: []const []const u8,
93clang_argv: []const []const u8,
94cache: std.cache_hash.CacheHash,
95
86pub const InnerError = error{ OutOfMemory, AnalysisFail };96pub const InnerError = error{ OutOfMemory, AnalysisFail };
8797
88const WorkItem = union(enum) {98const WorkItem = union(enum) {
...@@ -95,6 +105,9 @@ const WorkItem = union(enum) {...@@ -95,6 +105,9 @@ const WorkItem = union(enum) {
95 /// The source file containing the Decl has been updated, and so the105 /// The source file containing the Decl has been updated, and so the
96 /// Decl may need its line number information updated in the debug info.106 /// Decl may need its line number information updated in the debug info.
97 update_line_number: *Decl,107 update_line_number: *Decl,
108 /// Invoke the Clang compiler to create an object file, which gets linked
109 /// with the Module.
110 c_object: *CObject,
98};111};
99112
100pub const Export = struct {113pub const Export = struct {
...@@ -230,6 +243,7 @@ pub const Decl = struct {...@@ -230,6 +243,7 @@ pub const Decl = struct {
230 const src_decl = module.decls[self.src_index];243 const src_decl = module.decls[self.src_index];
231 return src_decl.inst.src;244 return src_decl.inst.src;
232 },245 },
246 .none => unreachable,
233 .file, .block => unreachable,247 .file, .block => unreachable,
234 .gen_zir => unreachable,248 .gen_zir => unreachable,
235 .local_val => unreachable,249 .local_val => unreachable,
...@@ -282,6 +296,30 @@ pub const Decl = struct {...@@ -282,6 +296,30 @@ pub const Decl = struct {
282 }296 }
283};297};
284298
299pub const CObject = struct {
300 /// Relative to cwd. Owned by arena.
301 src_path: []const u8,
302 /// Owned by arena.
303 extra_flags: []const []const u8,
304 arena: std.heap.ArenaAllocator.State,
305 status: union(enum) {
306 new,
307 /// This is the output object path. Owned by gpa.
308 success: []u8,
309 /// There will be a corresponding ErrorMsg in Module.failed_c_objects.
310 /// This is the C source file contents (used for printing error messages). Owned by gpa.
311 failure: []u8,
312 },
313
314 pub fn destroy(self: *CObject, gpa: *Allocator) void {
315 switch (self.status) {
316 .new => {},
317 .failure, .success => |data| gpa.free(data),
318 }
319 self.arena.promote(gpa).deinit();
320 }
321};
322
285/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.323/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
286pub const Fn = struct {324pub const Fn = struct {
287 /// This memory owned by the Decl's TypedValue.Managed arena allocator.325 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
...@@ -361,6 +399,7 @@ pub const Scope = struct {...@@ -361,6 +399,7 @@ pub const Scope = struct {
361 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,399 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
362 .file => unreachable,400 .file => unreachable,
363 .container => unreachable,401 .container => unreachable,
402 .none => unreachable,
364 }403 }
365 }404 }
366405
...@@ -376,6 +415,7 @@ pub const Scope = struct {...@@ -376,6 +415,7 @@ pub const Scope = struct {
376 .zir_module => null,415 .zir_module => null,
377 .file => null,416 .file => null,
378 .container => null,417 .container => null,
418 .none => unreachable,
379 };419 };
380 }420 }
381421
...@@ -390,6 +430,7 @@ pub const Scope = struct {...@@ -390,6 +430,7 @@ pub const Scope = struct {
390 .decl => return self.cast(DeclAnalysis).?.decl.scope,430 .decl => return self.cast(DeclAnalysis).?.decl.scope,
391 .file => return &self.cast(File).?.root_container.base,431 .file => return &self.cast(File).?.root_container.base,
392 .zir_module, .container => return self,432 .zir_module, .container => return self,
433 .none => unreachable,
393 }434 }
394 }435 }
395436
...@@ -406,6 +447,7 @@ pub const Scope = struct {...@@ -406,6 +447,7 @@ pub const Scope = struct {
406 .file => unreachable,447 .file => unreachable,
407 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),448 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
408 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),449 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
450 .none => unreachable,
409 }451 }
410 }452 }
411453
...@@ -414,6 +456,7 @@ pub const Scope = struct {...@@ -414,6 +456,7 @@ pub const Scope = struct {
414 switch (self.tag) {456 switch (self.tag) {
415 .file => return self.cast(File).?.contents.tree,457 .file => return self.cast(File).?.contents.tree,
416 .zir_module => unreachable,458 .zir_module => unreachable,
459 .none => unreachable,
417 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,460 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
418 .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,461 .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
419 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,462 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
...@@ -434,6 +477,7 @@ pub const Scope = struct {...@@ -434,6 +477,7 @@ pub const Scope = struct {
434 .zir_module => unreachable,477 .zir_module => unreachable,
435 .file => unreachable,478 .file => unreachable,
436 .container => unreachable,479 .container => unreachable,
480 .none => unreachable,
437 };481 };
438 }482 }
439483
...@@ -444,6 +488,7 @@ pub const Scope = struct {...@@ -444,6 +488,7 @@ pub const Scope = struct {
444 .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,488 .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
445 .file => return @fieldParentPtr(File, "base", base).sub_file_path,489 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
446 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,490 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
491 .none => unreachable,
447 .block => unreachable,492 .block => unreachable,
448 .gen_zir => unreachable,493 .gen_zir => unreachable,
449 .local_val => unreachable,494 .local_val => unreachable,
...@@ -456,6 +501,7 @@ pub const Scope = struct {...@@ -456,6 +501,7 @@ pub const Scope = struct {
456 switch (base.tag) {501 switch (base.tag) {
457 .file => return @fieldParentPtr(File, "base", base).unload(gpa),502 .file => return @fieldParentPtr(File, "base", base).unload(gpa),
458 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),503 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
504 .none => {},
459 .block => unreachable,505 .block => unreachable,
460 .gen_zir => unreachable,506 .gen_zir => unreachable,
461 .local_val => unreachable,507 .local_val => unreachable,
...@@ -470,6 +516,7 @@ pub const Scope = struct {...@@ -470,6 +516,7 @@ pub const Scope = struct {
470 .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),516 .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
471 .file => return @fieldParentPtr(File, "base", base).getSource(module),517 .file => return @fieldParentPtr(File, "base", base).getSource(module),
472 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),518 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
519 .none => unreachable,
473 .gen_zir => unreachable,520 .gen_zir => unreachable,
474 .local_val => unreachable,521 .local_val => unreachable,
475 .local_ptr => unreachable,522 .local_ptr => unreachable,
...@@ -483,6 +530,7 @@ pub const Scope = struct {...@@ -483,6 +530,7 @@ pub const Scope = struct {
483 switch (base.tag) {530 switch (base.tag) {
484 .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),531 .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
485 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),532 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
533 .none => unreachable,
486 .file => unreachable,534 .file => unreachable,
487 .block => unreachable,535 .block => unreachable,
488 .gen_zir => unreachable,536 .gen_zir => unreachable,
...@@ -505,6 +553,10 @@ pub const Scope = struct {...@@ -505,6 +553,10 @@ pub const Scope = struct {
505 scope_zir_module.deinit(gpa);553 scope_zir_module.deinit(gpa);
506 gpa.destroy(scope_zir_module);554 gpa.destroy(scope_zir_module);
507 },555 },
556 .none => {
557 const scope_none = @fieldParentPtr(None, "base", base);
558 gpa.destroy(scope_none);
559 },
508 .block => unreachable,560 .block => unreachable,
509 .gen_zir => unreachable,561 .gen_zir => unreachable,
510 .local_val => unreachable,562 .local_val => unreachable,
...@@ -527,6 +579,8 @@ pub const Scope = struct {...@@ -527,6 +579,8 @@ pub const Scope = struct {
527 zir_module,579 zir_module,
528 /// .zig source code.580 /// .zig source code.
529 file,581 file,
582 /// There is no .zig or .zir source code being compiled in this Module.
583 none,
530 /// struct, enum or union, every .file contains one of these.584 /// struct, enum or union, every .file contains one of these.
531 container,585 container,
532 block,586 block,
...@@ -622,7 +676,7 @@ pub const Scope = struct {...@@ -622,7 +676,7 @@ pub const Scope = struct {
622 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {676 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
623 switch (self.source) {677 switch (self.source) {
624 .unloaded => {678 .unloaded => {
625 const source = try module.root_pkg.root_src_dir.readFileAllocOptions(679 const source = try module.root_pkg.?.root_src_dir.readFileAllocOptions(
626 module.gpa,680 module.gpa,
627 self.sub_file_path,681 self.sub_file_path,
628 std.math.maxInt(u32),682 std.math.maxInt(u32),
...@@ -638,6 +692,12 @@ pub const Scope = struct {...@@ -638,6 +692,12 @@ pub const Scope = struct {
638 }692 }
639 };693 };
640694
695 /// For when there is no top level scope because there are no .zig files being compiled.
696 pub const None = struct {
697 pub const base_tag: Tag = .none;
698 base: Scope = Scope{ .tag = base_tag },
699 };
700
641 pub const ZIRModule = struct {701 pub const ZIRModule = struct {
642 pub const base_tag: Tag = .zir_module;702 pub const base_tag: Tag = .zir_module;
643 base: Scope = Scope{ .tag = base_tag },703 base: Scope = Scope{ .tag = base_tag },
...@@ -720,7 +780,7 @@ pub const Scope = struct {...@@ -720,7 +780,7 @@ pub const Scope = struct {
720 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {780 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
721 switch (self.source) {781 switch (self.source) {
722 .unloaded => {782 .unloaded => {
723 const source = try module.root_pkg.root_src_dir.readFileAllocOptions(783 const source = try module.root_pkg.?.root_src_dir.readFileAllocOptions(
724 module.gpa,784 module.gpa,
725 self.sub_file_path,785 self.sub_file_path,
726 std.math.maxInt(u32),786 std.math.maxInt(u32),
...@@ -855,20 +915,81 @@ pub const AllErrors = struct {...@@ -855,20 +915,81 @@ pub const AllErrors = struct {
855pub const InitOptions = struct {915pub const InitOptions = struct {
856 target: std.Target,916 target: std.Target,
857 root_name: []const u8,917 root_name: []const u8,
858 root_pkg: *Package,918 root_pkg: ?*Package,
859 output_mode: std.builtin.OutputMode,919 output_mode: std.builtin.OutputMode,
860 bin_file_dir: ?std.fs.Dir = null,920 bin_file_dir: ?std.fs.Dir = null,
861 bin_file_path: []const u8,921 bin_file_path: []const u8,
922 emit_h: ?[]const u8 = null,
862 link_mode: ?std.builtin.LinkMode = null,923 link_mode: ?std.builtin.LinkMode = null,
863 object_format: ?std.builtin.ObjectFormat = null,924 object_format: ?std.builtin.ObjectFormat = null,
864 optimize_mode: std.builtin.Mode = .Debug,925 optimize_mode: std.builtin.Mode = .Debug,
865 keep_source_files_loaded: bool = false,926 keep_source_files_loaded: bool = false,
927 clang_argv: []const []const u8 = &[0][]const u8{},
928 lib_dirs: []const []const u8 = &[0][]const u8{},
929 rpath_list: []const []const u8 = &[0][]const u8{},
930 c_source_files: []const []const u8 = &[0][]const u8{},
931 link_objects: []const []const u8 = &[0][]const u8{},
932 framework_dirs: []const []const u8 = &[0][]const u8{},
933 frameworks: []const []const u8 = &[0][]const u8{},
934 system_libs: []const []const u8 = &[0][]const u8{},
935 have_libc: bool = false,
936 have_libcpp: bool = false,
937 want_pic: ?bool = null,
938 want_sanitize_c: ?bool = null,
939 use_llvm: ?bool = null,
940 use_lld: ?bool = null,
941 use_clang: ?bool = null,
942 rdynamic: bool = false,
943 strip: bool = false,
944 linker_script: ?[]const u8 = null,
945 version_script: ?[]const u8 = null,
946 disable_c_depfile: bool = false,
947 override_soname: ?[]const u8 = null,
948 linker_optimization: ?[]const u8 = null,
949 linker_gc_sections: ?bool = null,
950 linker_allow_shlib_undefined: ?bool = null,
951 linker_bind_global_refs_locally: ?bool = null,
952 linker_z_nodelete: bool = false,
953 linker_z_defs: bool = false,
954 stack_size_override: u64 = 0,
955 compiler_id: [16]u8,
866};956};
867957
868pub fn init(gpa: *Allocator, options: InitOptions) !Module {958pub fn init(gpa: *Allocator, options: InitOptions) !Module {
869 const root_name = try gpa.dupe(u8, options.root_name);959 const root_name = try gpa.dupe(u8, options.root_name);
870 errdefer gpa.free(root_name);960 errdefer gpa.free(root_name);
871961
962 const ofmt = options.object_format orelse options.target.getObjectFormat();
963
964 // Make a decision on whether to use LLD or our own linker.
965 const use_lld = if (options.use_lld) |explicit| explicit else blk: {
966 if (!build_options.have_llvm)
967 break :blk false;
968
969 if (ofmt == .c)
970 break :blk false;
971
972 // Our linker can't handle objects or most advanced options yet.
973 if (options.link_objects.len != 0 or
974 options.c_source_files.len != 0 or
975 options.frameworks.len != 0 or
976 options.system_libs.len != 0 or
977 options.have_libc or options.have_libcpp or
978 options.linker_script != null or options.version_script != null)
979 {
980 break :blk true;
981 }
982 break :blk false;
983 };
984
985 // Make a decision on whether to use LLVM or our own backend.
986 const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
987 // We would want to prefer LLVM for release builds when it is available, however
988 // we don't have an LLVM backend yet :)
989 // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
990 break :blk false;
991 };
992
872 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();993 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
873 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{994 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
874 .root_name = root_name,995 .root_name = root_name,
...@@ -876,39 +997,130 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -876,39 +997,130 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
876 .target = options.target,997 .target = options.target,
877 .output_mode = options.output_mode,998 .output_mode = options.output_mode,
878 .link_mode = options.link_mode orelse .Static,999 .link_mode = options.link_mode orelse .Static,
879 .object_format = options.object_format orelse options.target.getObjectFormat(),1000 .object_format = ofmt,
880 .optimize_mode = options.optimize_mode,1001 .optimize_mode = options.optimize_mode,
1002 .use_lld = use_lld,
1003 .use_llvm = use_llvm,
1004 .objects = options.link_objects,
1005 .frameworks = options.frameworks,
1006 .framework_dirs = options.framework_dirs,
1007 .system_libs = options.system_libs,
1008 .lib_dirs = options.lib_dirs,
1009 .rpath_list = options.rpath_list,
1010 .strip = options.strip,
881 });1011 });
882 errdefer bin_file.destroy();1012 errdefer bin_file.destroy();
8831013
884 const root_scope = blk: {1014 const root_scope = blk: {
885 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {1015 if (options.root_pkg) |root_pkg| {
886 const root_scope = try gpa.create(Scope.File);1016 if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
887 root_scope.* = .{1017 const root_scope = try gpa.create(Scope.File);
888 .sub_file_path = options.root_pkg.root_src_path,1018 root_scope.* = .{
889 .source = .{ .unloaded = {} },1019 .sub_file_path = root_pkg.root_src_path,
890 .contents = .{ .not_available = {} },1020 .source = .{ .unloaded = {} },
891 .status = .never_loaded,1021 .contents = .{ .not_available = {} },
892 .root_container = .{1022 .status = .never_loaded,
893 .file_scope = root_scope,1023 .root_container = .{
1024 .file_scope = root_scope,
1025 .decls = .{},
1026 },
1027 };
1028 break :blk &root_scope.base;
1029 } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
1030 const root_scope = try gpa.create(Scope.ZIRModule);
1031 root_scope.* = .{
1032 .sub_file_path = root_pkg.root_src_path,
1033 .source = .{ .unloaded = {} },
1034 .contents = .{ .not_available = {} },
1035 .status = .never_loaded,
894 .decls = .{},1036 .decls = .{},
895 },1037 };
896 };1038 break :blk &root_scope.base;
897 break :blk &root_scope.base;1039 } else {
898 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {1040 unreachable;
899 const root_scope = try gpa.create(Scope.ZIRModule);1041 }
900 root_scope.* = .{
901 .sub_file_path = options.root_pkg.root_src_path,
902 .source = .{ .unloaded = {} },
903 .contents = .{ .not_available = {} },
904 .status = .never_loaded,
905 .decls = .{},
906 };
907 break :blk &root_scope.base;
908 } else {1042 } else {
909 unreachable;1043 const root_scope = try gpa.create(Scope.None);
1044 root_scope.* = .{};
1045 break :blk &root_scope.base;
1046 }
1047 };
1048
1049 // We put everything into the cache hash except for the root source file, because we want to
1050 // find the same binary and incrementally update it even if the file contents changed.
1051 const cache_dir = if (options.root_pkg) |root_pkg| root_pkg.root_src_dir else std.fs.cwd();
1052 var cache = try std.cache_hash.CacheHash.init(gpa, cache_dir, "zig-cache");
1053 errdefer cache.release();
1054
1055 // Now we will prepare hash state initializations to avoid redundantly computing hashes.
1056 // First we add common things between things that apply to zig source and all c source files.
1057 cache.add(options.compiler_id);
1058 cache.add(options.optimize_mode);
1059 cache.add(options.target.cpu.arch);
1060 cache.addBytes(options.target.cpu.model.name);
1061 cache.add(options.target.cpu.features.ints);
1062 cache.add(options.target.os.tag);
1063 switch (options.target.os.tag) {
1064 .linux => {
1065 cache.add(options.target.os.version_range.linux.range.min);
1066 cache.add(options.target.os.version_range.linux.range.max);
1067 cache.add(options.target.os.version_range.linux.glibc);
1068 },
1069 .windows => {
1070 cache.add(options.target.os.version_range.windows.min);
1071 cache.add(options.target.os.version_range.windows.max);
1072 },
1073 .freebsd,
1074 .macosx,
1075 .ios,
1076 .tvos,
1077 .watchos,
1078 .netbsd,
1079 .openbsd,
1080 .dragonfly,
1081 => {
1082 cache.add(options.target.os.version_range.semver.min);
1083 cache.add(options.target.os.version_range.semver.max);
1084 },
1085 else => {},
1086 }
1087 cache.add(options.target.abi);
1088 cache.add(ofmt);
1089 // TODO PIC (see detect_pic from codegen.cpp)
1090 cache.add(bin_file.options.link_mode);
1091 cache.add(options.strip);
1092
1093 // Make a decision on whether to use Clang for translate-c and compiling C files.
1094 const use_clang = if (options.use_clang) |explicit| explicit else blk: {
1095 if (build_options.have_llvm) {
1096 // Can't use it if we don't have it!
1097 break :blk false;
910 }1098 }
1099 // It's not planned to do our own translate-c or C compilation.
1100 break :blk true;
911 };1101 };
1102 var c_object_table = std.AutoArrayHashMapUnmanaged(*CObject, void){};
1103 errdefer {
1104 for (c_object_table.items()) |entry| entry.key.destroy(gpa);
1105 c_object_table.deinit(gpa);
1106 }
1107 // Add a `CObject` for each `c_source_files`.
1108 try c_object_table.ensureCapacity(gpa, options.c_source_files.len);
1109 for (options.c_source_files) |c_source_file| {
1110 var local_arena = std.heap.ArenaAllocator.init(gpa);
1111 errdefer local_arena.deinit();
1112
1113 const c_object = try local_arena.allocator.create(CObject);
1114 const src_path = try local_arena.allocator.dupe(u8, c_source_file);
1115
1116 c_object.* = .{
1117 .status = .{ .new = {} },
1118 .src_path = src_path,
1119 .extra_flags = &[0][]const u8{},
1120 .arena = local_arena.state,
1121 };
1122 c_object_table.putAssumeCapacityNoClobber(c_object, {});
1123 }
9121124
913 return Module{1125 return Module{
914 .gpa = gpa,1126 .gpa = gpa,
...@@ -920,6 +1132,11 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -920,6 +1132,11 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
920 .bin_file = bin_file,1132 .bin_file = bin_file,
921 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),1133 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
922 .keep_source_files_loaded = options.keep_source_files_loaded,1134 .keep_source_files_loaded = options.keep_source_files_loaded,
1135 .use_clang = use_clang,
1136 .clang_argv = options.clang_argv,
1137 .c_source_files = options.c_source_files,
1138 .cache = cache,
1139 .c_object_table = c_object_table,
923 };1140 };
924}1141}
9251142
...@@ -935,11 +1152,21 @@ pub fn deinit(self: *Module) void {...@@ -935,11 +1152,21 @@ pub fn deinit(self: *Module) void {
935 }1152 }
936 self.decl_table.deinit(gpa);1153 self.decl_table.deinit(gpa);
9371154
1155 for (self.c_object_table.items()) |entry| {
1156 entry.key.destroy(gpa);
1157 }
1158 self.c_object_table.deinit(gpa);
1159
938 for (self.failed_decls.items()) |entry| {1160 for (self.failed_decls.items()) |entry| {
939 entry.value.destroy(gpa);1161 entry.value.destroy(gpa);
940 }1162 }
941 self.failed_decls.deinit(gpa);1163 self.failed_decls.deinit(gpa);
9421164
1165 for (self.failed_c_objects.items()) |entry| {
1166 entry.value.destroy(gpa);
1167 }
1168 self.failed_c_objects.deinit(gpa);
1169
943 for (self.failed_files.items()) |entry| {1170 for (self.failed_files.items()) |entry| {
944 entry.value.destroy(gpa);1171 entry.value.destroy(gpa);
945 }1172 }
...@@ -969,6 +1196,7 @@ pub fn deinit(self: *Module) void {...@@ -969,6 +1196,7 @@ pub fn deinit(self: *Module) void {
969 gpa.free(entry.key);1196 gpa.free(entry.key);
970 }1197 }
971 self.global_error_set.deinit(gpa);1198 self.global_error_set.deinit(gpa);
1199 self.cache.release();
972 self.* = undefined;1200 self.* = undefined;
973}1201}
9741202
...@@ -995,7 +1223,15 @@ pub fn update(self: *Module) !void {...@@ -995,7 +1223,15 @@ pub fn update(self: *Module) !void {
9951223
996 self.generation += 1;1224 self.generation += 1;
9971225
998 // TODO Use the cache hash file system to detect which source files changed.1226 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
1227 // TODO Look into caching this data in memory to improve performance.
1228 // Add a WorkItem for each C object.
1229 try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len);
1230 for (self.c_object_table.items()) |entry| {
1231 self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key });
1232 }
1233
1234 // TODO Detect which source files changed.
999 // Until then we simulate a full cache miss. Source files could have been loaded for any reason;1235 // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
1000 // to force a refresh we unload now.1236 // to force a refresh we unload now.
1001 if (self.root_scope.cast(Scope.File)) |zig_file| {1237 if (self.root_scope.cast(Scope.File)) |zig_file| {
...@@ -1053,6 +1289,7 @@ pub fn makeBinFileWritable(self: *Module) !void {...@@ -1053,6 +1289,7 @@ pub fn makeBinFileWritable(self: *Module) !void {
10531289
1054pub fn totalErrorCount(self: *Module) usize {1290pub fn totalErrorCount(self: *Module) usize {
1055 const total = self.failed_decls.items().len +1291 const total = self.failed_decls.items().len +
1292 self.failed_c_objects.items().len +
1056 self.failed_files.items().len +1293 self.failed_files.items().len +
1057 self.failed_exports.items().len;1294 self.failed_exports.items().len;
1058 return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total;1295 return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total;
...@@ -1065,6 +1302,12 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {...@@ -1065,6 +1302,12 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
1065 var errors = std.ArrayList(AllErrors.Message).init(self.gpa);1302 var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
1066 defer errors.deinit();1303 defer errors.deinit();
10671304
1305 for (self.failed_c_objects.items()) |entry| {
1306 const c_object = entry.key;
1307 const err_msg = entry.value;
1308 const source = c_object.status.failure;
1309 try AllErrors.add(&arena, &errors, c_object.src_path, source, err_msg.*);
1310 }
1068 for (self.failed_files.items()) |entry| {1311 for (self.failed_files.items()) |entry| {
1069 const scope = entry.key;1312 const scope = entry.key;
1070 const err_msg = entry.value;1313 const err_msg = entry.value;
...@@ -1085,8 +1328,14 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {...@@ -1085,8 +1328,14 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
1085 }1328 }
10861329
1087 if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {1330 if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
1331 const global_err_src_path = blk: {
1332 if (self.root_pkg) |root_pkg| break :blk root_pkg.root_src_path;
1333 if (self.c_source_files.len != 0) break :blk self.c_source_files[0];
1334 if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0];
1335 break :blk "(no file)";
1336 };
1088 try errors.append(.{1337 try errors.append(.{
1089 .src_path = self.root_pkg.root_src_path,1338 .src_path = global_err_src_path,
1090 .line = 0,1339 .line = 0,
1091 .column = 0,1340 .column = 0,
1092 .byte_offset = 0,1341 .byte_offset = 0,
...@@ -1175,6 +1424,41 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1175,6 +1424,41 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1175 decl.analysis = .codegen_failure_retryable;1424 decl.analysis = .codegen_failure_retryable;
1176 };1425 };
1177 },1426 },
1427 .c_object => |c_object| {
1428 // Free the previous attempt.
1429 switch (c_object.status) {
1430 .new => {},
1431 .success => |o_file_path| {
1432 self.gpa.free(o_file_path);
1433 c_object.status = .{ .new = {} };
1434 },
1435 .failure => |source| {
1436 self.failed_c_objects.removeAssertDiscard(c_object);
1437 self.gpa.free(source);
1438
1439 c_object.status = .{ .new = {} };
1440 },
1441 }
1442 if (!build_options.have_llvm) {
1443 try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
1444 self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
1445 self.gpa,
1446 0,
1447 "clang not available: compiler not built with LLVM extensions enabled",
1448 .{},
1449 ));
1450 c_object.status = .{ .failure = "" };
1451 continue;
1452 }
1453 try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
1454 self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
1455 self.gpa,
1456 0,
1457 "TODO: implement invoking clang to compile C source files",
1458 .{},
1459 ));
1460 c_object.status = .{ .failure = "" };
1461 },
1178 };1462 };
1179}1463}
11801464
...@@ -3161,6 +3445,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err...@@ -3161,6 +3445,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
3161 zir_module.status = .loaded_sema_failure;3445 zir_module.status = .loaded_sema_failure;
3162 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);3446 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
3163 },3447 },
3448 .none => unreachable,
3164 .file => unreachable,3449 .file => unreachable,
3165 .container => unreachable,3450 .container => unreachable,
3166 }3451 }
src-self-hosted/link.zig+26-5
...@@ -16,14 +16,36 @@ pub const Options = struct {...@@ -16,14 +16,36 @@ pub const Options = struct {
16 object_format: std.builtin.ObjectFormat,16 object_format: std.builtin.ObjectFormat,
17 optimize_mode: std.builtin.Mode,17 optimize_mode: std.builtin.Mode,
18 root_name: []const u8,18 root_name: []const u8,
19 root_pkg: *const Package,19 root_pkg: ?*const Package,
20 /// Used for calculating how much space to reserve for symbols in case the binary file20 /// Used for calculating how much space to reserve for symbols in case the binary file
21 /// does not already have a symbol table.21 /// does not already have a symbol table.
22 symbol_count_hint: u64 = 32,22 symbol_count_hint: u64 = 32,
23 /// Used for calculating how much space to reserve for executable program code in case23 /// Used for calculating how much space to reserve for executable program code in case
24 /// the binary file deos not already have such a section.24 /// the binary file does not already have such a section.
25 program_code_size_hint: u64 = 256 * 1024,25 program_code_size_hint: u64 = 256 * 1024,
26 entry_addr: ?u64 = null,26 entry_addr: ?u64 = null,
27 /// Set to `true` to omit debug info.
28 strip: bool = false,
29 /// If this is true then this link code is responsible for outputting an object
30 /// file and then using LLD to link it together with the link options and other objects.
31 /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary.
32 use_lld: bool = false,
33 /// If this is true then this link code is responsible for making an LLVM IR Module,
34 /// outputting it to an object file, and then linking that together with link options and
35 /// other objects.
36 /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
37 use_llvm: bool = false,
38
39 objects: []const []const u8 = &[0][]const u8{},
40 framework_dirs: []const []const u8 = &[0][]const u8{},
41 frameworks: []const []const u8 = &[0][]const u8{},
42 system_libs: []const []const u8 = &[0][]const u8{},
43 lib_dirs: []const []const u8 = &[0][]const u8{},
44 rpath_list: []const []const u8 = &[0][]const u8{},
45
46 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
47 return if (options.use_lld) .Obj else options.output_mode;
48 }
27};49};
2850
29pub const File = struct {51pub const File = struct {
...@@ -67,14 +89,13 @@ pub const File = struct {...@@ -67,14 +89,13 @@ pub const File = struct {
67 /// and does not cause Illegal Behavior. This operation is not atomic.89 /// and does not cause Illegal Behavior. This operation is not atomic.
68 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {90 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
69 switch (options.object_format) {91 switch (options.object_format) {
70 .unknown => unreachable,
71 .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),92 .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),
72 .elf => return Elf.openPath(allocator, dir, sub_path, options),93 .elf => return Elf.openPath(allocator, dir, sub_path, options),
73 .macho => return MachO.openPath(allocator, dir, sub_path, options),94 .macho => return MachO.openPath(allocator, dir, sub_path, options),
74 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),95 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
75 .c => return C.openPath(allocator, dir, sub_path, options),96 .c => return C.openPath(allocator, dir, sub_path, options),
76 .hex => return error.TODOImplementHex,97 .hex => return error.HexObjectFormatUnimplemented,
77 .raw => return error.TODOImplementRaw,98 .raw => return error.RawObjectFormatUnimplemented,
78 }99 }
79 }100 }
80101
src-self-hosted/link/C.zig+3
...@@ -25,6 +25,9 @@ error_msg: *Module.ErrorMsg = undefined,...@@ -25,6 +25,9 @@ error_msg: *Module.ErrorMsg = undefined,
25pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {25pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
26 assert(options.object_format == .c);26 assert(options.object_format == .c);
2727
28 if (options.use_llvm) return error.LLVM_HasNoCBackend;
29 if (options.use_lld) return error.LLD_HasNoCBackend;
30
28 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });31 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
29 errdefer file.close();32 errdefer file.close();
3033
src-self-hosted/link/Coff.zig+3
...@@ -113,6 +113,9 @@ pub const SrcFn = void;...@@ -113,6 +113,9 @@ pub const SrcFn = void;
113pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {113pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
114 assert(options.object_format == .coff);114 assert(options.object_format == .coff);
115115
116 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO
117 if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO
118
116 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });119 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
117 errdefer file.close();120 errdefer file.close();
118121
src-self-hosted/link/Elf.zig+12-9
...@@ -219,6 +219,9 @@ pub const SrcFn = struct {...@@ -219,6 +219,9 @@ pub const SrcFn = struct {
219pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {219pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
220 assert(options.object_format == .elf);220 assert(options.object_format == .elf);
221221
222 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForELF; // TODO
223 if (options.use_lld) return error.LLD_LinkingIsTODOForELF; // TODO
224
222 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });225 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
223 errdefer file.close();226 errdefer file.close();
224227
...@@ -235,7 +238,7 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option...@@ -235,7 +238,7 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option
235238
236/// Returns error.IncrFailed if incremental update could not be performed.239/// Returns error.IncrFailed if incremental update could not be performed.
237fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {240fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
238 switch (options.output_mode) {241 switch (options.effectiveOutputMode()) {
239 .Exe => {},242 .Exe => {},
240 .Obj => {},243 .Obj => {},
241 .Lib => return error.IncrFailed,244 .Lib => return error.IncrFailed,
...@@ -264,7 +267,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {...@@ -264,7 +267,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
264/// Truncates the existing file contents and overwrites the contents.267/// Truncates the existing file contents and overwrites the contents.
265/// Returns an error if `file` is not already open with +read +write +seek abilities.268/// Returns an error if `file` is not already open with +read +write +seek abilities.
266fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {269fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
267 switch (options.output_mode) {270 switch (options.effectiveOutputMode()) {
268 .Exe => {},271 .Exe => {},
269 .Obj => {},272 .Obj => {},
270 .Lib => return error.TODOImplementWritingLibFiles,273 .Lib => return error.TODOImplementWritingLibFiles,
...@@ -861,8 +864,8 @@ pub fn flush(self: *Elf, module: *Module) !void {...@@ -861,8 +864,8 @@ pub fn flush(self: *Elf, module: *Module) !void {
861 },864 },
862 }865 }
863 // Write the form for the compile unit, which must match the abbrev table above.866 // Write the form for the compile unit, which must match the abbrev table above.
864 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);867 const name_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_path);
865 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);868 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_dir_path);
866 const producer_strp = try self.makeDebugString(link.producer_string);869 const producer_strp = try self.makeDebugString(link.producer_string);
867 // Currently only one compilation unit is supported, so the address range is simply870 // Currently only one compilation unit is supported, so the address range is simply
868 // identical to the main program header virtual address and memory size.871 // identical to the main program header virtual address and memory size.
...@@ -1031,7 +1034,7 @@ pub fn flush(self: *Elf, module: *Module) !void {...@@ -1031,7 +1034,7 @@ pub fn flush(self: *Elf, module: *Module) !void {
1031 0, // include_directories (none except the compilation unit cwd)1034 0, // include_directories (none except the compilation unit cwd)
1032 });1035 });
1033 // file_names[0]1036 // file_names[0]
1034 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name1037 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.?.root_src_path); // relative path name
1035 di_buf.appendSliceAssumeCapacity(&[_]u8{1038 di_buf.appendSliceAssumeCapacity(&[_]u8{
1036 0, // null byte for the relative path name1039 0, // null byte for the relative path name
1037 0, // directory_index1040 0, // directory_index
...@@ -1195,7 +1198,7 @@ pub fn flush(self: *Elf, module: *Module) !void {...@@ -1195,7 +1198,7 @@ pub fn flush(self: *Elf, module: *Module) !void {
1195 }1198 }
1196 self.shdr_table_dirty = false;1199 self.shdr_table_dirty = false;
1197 }1200 }
1198 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {1201 if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {
1199 log.debug("flushing. no_entry_point_found = true\n", .{});1202 log.debug("flushing. no_entry_point_found = true\n", .{});
1200 self.error_flags.no_entry_point_found = true;1203 self.error_flags.no_entry_point_found = true;
1201 } else {1204 } else {
...@@ -1255,7 +1258,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -1255,7 +1258,7 @@ fn writeElfHeader(self: *Elf) !void {
12551258
1256 assert(index == 16);1259 assert(index == 16);
12571260
1258 const elf_type = switch (self.base.options.output_mode) {1261 const elf_type = switch (self.base.options.effectiveOutputMode()) {
1259 .Exe => elf.ET.EXEC,1262 .Exe => elf.ET.EXEC,
1260 .Obj => elf.ET.REL,1263 .Obj => elf.ET.REL,
1261 .Lib => switch (self.base.options.link_mode) {1264 .Lib => switch (self.base.options.link_mode) {
...@@ -2430,8 +2433,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {...@@ -2430,8 +2433,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {
2430 directory_count * 8 + file_name_count * 8 +2433 directory_count * 8 + file_name_count * 8 +
2431 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like2434 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
2432 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.2435 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2433 self.base.options.root_pkg.root_src_dir_path.len +2436 self.base.options.root_pkg.?.root_src_dir_path.len +
2434 self.base.options.root_pkg.root_src_path.len);2437 self.base.options.root_pkg.?.root_src_path.len);
2435}2438}
24362439
2437fn dbgInfoNeededHeaderBytes(self: Elf) u32 {2440fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
src-self-hosted/link/MachO.zig+3
...@@ -137,6 +137,9 @@ pub const SrcFn = struct {...@@ -137,6 +137,9 @@ pub const SrcFn = struct {
137pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {137pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
138 assert(options.object_format == .macho);138 assert(options.object_format == .macho);
139139
140 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
141 if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO
142
140 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });143 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
141 errdefer file.close();144 errdefer file.close();
142145
src-self-hosted/link/Wasm.zig+3
...@@ -52,6 +52,9 @@ funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},...@@ -52,6 +52,9 @@ funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
52pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {52pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
53 assert(options.object_format == .wasm);53 assert(options.object_format == .wasm);
5454
55 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
56 if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
57
55 // TODO: read the file and keep vaild parts instead of truncating58 // TODO: read the file and keep vaild parts instead of truncating
56 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true });59 const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true });
57 errdefer file.close();60 errdefer file.close();
src-self-hosted/main.zig+87-19
...@@ -13,6 +13,7 @@ const Package = @import("Package.zig");...@@ -13,6 +13,7 @@ const Package = @import("Package.zig");
13const zir = @import("zir.zig");13const zir = @import("zir.zig");
14const build_options = @import("build_options");14const build_options = @import("build_options");
15const warn = std.log.warn;15const warn = std.log.warn;
16const introspect = @import("introspect.zig");
1617
17fn fatal(comptime format: []const u8, args: anytype) noreturn {18fn fatal(comptime format: []const u8, args: anytype) noreturn {
18 std.log.emerg(format, args);19 std.log.emerg(format, args);
...@@ -231,7 +232,6 @@ pub fn buildOutputType(...@@ -231,7 +232,6 @@ pub fn buildOutputType(
231 var root_src_file: ?[]const u8 = null;232 var root_src_file: ?[]const u8 = null;
232 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };233 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
233 var strip = false;234 var strip = false;
234 var emit_h = true;
235 var watch = false;235 var watch = false;
236 var debug_tokenize = false;236 var debug_tokenize = false;
237 var debug_ast_tree = false;237 var debug_ast_tree = false;
...@@ -248,6 +248,7 @@ pub fn buildOutputType(...@@ -248,6 +248,7 @@ pub fn buildOutputType(
248 var target_dynamic_linker: ?[]const u8 = null;248 var target_dynamic_linker: ?[]const u8 = null;
249 var target_ofmt: ?[]const u8 = null;249 var target_ofmt: ?[]const u8 = null;
250 var output_mode: std.builtin.OutputMode = undefined;250 var output_mode: std.builtin.OutputMode = undefined;
251 var emit_h: Emit = undefined;
251 var ensure_libc_on_non_freestanding = false;252 var ensure_libc_on_non_freestanding = false;
252 var ensure_libcpp_on_non_freestanding = false;253 var ensure_libcpp_on_non_freestanding = false;
253 var have_libc = false;254 var have_libc = false;
...@@ -269,6 +270,9 @@ pub fn buildOutputType(...@@ -269,6 +270,9 @@ pub fn buildOutputType(
269 var linker_z_nodelete = false;270 var linker_z_nodelete = false;
270 var linker_z_defs = false;271 var linker_z_defs = false;
271 var stack_size_override: u64 = 0;272 var stack_size_override: u64 = 0;
273 var use_llvm: ?bool = null;
274 var use_lld: ?bool = null;
275 var use_clang: ?bool = null;
272276
273 var system_libs = std.ArrayList([]const u8).init(gpa);277 var system_libs = std.ArrayList([]const u8).init(gpa);
274 defer system_libs.deinit();278 defer system_libs.deinit();
...@@ -296,6 +300,10 @@ pub fn buildOutputType(...@@ -296,6 +300,10 @@ pub fn buildOutputType(
296300
297 if (arg_mode == .build) {301 if (arg_mode == .build) {
298 output_mode = arg_mode.build;302 output_mode = arg_mode.build;
303 emit_h = switch (output_mode) {
304 .Exe => .no,
305 .Obj, .Lib => .yes_default_path,
306 };
299307
300 const args = all_args[2..];308 const args = all_args[2..];
301 var i: usize = 0;309 var i: usize = 0;
...@@ -416,6 +424,18 @@ pub fn buildOutputType(...@@ -416,6 +424,18 @@ pub fn buildOutputType(
416 want_pic = true;424 want_pic = true;
417 } else if (mem.eql(u8, arg, "-fno-PIC")) {425 } else if (mem.eql(u8, arg, "-fno-PIC")) {
418 want_pic = false;426 want_pic = false;
427 } else if (mem.eql(u8, arg, "-fLLVM")) {
428 use_llvm = true;
429 } else if (mem.eql(u8, arg, "-fno-LLVM")) {
430 use_llvm = false;
431 } else if (mem.eql(u8, arg, "-fLLD")) {
432 use_lld = true;
433 } else if (mem.eql(u8, arg, "-fno-LLD")) {
434 use_lld = false;
435 } else if (mem.eql(u8, arg, "-fClang")) {
436 use_clang = true;
437 } else if (mem.eql(u8, arg, "-fno-Clang")) {
438 use_clang = false;
419 } else if (mem.eql(u8, arg, "-rdynamic")) {439 } else if (mem.eql(u8, arg, "-rdynamic")) {
420 rdynamic = true;440 rdynamic = true;
421 } else if (mem.eql(u8, arg, "-femit-bin")) {441 } else if (mem.eql(u8, arg, "-femit-bin")) {
...@@ -430,6 +450,12 @@ pub fn buildOutputType(...@@ -430,6 +450,12 @@ pub fn buildOutputType(
430 emit_zir = .{ .yes = arg["-femit-zir=".len..] };450 emit_zir = .{ .yes = arg["-femit-zir=".len..] };
431 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {451 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
432 emit_zir = .no;452 emit_zir = .no;
453 } else if (mem.eql(u8, arg, "-femit-h")) {
454 emit_h = .yes_default_path;
455 } else if (mem.startsWith(u8, arg, "-femit-h=")) {
456 emit_h = .{ .yes = arg["-femit-h=".len..] };
457 } else if (mem.eql(u8, arg, "-fno-emit-h")) {
458 emit_h = .no;
433 } else if (mem.eql(u8, arg, "-dynamic")) {459 } else if (mem.eql(u8, arg, "-dynamic")) {
434 link_mode = .Dynamic;460 link_mode = .Dynamic;
435 } else if (mem.eql(u8, arg, "-static")) {461 } else if (mem.eql(u8, arg, "-static")) {
...@@ -491,7 +517,7 @@ pub fn buildOutputType(...@@ -491,7 +517,7 @@ pub fn buildOutputType(
491 }517 }
492 }518 }
493 } else {519 } else {
494 emit_h = false;520 emit_h = .no;
495 strip = true;521 strip = true;
496 ensure_libc_on_non_freestanding = true;522 ensure_libc_on_non_freestanding = true;
497 ensure_libcpp_on_non_freestanding = arg_mode == .cpp;523 ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
...@@ -874,14 +900,6 @@ pub fn buildOutputType(...@@ -874,14 +900,6 @@ pub fn buildOutputType(
874 }900 }
875 }901 }
876902
877 if (system_libs.items.len != 0) {
878 fatal("linking against system libraries not yet supported", .{});
879 }
880
881 const src_path = root_src_file orelse {
882 fatal("expected at least one file argument", .{});
883 };
884
885 const object_format: ?std.Target.ObjectFormat = blk: {903 const object_format: ?std.Target.ObjectFormat = blk: {
886 const ofmt = target_ofmt orelse break :blk null;904 const ofmt = target_ofmt orelse break :blk null;
887 if (mem.eql(u8, ofmt, "elf")) {905 if (mem.eql(u8, ofmt, "elf")) {
...@@ -909,11 +927,14 @@ pub fn buildOutputType(...@@ -909,11 +927,14 @@ pub fn buildOutputType(
909 .no => {927 .no => {
910 fatal("-fno-emit-bin not supported yet", .{});928 fatal("-fno-emit-bin not supported yet", .{});
911 },929 },
912 .yes_default_path => if (object_format != null and object_format.? == .c)930 .yes_default_path => try std.zig.binNameAlloc(
913 try std.fmt.allocPrint(arena, "{}.c", .{root_name})931 arena,
914 else932 root_name,
915 try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),933 target_info.target,
916934 output_mode,
935 link_mode,
936 object_format,
937 ),
917 .yes => |p| p,938 .yes => |p| p,
918 };939 };
919940
...@@ -930,10 +951,25 @@ pub fn buildOutputType(...@@ -930,10 +951,25 @@ pub fn buildOutputType(
930 .yes => |p| p,951 .yes => |p| p,
931 };952 };
932953
933 const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);954 const root_pkg = if (root_src_file) |src_path| try Package.create(gpa, fs.cwd(), ".", src_path) else null;
934 defer root_pkg.destroy();955 defer if (root_pkg) |pkg| pkg.destroy();
935956
936 var module = try Module.init(gpa, .{957 const emit_h_path: ?[]const u8 = switch (emit_h) {
958 .yes => |p| p,
959 .no => null,
960 .yes_default_path => try std.fmt.allocPrint(arena, "{}.h", .{root_name}),
961 };
962
963 // TODO Remove this, we'll have this error emitted lazily only if the features would end
964 // up actually getting used.
965 //if (!build_options.have_llvm) {
966 // if ((use_llvm orelse false) or (use_lld orelse false) or (use_clang orelse false))
967 // fatal("-fLLVM, -fLLD, and -fClang unavailable: compiler not built with LLVM extensions enabled", .{});
968 //}
969
970 const compiler_id = try introspect.resolveCompilerId(gpa);
971
972 var module = Module.init(gpa, .{
937 .root_name = root_name,973 .root_name = root_name,
938 .target = target_info.target,974 .target = target_info.target,
939 .output_mode = output_mode,975 .output_mode = output_mode,
...@@ -944,7 +980,39 @@ pub fn buildOutputType(...@@ -944,7 +980,39 @@ pub fn buildOutputType(
944 .object_format = object_format,980 .object_format = object_format,
945 .optimize_mode = build_mode,981 .optimize_mode = build_mode,
946 .keep_source_files_loaded = zir_out_path != null,982 .keep_source_files_loaded = zir_out_path != null,
947 });983 .clang_argv = clang_argv.items,
984 .lib_dirs = lib_dirs.items,
985 .rpath_list = rpath_list.items,
986 .c_source_files = c_source_files.items,
987 .link_objects = link_objects.items,
988 .framework_dirs = framework_dirs.items,
989 .frameworks = frameworks.items,
990 .system_libs = system_libs.items,
991 .emit_h = emit_h_path,
992 .have_libc = have_libc,
993 .have_libcpp = have_libcpp,
994 .want_pic = want_pic,
995 .want_sanitize_c = want_sanitize_c,
996 .use_llvm = use_llvm,
997 .use_lld = use_lld,
998 .use_clang = use_clang,
999 .rdynamic = rdynamic,
1000 .linker_script = linker_script,
1001 .version_script = version_script,
1002 .disable_c_depfile = disable_c_depfile,
1003 .override_soname = override_soname,
1004 .linker_optimization = linker_optimization,
1005 .linker_gc_sections = linker_gc_sections,
1006 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
1007 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
1008 .linker_z_nodelete = linker_z_nodelete,
1009 .linker_z_defs = linker_z_defs,
1010 .stack_size_override = stack_size_override,
1011 .compiler_id = compiler_id,
1012 .strip = strip,
1013 }) catch |err| {
1014 fatal("unable to initialize module: {}", .{@errorName(err)});
1015 };
948 defer module.deinit();1016 defer module.deinit();
9491017
950 const stdin = std.io.getStdIn().inStream();1018 const stdin = std.io.getStdIn().inStream();
src-self-hosted/test.zig+21-16
...@@ -9,6 +9,7 @@ const enable_qemu: bool = build_options.enable_qemu;...@@ -9,6 +9,7 @@ const enable_qemu: bool = build_options.enable_qemu;
9const enable_wine: bool = build_options.enable_wine;9const enable_wine: bool = build_options.enable_wine;
10const enable_wasmtime: bool = build_options.enable_wasmtime;10const enable_wasmtime: bool = build_options.enable_wasmtime;
11const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;11const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
12const introspect = @import("introspect.zig");
1213
13const cheader = @embedFile("link/cbe.h");14const cheader = @embedFile("link/cbe.h");
1415
...@@ -435,7 +436,10 @@ pub const TestContext = struct {...@@ -435,7 +436,10 @@ pub const TestContext = struct {
435 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);436 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
436 defer root_pkg.destroy();437 defer root_pkg.destroy();
437438
438 const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);439 const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
440 const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null, ofmt);
441
442 const compiler_id = try introspect.resolveCompilerId(arena);
439443
440 var module = try Module.init(allocator, .{444 var module = try Module.init(allocator, .{
441 .root_name = "test_case",445 .root_name = "test_case",
...@@ -450,7 +454,8 @@ pub const TestContext = struct {...@@ -450,7 +454,8 @@ pub const TestContext = struct {
450 .bin_file_path = bin_name,454 .bin_file_path = bin_name,
451 .root_pkg = root_pkg,455 .root_pkg = root_pkg,
452 .keep_source_files_loaded = true,456 .keep_source_files_loaded = true,
453 .object_format = if (case.cbe) .c else null,457 .object_format = ofmt,
458 .compiler_id = compiler_id,
454 });459 });
455 defer module.deinit();460 defer module.deinit();
456461
...@@ -693,23 +698,23 @@ pub const TestContext = struct {...@@ -693,23 +698,23 @@ pub const TestContext = struct {
693 }698 }
694699
695 var interpreter = spu.Interpreter(struct {700 var interpreter = spu.Interpreter(struct {
696 RAM: [0x10000]u8 = undefined,701 RAM: [0x10000]u8 = undefined,
697702
698 pub fn read8(bus: @This(), addr: u16) u8 {703 pub fn read8(bus: @This(), addr: u16) u8 {
699 return bus.RAM[addr];704 return bus.RAM[addr];
700 }705 }
701 pub fn read16(bus: @This(), addr: u16) u16 {706 pub fn read16(bus: @This(), addr: u16) u16 {
702 return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]);707 return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]);
703 }708 }
704709
705 pub fn write8(bus: *@This(), addr: u16, val: u8) void {710 pub fn write8(bus: *@This(), addr: u16, val: u8) void {
706 bus.RAM[addr] = val;711 bus.RAM[addr] = val;
707 }712 }
708713
709 pub fn write16(bus: *@This(), addr: u16, val: u16) void {714 pub fn write16(bus: *@This(), addr: u16, val: u16) void {
710 std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val);715 std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val);
711 }716 }
712 }){717 }){
713 .bus = .{},718 .bus = .{},
714 };719 };
715720