authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-26 20:41:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-26 20:41:07-07:00
logbfded492f0c12be2778c566bad0c82dea6ee76cb
tree0942c1ca40f40fe371504fd4818535a9b73f90e5
parent91c317bb9aa906684104db3d73442ab1198a83f4

stage2: rewire the frontend driver to whole-file-zir

* Remove some unused imports in AstGen.zig. I think it would make sense to start decoupling AstGen from the rest of the compiler code, similar to how the tokenizer and parser are decoupled. * AstGen: For decls, move the block_inline instructions to the top of the function so that they get lower ZIR instruction indexes. With this, the block_inline instruction index combined with its corresponding break_inline instruction index can be used to form a ZIR instruction range. This is useful for allocating an array to map ZIR instructions to semantically analyzed instructions. * Module: extract emit-h functionality into a struct, and only allocate it when emit-h is activated. * Module: remove the `decl_table` field. This previously was a table of all Decls in the entire Module. A "name hash" strategy was used to find decls within a given namespace, using this global table. Now, each Namespace has its own map of name to children Decls. - Additionally, there were 3 places that relied on iterating over decl_table in order to function: - C backend and SPIR-V backend. These now have their own decl_table that they keep populated when `updateDecl` and `removeDecl` are called. - emit-h. A `decl_table` field has been added to the new GlobalEmitH struct which is only allocated when emit-h is activated. * Module: fix ZIR serialization/deserialization bug in debug mode having to do with the secret safety tag for untagged unions. There is still an open TODO to investigate a friendlier solution to this problem with the language. * Module: improve deserialization of ZIR to allocate only exactly as much capacity as length in the instructions array so as to not waste space. * Module: move `srcHashEql` to `std.zig` to live next to the definition of `SrcHash` itself. * Module: re-introduce the logic for scanning top level declarations within a namespace. * Compilation: add an `analyze_pkg` Job which is used to kick off the start of semantic analysis by doing the equivalent of `_ = @import("std");`. The `analyze_pkg` job is unconditionally added to the work queue on every update(), with pkg set to the std lib pkg. * Rename TZIR to AIR in a few places. A more comprehensive rename will come later.

8 files changed, 493 insertions(+), 790 deletions(-)

BRANCH_TODO+55-304
...@@ -1,3 +1,6 @@...@@ -1,3 +1,6 @@
1 * reimplement semaDecl
2 * use a hash map for instructions because the array is too big
3
1 * keep track of file dependencies/dependants4 * keep track of file dependencies/dependants
2 * unload files from memory when a dependency is dropped5 * unload files from memory when a dependency is dropped
36
...@@ -5,6 +8,7 @@...@@ -5,6 +8,7 @@
58
6 * get rid of failed_root_src_file9 * get rid of failed_root_src_file
7 * get rid of Scope.DeclRef10 * get rid of Scope.DeclRef
11 * get rid of NameHash
8 * handle decl collision with usingnamespace12 * handle decl collision with usingnamespace
9 * the decl doing the looking up needs to create a decl dependency13 * the decl doing the looking up needs to create a decl dependency
10 on each usingnamespace decl14 on each usingnamespace decl
...@@ -35,58 +39,6 @@...@@ -35,58 +39,6 @@
35 * AstGen: add result location pointers to function calls39 * AstGen: add result location pointers to function calls
36 * nested function decl: how to refer to params?40 * nested function decl: how to refer to params?
3741
38 * detect when to put cached ZIR into the local cache instead of the global one
39
40 const container_name_hash: Scope.NameHash = if (found_pkg) |pkg|
41 pkg.namespace_hash
42 else
43 std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);
44
45 file_scope.* = .{
46 .root_container = .{
47 .parent = null,
48 .file_scope = file_scope,
49 .decls = .{},
50 .ty = struct_ty,
51 .parent_name_hash = container_name_hash,
52 },
53 };
54 mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
55 error.AnalysisFail => {
56 assert(mod.comp.totalErrorCount() != 0);
57 },
58 else => |e| return e,
59 };
60 return file_scope;
61
62
63
64 // Until then we simulate a full cache miss. Source files could have been loaded
65 // for any reason; to force a refresh we unload now.
66 module.unloadFile(module.root_scope);
67 module.failed_root_src_file = null;
68 module.analyzeNamespace(&module.root_scope.root_container) catch |err| switch (err) {
69 error.AnalysisFail => {
70 assert(self.totalErrorCount() != 0);
71 },
72 error.OutOfMemory => return error.OutOfMemory,
73 else => |e| {
74 module.failed_root_src_file = e;
75 },
76 };
77
78 // TODO only analyze imports if they are still referenced
79 for (module.import_table.items()) |entry| {
80 module.unloadFile(entry.value);
81 module.analyzeNamespace(&entry.value.root_container) catch |err| switch (err) {
82 error.AnalysisFail => {
83 assert(self.totalErrorCount() != 0);
84 },
85 else => |e| return e,
86 };
87 }
88
89
90pub fn createContainerDecl(42pub fn createContainerDecl(
91 mod: *Module,43 mod: *Module,
92 scope: *Scope,44 scope: *Scope,
...@@ -131,123 +83,6 @@ fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenInd...@@ -131,123 +83,6 @@ fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenInd
131}83}
13284
13385
134 const parent_name_hash: Scope.NameHash = if (found_pkg) |pkg|
135 pkg.namespace_hash
136 else
137 std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);
138
139 // We need a Decl to pass to AstGen and collect dependencies. But ultimately we
140 // want to pass them on to the Decl for the struct that represents the file.
141 var tmp_namespace: Scope.Namespace = .{
142 .parent = null,
143 .file_scope = new_file,
144 .parent_name_hash = parent_name_hash,
145 .ty = Type.initTag(.type),
146 };
147
148 const tree = try mod.getAstTree(new_file);
149
150
151 const top_decl = try mod.createNewDecl(
152 &tmp_namespace,
153 resolved_path,
154 0,
155 parent_name_hash,
156 std.zig.hashSrc(tree.source),
157 );
158 defer {
159 mod.decl_table.removeAssertDiscard(parent_name_hash);
160 top_decl.destroy(mod);
161 }
162
163 var gen_scope_arena = std.heap.ArenaAllocator.init(gpa);
164 defer gen_scope_arena.deinit();
165
166 var astgen = try AstGen.init(mod, top_decl, &gen_scope_arena.allocator);
167 defer astgen.deinit();
168
169 var gen_scope: Scope.GenZir = .{
170 .force_comptime = true,
171 .parent = &new_file.base,
172 .astgen = &astgen,
173 };
174 defer gen_scope.instructions.deinit(gpa);
175
176 const container_decl: ast.full.ContainerDecl = .{
177 .layout_token = null,
178 .ast = .{
179 .main_token = undefined,
180 .enum_token = null,
181 .members = tree.rootDecls(),
182 .arg = 0,
183 },
184 };
185
186 const struct_decl_ref = try AstGen.structDeclInner(
187 &gen_scope,
188 &gen_scope.base,
189 0,
190 container_decl,
191 .struct_decl,
192 );
193 _ = try gen_scope.addBreak(.break_inline, 0, struct_decl_ref);
194
195 var code = try gen_scope.finish();
196 defer code.deinit(gpa);
197 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
198 code.dump(gpa, "import", &gen_scope.base, 0) catch {};
199 }
200
201 var sema: Sema = .{
202 .mod = mod,
203 .gpa = gpa,
204 .arena = &gen_scope_arena.allocator,
205 .code = code,
206 .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
207 .owner_decl = top_decl,
208 .namespace = top_decl.namespace,
209 .func = null,
210 .owner_func = null,
211 .param_inst_list = &.{},
212 };
213 var block_scope: Scope.Block = .{
214 .parent = null,
215 .sema = &sema,
216 .src_decl = top_decl,
217 .instructions = .{},
218 .inlining = null,
219 .is_comptime = true,
220 };
221 defer block_scope.instructions.deinit(gpa);
222
223 const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
224 const analyzed_struct_inst = try sema.resolveInst(init_inst_zir_ref);
225 assert(analyzed_struct_inst.ty.zigTypeTag() == .Type);
226 const val = analyzed_struct_inst.value().?;
227 const struct_ty = try val.toType(&gen_scope_arena.allocator);
228 const struct_decl = struct_ty.getOwnerDecl();
229
230 struct_decl.contents_hash = top_decl.contents_hash;
231 new_file.namespace = struct_ty.getNamespace().?;
232 new_file.namespace.parent = null;
233 //new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash;
234
235 // Transfer the dependencies to `owner_decl`.
236 assert(top_decl.dependants.count() == 0);
237 for (top_decl.dependencies.items()) |entry| {
238 const dep = entry.key;
239 dep.removeDependant(top_decl);
240 if (dep == struct_decl) continue;
241 _ = try mod.declareDeclDependency(struct_decl, dep);
242 }
243
244 return new_file;
245
246
247
248
249
250
251pub fn analyzeFile(mod: *Module, file: *Scope.File) !void {86pub fn analyzeFile(mod: *Module, file: *Scope.File) !void {
252 // We call `getAstTree` here so that `analyzeFile` has the error set that includes87 // We call `getAstTree` here so that `analyzeFile` has the error set that includes
253 // file system operations, but `analyzeNamespace` does not.88 // file system operations, but `analyzeNamespace` does not.
...@@ -467,38 +302,6 @@ fn astgenAndSemaFn(...@@ -467,38 +302,6 @@ fn astgenAndSemaFn(
467 }302 }
468 return type_changed or is_inline != prev_is_inline;303 return type_changed or is_inline != prev_is_inline;
469}304}
470
471fn astgenAndSemaVarDecl(
472 mod: *Module,
473 decl: *Decl,
474 tree: ast.Tree,
475 var_decl: ast.full.VarDecl,
476) !bool {
477 const token_tags = tree.tokens.items(.tag);
478
479}
480
481
482 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
483 pub fn tree(scope: *Scope) *const ast.Tree {
484 switch (scope.tag) {
485 .file => return &scope.cast(File).?.tree,
486 .block => return &scope.cast(Block).?.src_decl.namespace.file_scope.tree,
487 .gen_zir => return scope.cast(GenZir).?.tree(),
488 .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.namespace.file_scope.tree,
489 .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.namespace.file_scope.tree,
490 .namespace => return &scope.cast(Namespace).?.file_scope.tree,
491 .decl_ref => return &scope.cast(DeclRef).?.decl.namespace.file_scope.tree,
492 }
493 }
494
495
496 error.FileNotFound => {
497 return mod.fail(&block.base, src, "unable to find '{s}'", .{operand});
498 },
499
500
501
502 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});305 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
503 mod.comp.stage1AddLinkLib(lib_name_str) catch |err| {306 mod.comp.stage1AddLinkLib(lib_name_str) catch |err| {
504 return mod.failTok(307 return mod.failTok(
...@@ -540,86 +343,6 @@ fn astgenAndSemaVarDecl(...@@ -540,86 +343,6 @@ fn astgenAndSemaVarDecl(
540 );343 );
541 }344 }
542345
543 if (counts.values == 0 and counts.decls == 0 and arg_inst == .none) {
544 // No explicitly provided tag values and no top level declarations! In this case,
545 // we can construct the enum type in AstGen and it will be correctly shared by all
546 // generic function instantiations and comptime function calls.
547 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
548 errdefer new_decl_arena.deinit();
549 const arena = &new_decl_arena.allocator;
550
551 var fields_map: std.StringArrayHashMapUnmanaged(void) = .{};
552 try fields_map.ensureCapacity(arena, counts.total_fields);
553 for (container_decl.ast.members) |member_node| {
554 if (member_node == counts.nonexhaustive_node)
555 continue;
556 const member = switch (node_tags[member_node]) {
557 .container_field_init => tree.containerFieldInit(member_node),
558 .container_field_align => tree.containerFieldAlign(member_node),
559 .container_field => tree.containerField(member_node),
560 else => unreachable, // We checked earlier.
561 };
562 const name_token = member.ast.name_token;
563 const tag_name = try mod.identifierTokenStringTreeArena(
564 scope,
565 name_token,
566 tree,
567 arena,
568 );
569 const gop = fields_map.getOrPutAssumeCapacity(tag_name);
570 if (gop.found_existing) {
571 const msg = msg: {
572 const msg = try mod.errMsg(
573 scope,
574 gz.tokSrcLoc(name_token),
575 "duplicate enum tag",
576 .{},
577 );
578 errdefer msg.destroy(gpa);
579 // Iterate to find the other tag. We don't eagerly store it in a hash
580 // map because in the hot path there will be no compile error and we
581 // don't need to waste time with a hash map.
582 const bad_node = for (container_decl.ast.members) |other_member_node| {
583 const other_member = switch (node_tags[other_member_node]) {
584 .container_field_init => tree.containerFieldInit(other_member_node),
585 .container_field_align => tree.containerFieldAlign(other_member_node),
586 .container_field => tree.containerField(other_member_node),
587 else => unreachable, // We checked earlier.
588 };
589 const other_tag_name = try mod.identifierTokenStringTreeArena(
590 scope,
591 other_member.ast.name_token,
592 tree,
593 arena,
594 );
595 if (mem.eql(u8, tag_name, other_tag_name))
596 break other_member_node;
597 } else unreachable;
598 const other_src = gz.nodeSrcLoc(bad_node);
599 try mod.errNote(scope, other_src, msg, "other tag here", .{});
600 break :msg msg;
601 };
602 return mod.failWithOwnedErrorMsg(scope, msg);
603 }
604 }
605 const enum_simple = try arena.create(Module.EnumSimple);
606 enum_simple.* = .{
607 .owner_decl = astgen.decl,
608 .node_offset = astgen.decl.nodeIndexToRelative(node),
609 .fields = fields_map,
610 };
611 const enum_ty = try Type.Tag.enum_simple.create(arena, enum_simple);
612 const enum_val = try Value.Tag.ty.create(arena, enum_ty);
613 const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
614 .ty = Type.initTag(.type),
615 .val = enum_val,
616 });
617 const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
618 const result = try gz.addDecl(.decl_val, decl_index, node);
619 return rvalue(gz, scope, rl, result, node);
620 }
621
622
623 if (mod.lookupIdentifier(scope, ident_name)) |decl| {346 if (mod.lookupIdentifier(scope, ident_name)) |decl| {
624 const msg = msg: {347 const msg = msg: {
625 const msg = try mod.errMsg(348 const msg = try mod.errMsg(
...@@ -687,29 +410,6 @@ fn astgenAndSemaVarDecl(...@@ -687,29 +410,6 @@ fn astgenAndSemaVarDecl(
687 }410 }
688 }411 }
689412
690 fn writeFuncExtra(
691 self: *Writer,
692 stream: anytype,
693 inst: Inst.Index,
694 var_args: bool,
695 ) !void {
696 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
697 const src = inst_data.src();
698 const extra = self.code.extraData(Inst.FuncExtra, inst_data.payload_index);
699 const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
700 const cc = extra.data.cc;
701 const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len];
702 return self.writeFuncCommon(
703 stream,
704 param_types,
705 extra.data.return_type,
706 var_args,
707 cc,
708 body,
709 src,
710 );
711 }
712
713413
714 const error_set = try arena.create(Module.ErrorSet);414 const error_set = try arena.create(Module.ErrorSet);
715 error_set.* = .{415 error_set.* = .{
...@@ -732,3 +432,54 @@ fn astgenAndSemaVarDecl(...@@ -732,3 +432,54 @@ fn astgenAndSemaVarDecl(
732432
733 // when implementing this be sure to add test coverage for the asm return type433 // when implementing this be sure to add test coverage for the asm return type
734 // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)434 // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)
435
436
437
438pub fn analyzeNamespace(
439 mod: *Module,
440 namespace: *Scope.Namespace,
441 decls: []const ast.Node.Index,
442) InnerError!void {
443 for (decls) |decl_node| switch (node_tags[decl_node]) {
444 .@"comptime" => {
445 const name_index = mod.getNextAnonNameIndex();
446 const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
447 defer mod.gpa.free(name);
448
449 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
450
451 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
452 namespace.decls.putAssumeCapacity(new_decl, {});
453 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
454 },
455
456 // Container fields are handled in AstGen.
457 .container_field_init,
458 .container_field_align,
459 .container_field,
460 => continue,
461
462 .test_decl => {
463 if (mod.comp.bin_file.options.is_test) {
464 log.err("TODO: analyze test decl", .{});
465 }
466 },
467 .@"usingnamespace" => {
468 const name_index = mod.getNextAnonNameIndex();
469 const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index});
470 defer mod.gpa.free(name);
471
472 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
473
474 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
475 namespace.decls.putAssumeCapacity(new_decl, {});
476
477 mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) {
478 error.OutOfMemory => return error.OutOfMemory,
479 error.AnalysisFail => continue,
480 };
481 },
482 else => unreachable,
483 };
484}
485
lib/std/zig.zig+4
...@@ -24,6 +24,10 @@ pub fn hashSrc(src: []const u8) SrcHash {...@@ -24,6 +24,10 @@ pub fn hashSrc(src: []const u8) SrcHash {
24 return out;24 return out;
25}25}
2626
27pub fn srcHashEql(a: SrcHash, b: SrcHash) bool {
28 return @bitCast(u128, a) == @bitCast(u128, b);
29}
30
27pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {31pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {
28 var out: SrcHash = undefined;32 var out: SrcHash = undefined;
29 var hasher = std.crypto.hash.Blake3.init(.{});33 var hasher = std.crypto.hash.Blake3.init(.{});
src/AstGen.zig+14-7
...@@ -12,9 +12,6 @@ const Allocator = std.mem.Allocator;...@@ -12,9 +12,6 @@ const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const ArrayListUnmanaged = std.ArrayListUnmanaged;13const ArrayListUnmanaged = std.ArrayListUnmanaged;
1414
15const Value = @import("value.zig").Value;
16const Type = @import("type.zig").Type;
17const TypedValue = @import("TypedValue.zig");
18const Zir = @import("Zir.zig");15const Zir = @import("Zir.zig");
19const Module = @import("Module.zig");16const Module = @import("Module.zig");
20const trace = @import("tracy.zig").trace;17const trace = @import("tracy.zig").trace;
...@@ -2648,6 +2645,10 @@ fn fnDecl(...@@ -2648,6 +2645,10 @@ fn fnDecl(
2648 const tree = &astgen.file.tree;2645 const tree = &astgen.file.tree;
2649 const token_tags = tree.tokens.items(.tag);2646 const token_tags = tree.tokens.items(.tag);
26502647
2648 // We insert this at the beginning so that its instruction index marks the
2649 // start of the top level declaration.
2650 const block_inst = try gz.addBlock(.block_inline, fn_proto.ast.proto_node);
2651
2651 var decl_gz: GenZir = .{2652 var decl_gz: GenZir = .{
2652 .force_comptime = true,2653 .force_comptime = true,
2653 .decl_node_index = fn_proto.ast.proto_node,2654 .decl_node_index = fn_proto.ast.proto_node,
...@@ -2843,7 +2844,8 @@ fn fnDecl(...@@ -2843,7 +2844,8 @@ fn fnDecl(
2843 };2844 };
2844 const fn_name_str_index = try decl_gz.identAsString(fn_name_token);2845 const fn_name_str_index = try decl_gz.identAsString(fn_name_token);
28452846
2846 const block_inst = try gz.addBlock(.block_inline, fn_proto.ast.proto_node);2847 // We add this at the end so that its instruction index marks the end range
2848 // of the top level declaration.
2847 _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);2849 _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
2848 try decl_gz.setBlockBody(block_inst);2850 try decl_gz.setBlockBody(block_inst);
28492851
...@@ -2875,6 +2877,12 @@ fn globalVarDecl(...@@ -2875,6 +2877,12 @@ fn globalVarDecl(
2875 const tree = &astgen.file.tree;2877 const tree = &astgen.file.tree;
2876 const token_tags = tree.tokens.items(.tag);2878 const token_tags = tree.tokens.items(.tag);
28772879
2880 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
2881 const tag: Zir.Inst.Tag = if (is_mutable) .block_inline_var else .block_inline;
2882 // We do this at the beginning so that the instruction index marks the range start
2883 // of the top level declaration.
2884 const block_inst = try gz.addBlock(tag, node);
2885
2878 var block_scope: GenZir = .{2886 var block_scope: GenZir = .{
2879 .parent = scope,2887 .parent = scope,
2880 .decl_node_index = node,2888 .decl_node_index = node,
...@@ -2900,7 +2908,6 @@ fn globalVarDecl(...@@ -2900,7 +2908,6 @@ fn globalVarDecl(
2900 };2908 };
2901 try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none);2909 try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none);
29022910
2903 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
2904 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {2911 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
2905 if (!is_mutable) {2912 if (!is_mutable) {
2906 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});2913 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
...@@ -2940,8 +2947,8 @@ fn globalVarDecl(...@@ -2940,8 +2947,8 @@ fn globalVarDecl(
2940 var_decl.ast.init_node,2947 var_decl.ast.init_node,
2941 );2948 );
29422949
2943 const tag: Zir.Inst.Tag = if (is_mutable) .block_inline_var else .block_inline;2950 // We do this at the end so that the instruction index marks the end
2944 const block_inst = try gz.addBlock(tag, node);2951 // range of a top level declaration.
2945 _ = try block_scope.addBreak(.break_inline, block_inst, init_inst);2952 _ = try block_scope.addBreak(.break_inline, block_inst, init_inst);
2946 try block_scope.setBlockBody(block_inst);2953 try block_scope.setBlockBody(block_inst);
2947 break :vi block_inst;2954 break :vi block_inst;
src/Compilation.zig+48-15
...@@ -180,6 +180,8 @@ const Job = union(enum) {...@@ -180,6 +180,8 @@ const Job = union(enum) {
180 /// The source file containing the Decl has been updated, and so the180 /// The source file containing the Decl has been updated, and so the
181 /// Decl may need its line number information updated in the debug info.181 /// Decl may need its line number information updated in the debug info.
182 update_line_number: *Module.Decl,182 update_line_number: *Module.Decl,
183 /// The main source file for the package needs to be analyzed.
184 analyze_pkg: *Package,
183185
184 /// one of the glibc static objects186 /// one of the glibc static objects
185 glibc_crt_file: glibc.CRTFile,187 glibc_crt_file: glibc.CRTFile,
...@@ -278,6 +280,7 @@ pub const MiscTask = enum {...@@ -278,6 +280,7 @@ pub const MiscTask = enum {
278 compiler_rt,280 compiler_rt,
279 libssp,281 libssp,
280 zig_libc,282 zig_libc,
283 analyze_pkg,
281};284};
282285
283pub const MiscError = struct {286pub const MiscError = struct {
...@@ -1155,6 +1158,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1155,6 +1158,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1155 .path = try options.global_cache_directory.join(arena, &[_][]const u8{zir_sub_dir}),1158 .path = try options.global_cache_directory.join(arena, &[_][]const u8{zir_sub_dir}),
1156 };1159 };
11571160
1161 const emit_h: ?*Module.GlobalEmitH = if (options.emit_h) |loc| eh: {
1162 const eh = try gpa.create(Module.GlobalEmitH);
1163 eh.* = .{ .loc = loc };
1164 break :eh eh;
1165 } else null;
1166 errdefer if (emit_h) |eh| gpa.destroy(eh);
1167
1158 // TODO when we implement serialization and deserialization of incremental1168 // TODO when we implement serialization and deserialization of incremental
1159 // compilation metadata, this is where we would load it. We have open a handle1169 // compilation metadata, this is where we would load it. We have open a handle
1160 // to the directory where the output either already is, or will be.1170 // to the directory where the output either already is, or will be.
...@@ -1170,7 +1180,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1170,7 +1180,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1170 .zig_cache_artifact_directory = zig_cache_artifact_directory,1180 .zig_cache_artifact_directory = zig_cache_artifact_directory,
1171 .global_zir_cache = global_zir_cache,1181 .global_zir_cache = global_zir_cache,
1172 .local_zir_cache = local_zir_cache,1182 .local_zir_cache = local_zir_cache,
1173 .emit_h = options.emit_h,1183 .emit_h = emit_h,
1174 .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),1184 .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
1175 };1185 };
1176 module.error_name_list.appendAssumeCapacity("(no error)");1186 module.error_name_list.appendAssumeCapacity("(no error)");
...@@ -1595,6 +1605,8 @@ pub fn update(self: *Compilation) !void {...@@ -1595,6 +1605,8 @@ pub fn update(self: *Compilation) !void {
1595 for (module.import_table.items()) |entry| {1605 for (module.import_table.items()) |entry| {
1596 self.astgen_work_queue.writeItemAssumeCapacity(entry.value);1606 self.astgen_work_queue.writeItemAssumeCapacity(entry.value);
1597 }1607 }
1608
1609 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
1598 }1610 }
1599 }1611 }
16001612
...@@ -1672,11 +1684,13 @@ pub fn totalErrorCount(self: *Compilation) usize {...@@ -1672,11 +1684,13 @@ pub fn totalErrorCount(self: *Compilation) usize {
1672 }1684 }
1673 total += 1;1685 total += 1;
1674 }1686 }
1675 for (module.emit_h_failed_decls.items()) |entry| {1687 if (module.emit_h) |emit_h| {
1676 if (entry.key.namespace.file_scope.status == .parse_failure) {1688 for (emit_h.failed_decls.items()) |entry| {
1677 continue;1689 if (entry.key.namespace.file_scope.status == .parse_failure) {
1690 continue;
1691 }
1692 total += 1;
1678 }1693 }
1679 total += 1;
1680 }1694 }
1681 }1695 }
16821696
...@@ -1743,13 +1757,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -1743,13 +1757,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
1743 }1757 }
1744 try AllErrors.add(module, &arena, &errors, entry.value.*);1758 try AllErrors.add(module, &arena, &errors, entry.value.*);
1745 }1759 }
1746 for (module.emit_h_failed_decls.items()) |entry| {1760 if (module.emit_h) |emit_h| {
1747 if (entry.key.namespace.file_scope.status == .parse_failure) {1761 for (emit_h.failed_decls.items()) |entry| {
1748 // Skip errors for Decls within files that had a parse failure.1762 if (entry.key.namespace.file_scope.status == .parse_failure) {
1749 // We'll try again once parsing succeeds.1763 // Skip errors for Decls within files that had a parse failure.
1750 continue;1764 // We'll try again once parsing succeeds.
1765 continue;
1766 }
1767 try AllErrors.add(module, &arena, &errors, entry.value.*);
1751 }1768 }
1752 try AllErrors.add(module, &arena, &errors, entry.value.*);
1753 }1769 }
1754 for (module.failed_exports.items()) |entry| {1770 for (module.failed_exports.items()) |entry| {
1755 try AllErrors.add(module, &arena, &errors, entry.value.*);1771 try AllErrors.add(module, &arena, &errors, entry.value.*);
...@@ -1942,10 +1958,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1942,10 +1958,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1942 if (build_options.omit_stage2)1958 if (build_options.omit_stage2)
1943 @panic("sadly stage2 is omitted from this build to save memory on the CI server");1959 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
1944 const module = self.bin_file.options.module.?;1960 const module = self.bin_file.options.module.?;
1945 const emit_loc = module.emit_h.?;1961 const emit_h = module.emit_h.?;
1962 _ = try emit_h.decl_table.getOrPut(module.gpa, decl);
1946 const tv = decl.typed_value.most_recent.typed_value;1963 const tv = decl.typed_value.most_recent.typed_value;
1947 const emit_h = decl.getEmitH(module);1964 const decl_emit_h = decl.getEmitH(module);
1948 const fwd_decl = &emit_h.fwd_decl;1965 const fwd_decl = &decl_emit_h.fwd_decl;
1949 fwd_decl.shrinkRetainingCapacity(0);1966 fwd_decl.shrinkRetainingCapacity(0);
19501967
1951 var dg: c_codegen.DeclGen = .{1968 var dg: c_codegen.DeclGen = .{
...@@ -1960,7 +1977,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1960,7 +1977,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
19601977
1961 c_codegen.genHeader(&dg) catch |err| switch (err) {1978 c_codegen.genHeader(&dg) catch |err| switch (err) {
1962 error.AnalysisFail => {1979 error.AnalysisFail => {
1963 try module.emit_h_failed_decls.put(module.gpa, decl, dg.error_msg.?);1980 try emit_h.failed_decls.put(module.gpa, decl, dg.error_msg.?);
1964 continue;1981 continue;
1965 },1982 },
1966 else => |e| return e,1983 else => |e| return e,
...@@ -1994,6 +2011,22 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1994,6 +2011,22 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1994 decl.analysis = .codegen_failure_retryable;2011 decl.analysis = .codegen_failure_retryable;
1995 };2012 };
1996 },2013 },
2014 .analyze_pkg => |pkg| {
2015 if (build_options.omit_stage2)
2016 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2017 const module = self.bin_file.options.module.?;
2018 module.semaPkg(pkg) catch |err| switch (err) {
2019 error.CurrentWorkingDirectoryUnlinked,
2020 error.Unexpected,
2021 => try self.setMiscFailure(
2022 .analyze_pkg,
2023 "unexpected problem analyzing package '{s}'",
2024 .{pkg.root_src_path},
2025 ),
2026 error.OutOfMemory => return error.OutOfMemory,
2027 error.AnalysisFail => continue,
2028 };
2029 },
1997 .glibc_crt_file => |crt_file| {2030 .glibc_crt_file => |crt_file| {
1998 glibc.buildCRTFile(self, crt_file) catch |err| {2031 glibc.buildCRTFile(self, crt_file) catch |err| {
1999 // TODO Surface more error details.2032 // TODO Surface more error details.
src/Module.zig+315-388
...@@ -54,8 +54,6 @@ symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},...@@ -54,8 +54,6 @@ symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
54/// is performing the export of another Decl.54/// is performing the export of another Decl.
55/// This table owns the Export memory.55/// This table owns the Export memory.
56export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},56export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
57/// Maps fully qualified namespaced names to the Decl struct for them.
58decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
59/// The set of all the files in the Module. We keep track of this in order to iterate57/// The set of all the files in the Module. We keep track of this in order to iterate
60/// over it and check which source files have been modified on the file system when58/// over it and check which source files have been modified on the file system when
61/// an update is requested, as well as to cache `@import` results.59/// an update is requested, as well as to cache `@import` results.
...@@ -68,10 +66,6 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},...@@ -68,10 +66,6 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
68/// Note that a Decl can succeed but the Fn it represents can fail. In this case,66/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
69/// a Decl can have a failed_decls entry but have analysis status of success.67/// a Decl can have a failed_decls entry but have analysis status of success.
70failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},68failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
71/// When emit_h is non-null, each Decl gets one more compile error slot for
72/// emit-h failing for that Decl. This table is also how we tell if a Decl has
73/// failed emit-h or succeeded.
74emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
75/// Keep track of one `@compileLog` callsite per owner Decl.69/// Keep track of one `@compileLog` callsite per owner Decl.
76compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{},70compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{},
77/// Using a map here for consistency with the other fields here.71/// Using a map here for consistency with the other fields here.
...@@ -113,12 +107,24 @@ stage1_flags: packed struct {...@@ -113,12 +107,24 @@ stage1_flags: packed struct {
113 reserved: u2 = 0,107 reserved: u2 = 0,
114} = .{},108} = .{},
115109
116emit_h: ?Compilation.EmitLoc,
117
118job_queued_update_builtin_zig: bool = true,110job_queued_update_builtin_zig: bool = true,
119111
120compile_log_text: ArrayListUnmanaged(u8) = .{},112compile_log_text: ArrayListUnmanaged(u8) = .{},
121113
114emit_h: ?*GlobalEmitH,
115
116/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
117pub const GlobalEmitH = struct {
118 /// Where to put the output.
119 loc: Compilation.EmitLoc,
120 /// When emit_h is non-null, each Decl gets one more compile error slot for
121 /// emit-h failing for that Decl. This table is also how we tell if a Decl has
122 /// failed emit-h or succeeded.
123 failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
124 /// Tracks all decls in order to iterate over them and emit .h code for them.
125 decl_table: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
126};
127
122pub const ErrorInt = u32;128pub const ErrorInt = u32;
123129
124pub const Export = struct {130pub const Export = struct {
...@@ -293,10 +299,6 @@ pub const Decl = struct {...@@ -293,10 +299,6 @@ pub const Decl = struct {
293 return tree.tokens.items(.start)[decl.srcToken()];299 return tree.tokens.items(.start)[decl.srcToken()];
294 }300 }
295301
296 pub fn fullyQualifiedNameHash(decl: Decl) Scope.NameHash {
297 return decl.namespace.fullyQualifiedNameHash(mem.spanZ(decl.name));
298 }
299
300 pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void {302 pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void {
301 const unqualified_name = mem.spanZ(decl.name);303 const unqualified_name = mem.spanZ(decl.name);
302 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);304 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);
...@@ -318,6 +320,11 @@ pub const Decl = struct {...@@ -318,6 +320,11 @@ pub const Decl = struct {
318 return (try decl.typedValue()).val;320 return (try decl.typedValue()).val;
319 }321 }
320322
323 pub fn isFunction(decl: *Decl) !bool {
324 const tv = try decl.typedValue();
325 return tv.ty.zigTypeTag() == .Fn;
326 }
327
321 pub fn dump(decl: *Decl) void {328 pub fn dump(decl: *Decl) void {
322 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);329 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);
323 std.debug.print("{s}:{d}:{d} name={s} status={s}", .{330 std.debug.print("{s}:{d}:{d} name={s} status={s}", .{
...@@ -611,14 +618,6 @@ pub const Scope = struct {...@@ -611,14 +618,6 @@ pub const Scope = struct {
611 }618 }
612 }619 }
613620
614 fn name_hash_hash(x: NameHash) u32 {
615 return @truncate(u32, @bitCast(u128, x));
616 }
617
618 fn name_hash_eql(a: NameHash, b: NameHash) bool {
619 return @bitCast(u128, a) == @bitCast(u128, b);
620 }
621
622 pub const Tag = enum {621 pub const Tag = enum {
623 /// .zig source code.622 /// .zig source code.
624 file,623 file,
...@@ -643,28 +642,32 @@ pub const Scope = struct {...@@ -643,28 +642,32 @@ pub const Scope = struct {
643642
644 parent: ?*Namespace,643 parent: ?*Namespace,
645 file_scope: *Scope.File,644 file_scope: *Scope.File,
646 parent_name_hash: NameHash,
647 /// Will be a struct, enum, union, or opaque.645 /// Will be a struct, enum, union, or opaque.
648 ty: Type,646 ty: Type,
649 /// Direct children of the namespace. Used during an update to detect647 /// Direct children of the namespace. Used during an update to detect
650 /// which decls have been added/removed from source.648 /// which decls have been added/removed from source.
651 decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},649 /// Declaration order is preserved via entry order.
652 usingnamespace_set: std.AutoHashMapUnmanaged(*Namespace, bool) = .{},650 /// Key memory references the string table of the containing `File` ZIR.
653651 /// TODO save memory with https://github.com/ziglang/zig/issues/8619.
654 pub fn deinit(ns: *Namespace, gpa: *Allocator) void {652 /// Does not contain anonymous decls.
653 decls: std.StringArrayHashMapUnmanaged(*Decl) = .{},
654 /// Names imported into the namespace via `usingnamespace`.
655 /// The key memory is owned by the ZIR of the `File` containing the `Namespace`.
656 usingnamespace_decls: std.StringArrayHashMapUnmanaged(*Namespace) = .{},
657
658 pub fn deinit(ns: *Namespace, mod: *Module) void {
659 const gpa = mod.gpa;
660
661 for (ns.decls.items()) |entry| {
662 entry.value.destroy(mod);
663 }
655 ns.decls.deinit(gpa);664 ns.decls.deinit(gpa);
656 ns.* = undefined;665 ns.* = undefined;
657 }666 }
658667
659 pub fn removeDecl(ns: *Namespace, child: *Decl) void {668 pub fn removeDecl(ns: *Namespace, child: *Decl) void {
660 _ = ns.decls.swapRemove(child);669 // Preserve declaration order.
661 }670 _ = ns.decls.orderedRemove(mem.spanZ(child.name));
662
663 /// Must generate unique bytes with no collisions with other decls.
664 /// The point of hashing here is only to limit the number of bytes of
665 /// the unique identifier to a fixed size (16 bytes).
666 pub fn fullyQualifiedNameHash(ns: Namespace, name: []const u8) NameHash {
667 return std.zig.hashName(ns.parent_name_hash, ".", name);
668 }671 }
669672
670 pub fn renderFullyQualifiedName(ns: Namespace, name: []const u8, writer: anytype) !void {673 pub fn renderFullyQualifiedName(ns: Namespace, name: []const u8, writer: anytype) !void {
...@@ -738,7 +741,9 @@ pub const Scope = struct {...@@ -738,7 +741,9 @@ pub const Scope = struct {
738 }741 }
739 }742 }
740743
741 pub fn deinit(file: *File, gpa: *Allocator) void {744 pub fn deinit(file: *File, mod: *Module) void {
745 const gpa = mod.gpa;
746 file.namespace.deinit(mod);
742 gpa.free(file.sub_file_path);747 gpa.free(file.sub_file_path);
743 file.unload(gpa);748 file.unload(gpa);
744 file.* = undefined;749 file.* = undefined;
...@@ -786,8 +791,9 @@ pub const Scope = struct {...@@ -786,8 +791,9 @@ pub const Scope = struct {
786 return &file.tree;791 return &file.tree;
787 }792 }
788793
789 pub fn destroy(file: *File, gpa: *Allocator) void {794 pub fn destroy(file: *File, mod: *Module) void {
790 file.deinit(gpa);795 const gpa = mod.gpa;
796 file.deinit(mod);
791 gpa.destroy(file);797 gpa.destroy(file);
792 }798 }
793799
...@@ -798,7 +804,7 @@ pub const Scope = struct {...@@ -798,7 +804,7 @@ pub const Scope = struct {
798 };804 };
799805
800 /// This is the context needed to semantically analyze ZIR instructions and806 /// This is the context needed to semantically analyze ZIR instructions and
801 /// produce TZIR instructions.807 /// produce AIR instructions.
802 /// This is a temporary structure stored on the stack; references to it are valid only808 /// This is a temporary structure stored on the stack; references to it are valid only
803 /// during semantic analysis of the block.809 /// during semantic analysis of the block.
804 pub const Block = struct {810 pub const Block = struct {
...@@ -818,7 +824,7 @@ pub const Scope = struct {...@@ -818,7 +824,7 @@ pub const Scope = struct {
818 is_comptime: bool,824 is_comptime: bool,
819825
820 /// This `Block` maps a block ZIR instruction to the corresponding826 /// This `Block` maps a block ZIR instruction to the corresponding
821 /// TZIR instruction for break instruction analysis.827 /// AIR instruction for break instruction analysis.
822 pub const Label = struct {828 pub const Label = struct {
823 zir_block: Zir.Inst.Index,829 zir_block: Zir.Inst.Index,
824 merges: Merges,830 merges: Merges,
...@@ -826,7 +832,7 @@ pub const Scope = struct {...@@ -826,7 +832,7 @@ pub const Scope = struct {
826832
827 /// This `Block` indicates that an inline function call is happening833 /// This `Block` indicates that an inline function call is happening
828 /// and return instructions should be analyzed as a break instruction834 /// and return instructions should be analyzed as a break instruction
829 /// to this TZIR block instruction.835 /// to this AIR block instruction.
830 /// It is shared among all the blocks in an inline or comptime called836 /// It is shared among all the blocks in an inline or comptime called
831 /// function.837 /// function.
832 pub const Inlining = struct {838 pub const Inlining = struct {
...@@ -2632,20 +2638,19 @@ pub fn deinit(mod: *Module) void {...@@ -2632,20 +2638,19 @@ pub fn deinit(mod: *Module) void {
26322638
2633 mod.deletion_set.deinit(gpa);2639 mod.deletion_set.deinit(gpa);
26342640
2635 for (mod.decl_table.items()) |entry| {
2636 entry.value.destroy(mod);
2637 }
2638 mod.decl_table.deinit(gpa);
2639
2640 for (mod.failed_decls.items()) |entry| {2641 for (mod.failed_decls.items()) |entry| {
2641 entry.value.destroy(gpa);2642 entry.value.destroy(gpa);
2642 }2643 }
2643 mod.failed_decls.deinit(gpa);2644 mod.failed_decls.deinit(gpa);
26442645
2645 for (mod.emit_h_failed_decls.items()) |entry| {2646 if (mod.emit_h) |emit_h| {
2646 entry.value.destroy(gpa);2647 for (emit_h.failed_decls.items()) |entry| {
2648 entry.value.destroy(gpa);
2649 }
2650 emit_h.failed_decls.deinit(gpa);
2651 emit_h.decl_table.deinit(gpa);
2652 gpa.destroy(emit_h);
2647 }2653 }
2648 mod.emit_h_failed_decls.deinit(gpa);
26492654
2650 for (mod.failed_files.items()) |entry| {2655 for (mod.failed_files.items()) |entry| {
2651 if (entry.value) |msg| msg.destroy(gpa);2656 if (entry.value) |msg| msg.destroy(gpa);
...@@ -2682,7 +2687,7 @@ pub fn deinit(mod: *Module) void {...@@ -2682,7 +2687,7 @@ pub fn deinit(mod: *Module) void {
26822687
2683 for (mod.import_table.items()) |entry| {2688 for (mod.import_table.items()) |entry| {
2684 gpa.free(entry.key);2689 gpa.free(entry.key);
2685 entry.value.destroy(gpa);2690 entry.value.destroy(mod);
2686 }2691 }
2687 mod.import_table.deinit(gpa);2692 mod.import_table.deinit(gpa);
2688}2693}
...@@ -2700,8 +2705,8 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;...@@ -2700,8 +2705,8 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
2700// We need a better language feature for initializing a union with2705// We need a better language feature for initializing a union with
2701// a runtime known tag.2706// a runtime known tag.
2702const Stage1DataLayout = extern struct {2707const Stage1DataLayout = extern struct {
2703 safety_tag: u8,
2704 data: [8]u8 align(8),2708 data: [8]u8 align(8),
2709 safety_tag: u8,
2705};2710};
2706comptime {2711comptime {
2707 if (data_has_safety_tag) {2712 if (data_has_safety_tag) {
...@@ -2783,12 +2788,15 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node...@@ -2783,12 +2788,15 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
2783 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});2788 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
2784 break :cached;2789 break :cached;
2785 }2790 }
2786 log.debug("AstGen cache hit: {s}", .{file.sub_file_path});2791 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
2792 file.sub_file_path, header.instructions_len,
2793 });
27872794
2788 var instructions: std.MultiArrayList(Zir.Inst) = .{};2795 var instructions: std.MultiArrayList(Zir.Inst) = .{};
2789 defer instructions.deinit(gpa);2796 defer instructions.deinit(gpa);
27902797
2791 try instructions.resize(gpa, header.instructions_len);2798 try instructions.setCapacity(gpa, header.instructions_len);
2799 instructions.len = header.instructions_len;
27922800
2793 var zir: Zir = .{2801 var zir: Zir = .{
2794 .instructions = instructions.toOwnedSlice(),2802 .instructions = instructions.toOwnedSlice(),
...@@ -3126,6 +3134,88 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -3126,6 +3134,88 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
3126 }3134 }
3127}3135}
31283136
3137pub fn semaPkg(mod: *Module, pkg: *Package) !void {
3138 const file = (try mod.importPkg(mod.root_pkg, pkg)).file;
3139 return mod.semaFile(file);
3140}
3141
3142pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
3143 const tracy = trace(@src());
3144 defer tracy.end();
3145
3146 assert(file.zir_loaded);
3147 assert(!file.zir.hasCompileErrors());
3148
3149 const gpa = mod.gpa;
3150 var decl_arena = std.heap.ArenaAllocator.init(gpa);
3151 defer decl_arena.deinit();
3152
3153 // We need a Decl to pass to Sema and collect dependencies. But ultimately we
3154 // want to pass them on to the Decl for the struct that represents the file.
3155 var tmp_namespace: Scope.Namespace = .{
3156 .parent = null,
3157 .file_scope = file,
3158 .ty = Type.initTag(.type),
3159 };
3160 var top_decl: Decl = .{
3161 .name = "",
3162 .namespace = &tmp_namespace,
3163 .generation = mod.generation,
3164 .src_node = 0, // the root AST node for the file
3165 .typed_value = .never_succeeded,
3166 .analysis = .in_progress,
3167 .deletion_flag = false,
3168 .is_pub = true,
3169 .link = undefined, // don't try to codegen this
3170 .fn_link = undefined, // not a function
3171 .contents_hash = undefined, // top-level struct has no contents hash
3172 };
3173 defer top_decl.dependencies.deinit(gpa);
3174
3175 var sema: Sema = .{
3176 .mod = mod,
3177 .gpa = gpa,
3178 .arena = &decl_arena.allocator,
3179 .code = file.zir,
3180 // TODO use a map because this array is too big
3181 .inst_map = try decl_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len),
3182 .owner_decl = &top_decl,
3183 .namespace = &tmp_namespace,
3184 .func = null,
3185 .owner_func = null,
3186 .param_inst_list = &.{},
3187 };
3188 var block_scope: Scope.Block = .{
3189 .parent = null,
3190 .sema = &sema,
3191 .src_decl = &top_decl,
3192 .instructions = .{},
3193 .inlining = null,
3194 .is_comptime = true,
3195 };
3196 defer block_scope.instructions.deinit(gpa);
3197
3198 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
3199 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
3200 const air_inst = try sema.zirStructDecl(&block_scope, main_struct_inst, .Auto);
3201 assert(air_inst.ty.zigTypeTag() == .Type);
3202 const val = air_inst.value().?;
3203 const struct_ty = try val.toType(&decl_arena.allocator);
3204 const struct_decl = struct_ty.getOwnerDecl();
3205
3206 file.namespace = struct_ty.getNamespace().?;
3207 file.namespace.parent = null;
3208
3209 // Transfer the dependencies to `owner_decl`.
3210 assert(top_decl.dependants.count() == 0);
3211 for (top_decl.dependencies.items()) |entry| {
3212 const dep = entry.key;
3213 dep.removeDependant(&top_decl);
3214 if (dep == struct_decl) continue;
3215 _ = try mod.declareDeclDependency(struct_decl, dep);
3216 }
3217}
3218
3129/// Returns `true` if the Decl type changed.3219/// Returns `true` if the Decl type changed.
3130/// Returns `true` if this is the first time analyzing the Decl.3220/// Returns `true` if this is the first time analyzing the Decl.
3131/// Returns `false` otherwise.3221/// Returns `false` otherwise.
...@@ -3268,31 +3358,32 @@ pub fn importFile(...@@ -3268,31 +3358,32 @@ pub fn importFile(
3268 };3358 };
3269}3359}
32703360
3271pub fn analyzeNamespace(3361pub fn scanNamespace(
3272 mod: *Module,3362 mod: *Module,
3273 namespace: *Scope.Namespace,3363 namespace: *Scope.Namespace,
3274 decls: []const ast.Node.Index,3364 extra_start: usize,
3275) InnerError!void {3365 decls_len: u32,
3366 parent_decl: *Decl,
3367) InnerError!usize {
3276 const tracy = trace(@src());3368 const tracy = trace(@src());
3277 defer tracy.end();3369 defer tracy.end();
32783370
3279 // We may be analyzing it for the first time, or this may be3371 const gpa = mod.gpa;
3280 // an incremental update. This code handles both cases.3372 const zir = namespace.file_scope.zir;
3281 assert(namespace.file_scope.tree_loaded); // Caller must ensure tree loaded.
3282 const tree: *const ast.Tree = &namespace.file_scope.tree;
3283 const node_tags = tree.nodes.items(.tag);
3284 const node_datas = tree.nodes.items(.data);
32853373
3286 try mod.comp.work_queue.ensureUnusedCapacity(decls.len);3374 try mod.comp.work_queue.ensureUnusedCapacity(decls_len);
3287 try namespace.decls.ensureCapacity(mod.gpa, decls.len);3375 try namespace.decls.ensureCapacity(gpa, decls_len);
32883376
3289 // Keep track of the decls that we expect to see in this namespace so that3377 // Keep track of the decls that we expect to see in this namespace so that
3290 // we know which ones have been deleted.3378 // we know which ones have been deleted.
3291 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);3379 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(gpa);
3292 defer deleted_decls.deinit();3380 defer deleted_decls.deinit();
3293 try deleted_decls.ensureCapacity(namespace.decls.items().len);3381 {
3294 for (namespace.decls.items()) |entry| {3382 const namespace_decls = namespace.decls.items();
3295 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});3383 try deleted_decls.ensureCapacity(namespace_decls.len);
3384 for (namespace_decls) |entry| {
3385 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
3386 }
3296 }3387 }
32973388
3298 // Keep track of decls that are invalidated from the update. Ultimately,3389 // Keep track of decls that are invalidated from the update. Ultimately,
...@@ -3300,177 +3391,61 @@ pub fn analyzeNamespace(...@@ -3300,177 +3391,61 @@ pub fn analyzeNamespace(
3300 // the outdated decls, but we cannot queue up the tasks until after3391 // the outdated decls, but we cannot queue up the tasks until after
3301 // we find out which ones have been deleted, otherwise there would be3392 // we find out which ones have been deleted, otherwise there would be
3302 // deleted Decl pointers in the work queue.3393 // deleted Decl pointers in the work queue.
3303 var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);3394 var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(gpa);
3304 defer outdated_decls.deinit();3395 defer outdated_decls.deinit();
33053396
3306 for (decls) |decl_node| switch (node_tags[decl_node]) {3397 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
3307 .fn_decl => {3398 var extra_index = extra_start + bit_bags_count;
3308 const fn_proto = node_datas[decl_node].lhs;3399 var bit_bag_index: usize = extra_start;
3309 const body = node_datas[decl_node].rhs;3400 var cur_bit_bag: u32 = undefined;
3310 switch (node_tags[fn_proto]) {3401 var decl_i: u32 = 0;
3311 .fn_proto_simple => {3402 while (decl_i < decls_len) : (decl_i += 1) {
3312 var params: [1]ast.Node.Index = undefined;3403 if (decl_i % 8 == 0) {
3313 try mod.semaContainerFn(3404 cur_bit_bag = zir.extra[bit_bag_index];
3314 namespace,3405 bit_bag_index += 1;
3315 &deleted_decls,3406 }
3316 &outdated_decls,3407 const is_pub = @truncate(u1, cur_bit_bag) != 0;
3317 decl_node,3408 cur_bit_bag >>= 1;
3318 tree.*,3409 const is_exported = @truncate(u1, cur_bit_bag) != 0;
3319 body,3410 cur_bit_bag >>= 1;
3320 tree.fnProtoSimple(&params, fn_proto),3411 const has_align = @truncate(u1, cur_bit_bag) != 0;
3321 );3412 cur_bit_bag >>= 1;
3322 },3413 const has_section = @truncate(u1, cur_bit_bag) != 0;
3323 .fn_proto_multi => try mod.semaContainerFn(3414 cur_bit_bag >>= 1;
3324 namespace,3415
3325 &deleted_decls,3416 const hash_u32s = zir.extra[extra_index..][0..4];
3326 &outdated_decls,3417 extra_index += 4;
3327 decl_node,3418 const name_idx = zir.extra[extra_index];
3328 tree.*,3419 extra_index += 1;
3329 body,3420 const decl_index = zir.extra[extra_index];
3330 tree.fnProtoMulti(fn_proto),3421 extra_index += 1;
3331 ),3422 const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
3332 .fn_proto_one => {3423 const inst = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
3333 var params: [1]ast.Node.Index = undefined;3424 extra_index += 1;
3334 try mod.semaContainerFn(3425 break :inst inst;
3335 namespace,3426 };
3336 &deleted_decls,3427 const section_inst: Zir.Inst.Ref = if (!has_section) .none else inst: {
3337 &outdated_decls,3428 const inst = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
3338 decl_node,3429 extra_index += 1;
3339 tree.*,3430 break :inst inst;
3340 body,3431 };
3341 tree.fnProtoOne(&params, fn_proto),3432 const decl_name: ?[]const u8 = if (name_idx == 0) null else zir.nullTerminatedString(name_idx);
3342 );3433 const contents_hash = @bitCast(std.zig.SrcHash, hash_u32s.*);
3343 },
3344 .fn_proto => try mod.semaContainerFn(
3345 namespace,
3346 &deleted_decls,
3347 &outdated_decls,
3348 decl_node,
3349 tree.*,
3350 body,
3351 tree.fnProto(fn_proto),
3352 ),
3353 else => unreachable,
3354 }
3355 },
3356 .fn_proto_simple => {
3357 var params: [1]ast.Node.Index = undefined;
3358 try mod.semaContainerFn(
3359 namespace,
3360 &deleted_decls,
3361 &outdated_decls,
3362 decl_node,
3363 tree.*,
3364 0,
3365 tree.fnProtoSimple(&params, decl_node),
3366 );
3367 },
3368 .fn_proto_multi => try mod.semaContainerFn(
3369 namespace,
3370 &deleted_decls,
3371 &outdated_decls,
3372 decl_node,
3373 tree.*,
3374 0,
3375 tree.fnProtoMulti(decl_node),
3376 ),
3377 .fn_proto_one => {
3378 var params: [1]ast.Node.Index = undefined;
3379 try mod.semaContainerFn(
3380 namespace,
3381 &deleted_decls,
3382 &outdated_decls,
3383 decl_node,
3384 tree.*,
3385 0,
3386 tree.fnProtoOne(&params, decl_node),
3387 );
3388 },
3389 .fn_proto => try mod.semaContainerFn(
3390 namespace,
3391 &deleted_decls,
3392 &outdated_decls,
3393 decl_node,
3394 tree.*,
3395 0,
3396 tree.fnProto(decl_node),
3397 ),
33983434
3399 .global_var_decl => try mod.semaContainerVar(3435 try mod.scanDecl(
3400 namespace,
3401 &deleted_decls,
3402 &outdated_decls,
3403 decl_node,
3404 tree.*,
3405 tree.globalVarDecl(decl_node),
3406 ),
3407 .local_var_decl => try mod.semaContainerVar(
3408 namespace,
3409 &deleted_decls,
3410 &outdated_decls,
3411 decl_node,
3412 tree.*,
3413 tree.localVarDecl(decl_node),
3414 ),
3415 .simple_var_decl => try mod.semaContainerVar(
3416 namespace,3436 namespace,
3417 &deleted_decls,3437 &deleted_decls,
3418 &outdated_decls,3438 &outdated_decls,
3419 decl_node,3439 contents_hash,
3420 tree.*,3440 decl_name,
3421 tree.simpleVarDecl(decl_node),3441 decl_index,
3422 ),3442 is_pub,
3423 .aligned_var_decl => try mod.semaContainerVar(3443 is_exported,
3424 namespace,3444 align_inst,
3425 &deleted_decls,3445 section_inst,
3426 &outdated_decls,3446 parent_decl,
3427 decl_node,3447 );
3428 tree.*,3448 }
3429 tree.alignedVarDecl(decl_node),
3430 ),
3431
3432 .@"comptime" => {
3433 const name_index = mod.getNextAnonNameIndex();
3434 const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
3435 defer mod.gpa.free(name);
3436
3437 const name_hash = namespace.fullyQualifiedNameHash(name);
3438 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3439
3440 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
3441 namespace.decls.putAssumeCapacity(new_decl, {});
3442 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
3443 },
3444
3445 // Container fields are handled in AstGen.
3446 .container_field_init,
3447 .container_field_align,
3448 .container_field,
3449 => continue,
3450
3451 .test_decl => {
3452 if (mod.comp.bin_file.options.is_test) {
3453 log.err("TODO: analyze test decl", .{});
3454 }
3455 },
3456 .@"usingnamespace" => {
3457 const name_index = mod.getNextAnonNameIndex();
3458 const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index});
3459 defer mod.gpa.free(name);
3460
3461 const name_hash = namespace.fullyQualifiedNameHash(name);
3462 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3463
3464 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
3465 namespace.decls.putAssumeCapacity(new_decl, {});
3466
3467 mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) {
3468 error.OutOfMemory => return error.OutOfMemory,
3469 error.AnalysisFail => continue,
3470 };
3471 },
3472 else => unreachable,
3473 };
3474 // Handle explicitly deleted decls from the source code. This is one of two3449 // Handle explicitly deleted decls from the source code. This is one of two
3475 // places that Decl deletions happen. The other is in `Compilation`, after3450 // places that Decl deletions happen. The other is in `Compilation`, after
3476 // `performAllTheWork`, where we iterate over `Module.deletion_set` and3451 // `performAllTheWork`, where we iterate over `Module.deletion_set` and
...@@ -3493,133 +3468,98 @@ pub fn analyzeNamespace(...@@ -3493,133 +3468,98 @@ pub fn analyzeNamespace(
3493 for (outdated_decls.items()) |entry| {3468 for (outdated_decls.items()) |entry| {
3494 try mod.markOutdatedDecl(entry.key);3469 try mod.markOutdatedDecl(entry.key);
3495 }3470 }
3471 return extra_index;
3496}3472}
34973473
3498fn semaContainerFn(3474fn scanDecl(
3499 mod: *Module,3475 mod: *Module,
3500 namespace: *Scope.Namespace,3476 namespace: *Scope.Namespace,
3501 deleted_decls: *std.AutoArrayHashMap(*Decl, void),3477 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
3502 outdated_decls: *std.AutoArrayHashMap(*Decl, void),3478 outdated_decls: *std.AutoArrayHashMap(*Decl, void),
3503 decl_node: ast.Node.Index,3479 contents_hash: std.zig.SrcHash,
3504 tree: ast.Tree,3480 decl_name: ?[]const u8,
3505 body_node: ast.Node.Index,3481 decl_index: Zir.Inst.Index,
3506 fn_proto: ast.full.FnProto,3482 is_pub: bool,
3507) !void {3483 is_exported: bool,
3484 align_inst: Zir.Inst.Ref,
3485 section_inst: Zir.Inst.Ref,
3486 parent_decl: *Decl,
3487) InnerError!void {
3508 const tracy = trace(@src());3488 const tracy = trace(@src());
3509 defer tracy.end();3489 defer tracy.end();
35103490
3511 // We will create a Decl for it regardless of analysis status.3491 const gpa = mod.gpa;
3512 const name_token = fn_proto.name_token orelse {3492 const zir = namespace.file_scope.zir;
3513 // This problem will go away with #1717.3493 const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node;
3514 @panic("TODO missing function name");3494 const decl_node = parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
3515 };3495
3516 const name = tree.tokenSlice(name_token); // TODO use identifierTokenString3496 // We create a Decl for it regardless of analysis status.
3517 const name_hash = namespace.fullyQualifiedNameHash(name);3497 // Decls that have names are keyed in the namespace by the name. Decls without
3518 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));3498 // names are keyed by their contents hash. This way we can detect if, for example,
3519 if (mod.decl_table.get(name_hash)) |decl| {3499 // a comptime decl gets moved around in the file.
3520 // Update the AST node of the decl; even if its contents are unchanged, it may3500 const decl_key = decl_name orelse &contents_hash;
3521 // have been re-ordered.3501 const gop = try namespace.decls.getOrPut(gpa, decl_key);
3522 const prev_src_node = decl.src_node;3502 if (!gop.found_existing) {
3523 decl.src_node = decl_node;3503 if (align_inst != .none) {
3524 if (deleted_decls.swapRemove(decl) == null) {3504 return mod.fail(&namespace.base, .{ .node_abs = decl_node }, "TODO: implement decls with align()", .{});
3525 decl.analysis = .sema_failure;
3526 const msg = try ErrorMsg.create(mod.gpa, .{
3527 .file_scope = namespace.file_scope,
3528 .parent_decl_node = 0,
3529 .lazy = .{ .token_abs = name_token },
3530 }, "redeclaration of '{s}'", .{decl.name});
3531 errdefer msg.destroy(mod.gpa);
3532 const other_src_loc: SrcLoc = .{
3533 .file_scope = namespace.file_scope,
3534 .parent_decl_node = 0,
3535 .lazy = .{ .node_abs = prev_src_node },
3536 };
3537 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3538 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
3539 } else {
3540 if (!srcHashEql(decl.contents_hash, contents_hash)) {
3541 try outdated_decls.put(decl, {});
3542 decl.contents_hash = contents_hash;
3543 } else switch (mod.comp.bin_file.tag) {
3544 .coff => {
3545 // TODO Implement for COFF
3546 },
3547 .elf => if (decl.fn_link.elf.len != 0) {
3548 // TODO Look into detecting when this would be unnecessary by storing enough state
3549 // in `Decl` to notice that the line number did not change.
3550 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3551 },
3552 .macho => if (decl.fn_link.macho.len != 0) {
3553 // TODO Look into detecting when this would be unnecessary by storing enough state
3554 // in `Decl` to notice that the line number did not change.
3555 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3556 },
3557 .c, .wasm, .spirv => {},
3558 }
3559 }3505 }
3560 } else {3506 if (section_inst != .none) {
3561 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);3507 return mod.fail(&namespace.base, .{ .node_abs = decl_node }, "TODO: implement decls with linksection()", .{});
3562 namespace.decls.putAssumeCapacity(new_decl, {});3508 }
3563 if (fn_proto.extern_export_token) |maybe_export_token| {3509 const new_decl = try mod.createNewDecl(namespace, decl_key, decl_node, contents_hash);
3564 const token_tags = tree.tokens.items(.tag);3510 // Update the key reference to the longer-lived memory.
3565 if (token_tags[maybe_export_token] == .keyword_export) {3511 gop.entry.key = &new_decl.contents_hash;
3566 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });3512 gop.entry.value = new_decl;
3567 }3513 // exported decls, comptime, test, and usingnamespace decls get analyzed.
3514 if (decl_name == null or is_exported) {
3515 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
3568 }3516 }
3569 new_decl.is_pub = fn_proto.visib_token != null;3517 new_decl.is_pub = is_pub;
3518 return;
3570 }3519 }
3571}3520 const decl = gop.entry.value;
35723521 // Update the AST node of the decl; even if its contents are unchanged, it may
3573fn semaContainerVar(3522 // have been re-ordered.
3574 mod: *Module,3523 const prev_src_node = decl.src_node;
3575 namespace: *Scope.Namespace,3524 decl.src_node = decl_node;
3576 deleted_decls: *std.AutoArrayHashMap(*Decl, void),3525 if (deleted_decls.swapRemove(decl) == null) {
3577 outdated_decls: *std.AutoArrayHashMap(*Decl, void),3526 if (true) {
3578 decl_node: ast.Node.Index,3527 @panic("TODO I think this code path is unreachable; should be caught by AstGen.");
3579 tree: ast.Tree,
3580 var_decl: ast.full.VarDecl,
3581) !void {
3582 const tracy = trace(@src());
3583 defer tracy.end();
3584
3585 const name_token = var_decl.ast.mut_token + 1;
3586 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
3587 const name_hash = namespace.fullyQualifiedNameHash(name);
3588 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3589 if (mod.decl_table.get(name_hash)) |decl| {
3590 // Update the AST Node index of the decl, even if its contents are unchanged, it may
3591 // have been re-ordered.
3592 const prev_src_node = decl.src_node;
3593 decl.src_node = decl_node;
3594 if (deleted_decls.swapRemove(decl) == null) {
3595 decl.analysis = .sema_failure;
3596 const msg = try ErrorMsg.create(mod.gpa, .{
3597 .file_scope = namespace.file_scope,
3598 .parent_decl_node = 0,
3599 .lazy = .{ .token_abs = name_token },
3600 }, "redeclaration of '{s}'", .{decl.name});
3601 errdefer msg.destroy(mod.gpa);
3602 const other_src_loc: SrcLoc = .{
3603 .file_scope = decl.namespace.file_scope,
3604 .parent_decl_node = 0,
3605 .lazy = .{ .node_abs = prev_src_node },
3606 };
3607 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3608 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
3609 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
3610 try outdated_decls.put(decl, {});
3611 decl.contents_hash = contents_hash;
3612 }3528 }
3529 decl.analysis = .sema_failure;
3530 const msg = try ErrorMsg.create(gpa, .{
3531 .file_scope = namespace.file_scope,
3532 .parent_decl_node = 0,
3533 .lazy = .{ .token_abs = name_token },
3534 }, "redeclaration of '{s}'", .{decl.name});
3535 errdefer msg.destroy(gpa);
3536 const other_src_loc: SrcLoc = .{
3537 .file_scope = namespace.file_scope,
3538 .parent_decl_node = 0,
3539 .lazy = .{ .node_abs = prev_src_node },
3540 };
3541 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3542 try mod.failed_decls.putNoClobber(gpa, decl, msg);
3613 } else {3543 } else {
3614 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);3544 if (!std.zig.srcHashEql(decl.contents_hash, contents_hash)) {
3615 namespace.decls.putAssumeCapacity(new_decl, {});3545 try outdated_decls.put(decl, {});
3616 if (var_decl.extern_export_token) |maybe_export_token| {3546 decl.contents_hash = contents_hash;
3617 const token_tags = tree.tokens.items(.tag);3547 } else if (try decl.isFunction()) switch (mod.comp.bin_file.tag) {
3618 if (token_tags[maybe_export_token] == .keyword_export) {3548 .coff => {
3619 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });3549 // TODO Implement for COFF
3620 }3550 },
3621 }3551 .elf => if (decl.fn_link.elf.len != 0) {
3622 new_decl.is_pub = var_decl.visib_token != null;3552 // TODO Look into detecting when this would be unnecessary by storing enough state
3553 // in `Decl` to notice that the line number did not change.
3554 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3555 },
3556 .macho => if (decl.fn_link.macho.len != 0) {
3557 // TODO Look into detecting when this would be unnecessary by storing enough state
3558 // in `Decl` to notice that the line number did not change.
3559 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3560 },
3561 .c, .wasm, .spirv => {},
3562 };
3623 }3563 }
3624}3564}
36253565
...@@ -3644,8 +3584,6 @@ pub fn deleteDecl(...@@ -3644,8 +3584,6 @@ pub fn deleteDecl(
3644 // not be present in the set, and this does nothing.3584 // not be present in the set, and this does nothing.
3645 decl.namespace.removeDecl(decl);3585 decl.namespace.removeDecl(decl);
36463586
3647 const name_hash = decl.fullyQualifiedNameHash();
3648 mod.decl_table.removeAssertDiscard(name_hash);
3649 // Remove itself from its dependencies, because we are about to destroy the decl pointer.3587 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
3650 for (decl.dependencies.items()) |entry| {3588 for (decl.dependencies.items()) |entry| {
3651 const dep = entry.key;3589 const dep = entry.key;
...@@ -3675,8 +3613,11 @@ pub fn deleteDecl(...@@ -3675,8 +3613,11 @@ pub fn deleteDecl(
3675 if (mod.failed_decls.swapRemove(decl)) |entry| {3613 if (mod.failed_decls.swapRemove(decl)) |entry| {
3676 entry.value.destroy(mod.gpa);3614 entry.value.destroy(mod.gpa);
3677 }3615 }
3678 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {3616 if (mod.emit_h) |emit_h| {
3679 entry.value.destroy(mod.gpa);3617 if (emit_h.failed_decls.swapRemove(decl)) |entry| {
3618 entry.value.destroy(mod.gpa);
3619 }
3620 emit_h.decl_table.removeAssertDiscard(decl);
3680 }3621 }
3681 _ = mod.compile_log_decls.swapRemove(decl);3622 _ = mod.compile_log_decls.swapRemove(decl);
3682 mod.deleteDeclExports(decl);3623 mod.deleteDeclExports(decl);
...@@ -3776,7 +3717,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {...@@ -3776,7 +3717,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
3776 };3717 };
3777 defer inner_block.instructions.deinit(mod.gpa);3718 defer inner_block.instructions.deinit(mod.gpa);
37783719
3779 // TZIR currently requires the arg parameters to be the first N instructions3720 // AIR currently requires the arg parameters to be the first N instructions
3780 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);3721 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);
37813722
3782 func.state = .in_progress;3723 func.state = .in_progress;
...@@ -3796,8 +3737,10 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {...@@ -3796,8 +3737,10 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
3796 if (mod.failed_decls.swapRemove(decl)) |entry| {3737 if (mod.failed_decls.swapRemove(decl)) |entry| {
3797 entry.value.destroy(mod.gpa);3738 entry.value.destroy(mod.gpa);
3798 }3739 }
3799 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {3740 if (mod.emit_h) |emit_h| {
3800 entry.value.destroy(mod.gpa);3741 if (emit_h.failed_decls.swapRemove(decl)) |entry| {
3742 entry.value.destroy(mod.gpa);
3743 }
3801 }3744 }
3802 _ = mod.compile_log_decls.swapRemove(decl);3745 _ = mod.compile_log_decls.swapRemove(decl);
3803 decl.analysis = .outdated;3746 decl.analysis = .outdated;
...@@ -3854,18 +3797,11 @@ fn createNewDecl(...@@ -3854,18 +3797,11 @@ fn createNewDecl(
3854 namespace: *Scope.Namespace,3797 namespace: *Scope.Namespace,
3855 decl_name: []const u8,3798 decl_name: []const u8,
3856 src_node: ast.Node.Index,3799 src_node: ast.Node.Index,
3857 name_hash: Scope.NameHash,
3858 contents_hash: std.zig.SrcHash,3800 contents_hash: std.zig.SrcHash,
3859) !*Decl {3801) !*Decl {
3860 try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1);
3861 const new_decl = try mod.allocateNewDecl(namespace, src_node, contents_hash);3802 const new_decl = try mod.allocateNewDecl(namespace, src_node, contents_hash);
3862 errdefer mod.gpa.destroy(new_decl);3803 errdefer mod.gpa.destroy(new_decl);
3863 new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name);3804 new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name);
3864 log.debug("insert Decl {s} with hash {}", .{
3865 new_decl.name,
3866 std.fmt.fmtSliceHexLower(&name_hash),
3867 });
3868 mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
3869 return new_decl;3805 return new_decl;
3870}3806}
38713807
...@@ -4074,9 +4010,8 @@ pub fn createAnonymousDecl(...@@ -4074,9 +4010,8 @@ pub fn createAnonymousDecl(
4074 const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });4010 const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
4075 defer mod.gpa.free(name);4011 defer mod.gpa.free(name);
4076 const namespace = scope_decl.namespace;4012 const namespace = scope_decl.namespace;
4077 const name_hash = namespace.fullyQualifiedNameHash(name);
4078 const src_hash: std.zig.SrcHash = undefined;4013 const src_hash: std.zig.SrcHash = undefined;
4079 const new_decl = try mod.createNewDecl(namespace, name, scope_decl.src_node, name_hash, src_hash);4014 const new_decl = try mod.createNewDecl(namespace, name, scope_decl.src_node, src_hash);
4080 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);4015 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
40814016
4082 decl_arena_state.* = decl_arena.state;4017 decl_arena_state.* = decl_arena.state;
...@@ -4125,30 +4060,26 @@ pub fn lookupInNamespace(...@@ -4125,30 +4060,26 @@ pub fn lookupInNamespace(
4125 ident_name: []const u8,4060 ident_name: []const u8,
4126 only_pub_usingnamespaces: bool,4061 only_pub_usingnamespaces: bool,
4127) ?*Decl {4062) ?*Decl {
4128 const name_hash = namespace.fullyQualifiedNameHash(ident_name);4063 @panic("TODO lookupInNamespace");
4129 log.debug("lookup Decl {s} with hash {}", .{4064 //// TODO handle decl collision with usingnamespace
4130 ident_name,4065 //// TODO the decl doing the looking up needs to create a decl dependency
4131 std.fmt.fmtSliceHexLower(&name_hash),4066 //// on each usingnamespace decl here.
4132 });4067 //if (mod.decl_table.get(name_hash)) |decl| {
4133 // TODO handle decl collision with usingnamespace4068 // return decl;
4134 // TODO the decl doing the looking up needs to create a decl dependency4069 //}
4135 // on each usingnamespace decl here.4070 //{
4136 if (mod.decl_table.get(name_hash)) |decl| {4071 // var it = namespace.usingnamespace_set.iterator();
4137 return decl;4072 // while (it.next()) |entry| {
4138 }4073 // const other_ns = entry.key;
4139 {4074 // const other_is_pub = entry.value;
4140 var it = namespace.usingnamespace_set.iterator();4075 // if (only_pub_usingnamespaces and !other_is_pub) continue;
4141 while (it.next()) |entry| {4076 // // TODO handle cycles
4142 const other_ns = entry.key;4077 // if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| {
4143 const other_is_pub = entry.value;4078 // return decl;
4144 if (only_pub_usingnamespaces and !other_is_pub) continue;4079 // }
4145 // TODO handle cycles4080 // }
4146 if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| {4081 //}
4147 return decl;4082 //return null;
4148 }
4149 }
4150 }
4151 return null;
4152}4083}
41534084
4154pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type {4085pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type {
...@@ -4274,10 +4205,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In...@@ -4274,10 +4205,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In
4274 return error.AnalysisFail;4205 return error.AnalysisFail;
4275}4206}
42764207
4277fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
4278 return @bitCast(u128, a) == @bitCast(u128, b);
4279}
4280
4281pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {4208pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4282 // TODO is this a performance issue? maybe we should try the operation without4209 // TODO is this a performance issue? maybe we should try the operation without
4283 // resorting to BigInt first.4210 // resorting to BigInt first.
src/Sema.zig+21-57
...@@ -655,7 +655,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In...@@ -655,7 +655,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
655 return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{});655 return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{});
656}656}
657657
658fn zirStructDecl(658pub fn zirStructDecl(
659 sema: *Sema,659 sema: *Sema,
660 block: *Scope.Block,660 block: *Scope.Block,
661 inst: Zir.Inst.Index,661 inst: Zir.Inst.Index,
...@@ -668,8 +668,8 @@ fn zirStructDecl(...@@ -668,8 +668,8 @@ fn zirStructDecl(
668 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;668 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
669 const src = inst_data.src();669 const src = inst_data.src();
670 const extra = sema.code.extraData(Zir.Inst.StructDecl, inst_data.payload_index);670 const extra = sema.code.extraData(Zir.Inst.StructDecl, inst_data.payload_index);
671 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
672 const fields_len = extra.data.fields_len;671 const fields_len = extra.data.fields_len;
672 const decls_len = extra.data.decls_len;
673673
674 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);674 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
675675
...@@ -686,37 +686,19 @@ fn zirStructDecl(...@@ -686,37 +686,19 @@ fn zirStructDecl(
686 .node_offset = inst_data.src_node,686 .node_offset = inst_data.src_node,
687 .namespace = .{687 .namespace = .{
688 .parent = sema.owner_decl.namespace,688 .parent = sema.owner_decl.namespace,
689 .parent_name_hash = new_decl.fullyQualifiedNameHash(),
690 .ty = struct_ty,689 .ty = struct_ty,
691 .file_scope = block.getFileScope(),690 .file_scope = block.getFileScope(),
692 },691 },
693 };692 };
694693
695 {694 var extra_index: usize = try sema.mod.scanNamespace(
696 const ast = std.zig.ast;695 &struct_obj.namespace,
697 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);696 extra.end,
698 const tree: *const ast.Tree = &struct_obj.namespace.file_scope.tree;697 decls_len,
699 const node_tags = tree.nodes.items(.tag);698 new_decl,
700 var buf: [2]ast.Node.Index = undefined;699 );
701 const members: []const ast.Node.Index = switch (node_tags[node]) {
702 .container_decl,
703 .container_decl_trailing,
704 => tree.containerDecl(node).ast.members,
705
706 .container_decl_two,
707 .container_decl_two_trailing,
708 => tree.containerDeclTwo(&buf, node).ast.members,
709
710 .container_decl_arg,
711 .container_decl_arg_trailing,
712 => tree.containerDeclArg(node).ast.members,
713
714 .root => tree.rootDecls(),
715 else => unreachable,
716 };
717 try sema.mod.analyzeNamespace(&struct_obj.namespace, members);
718 }
719700
701 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
720 if (fields_len == 0) {702 if (fields_len == 0) {
721 assert(body.len == 0);703 assert(body.len == 0);
722 return sema.analyzeDeclVal(block, src, new_decl);704 return sema.analyzeDeclVal(block, src, new_decl);
...@@ -760,8 +742,8 @@ fn zirStructDecl(...@@ -760,8 +742,8 @@ fn zirStructDecl(
760 sema.branch_quota = struct_sema.branch_quota;742 sema.branch_quota = struct_sema.branch_quota;
761 }743 }
762 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;744 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;
763 const body_end = extra.end + body.len;745 const body_end = extra_index + body.len;
764 var extra_index: usize = body_end + bit_bags_count;746 extra_index += bit_bags_count;
765 var bit_bag_index: usize = body_end;747 var bit_bag_index: usize = body_end;
766 var cur_bit_bag: u32 = undefined;748 var cur_bit_bag: u32 = undefined;
767 var field_i: u32 = 0;749 var field_i: u32 = 0;
...@@ -829,8 +811,8 @@ fn zirEnumDecl(...@@ -829,8 +811,8 @@ fn zirEnumDecl(
829 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;811 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
830 const src = inst_data.src();812 const src = inst_data.src();
831 const extra = sema.code.extraData(Zir.Inst.EnumDecl, inst_data.payload_index);813 const extra = sema.code.extraData(Zir.Inst.EnumDecl, inst_data.payload_index);
832 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
833 const fields_len = extra.data.fields_len;814 const fields_len = extra.data.fields_len;
815 const decls_len = extra.data.decls_len;
834816
835 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);817 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
836818
...@@ -865,44 +847,27 @@ fn zirEnumDecl(...@@ -865,44 +847,27 @@ fn zirEnumDecl(
865 .node_offset = inst_data.src_node,847 .node_offset = inst_data.src_node,
866 .namespace = .{848 .namespace = .{
867 .parent = sema.owner_decl.namespace,849 .parent = sema.owner_decl.namespace,
868 .parent_name_hash = new_decl.fullyQualifiedNameHash(),
869 .ty = enum_ty,850 .ty = enum_ty,
870 .file_scope = block.getFileScope(),851 .file_scope = block.getFileScope(),
871 },852 },
872 };853 };
873854
874 {855 var extra_index: usize = try sema.mod.scanNamespace(
875 const ast = std.zig.ast;856 &enum_obj.namespace,
876 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);857 extra.end,
877 const tree: *const ast.Tree = &enum_obj.namespace.file_scope.tree;858 decls_len,
878 const node_tags = tree.nodes.items(.tag);859 new_decl,
879 var buf: [2]ast.Node.Index = undefined;860 );
880 const members: []const ast.Node.Index = switch (node_tags[node]) {
881 .container_decl,
882 .container_decl_trailing,
883 => tree.containerDecl(node).ast.members,
884
885 .container_decl_two,
886 .container_decl_two_trailing,
887 => tree.containerDeclTwo(&buf, node).ast.members,
888
889 .container_decl_arg,
890 .container_decl_arg_trailing,
891 => tree.containerDeclArg(node).ast.members,
892
893 .root => tree.rootDecls(),
894 else => unreachable,
895 };
896 try sema.mod.analyzeNamespace(&enum_obj.namespace, members);
897 }
898861
862 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
899 if (fields_len == 0) {863 if (fields_len == 0) {
900 assert(body.len == 0);864 assert(body.len == 0);
901 return sema.analyzeDeclVal(block, src, new_decl);865 return sema.analyzeDeclVal(block, src, new_decl);
902 }866 }
903867
904 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;868 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
905 const body_end = extra.end + body.len;869 const body_end = extra_index + body.len;
870 extra_index += bit_bags_count;
906871
907 try enum_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);872 try enum_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);
908 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {873 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
...@@ -947,7 +912,6 @@ fn zirEnumDecl(...@@ -947,7 +912,6 @@ fn zirEnumDecl(
947 sema.branch_count = enum_sema.branch_count;912 sema.branch_count = enum_sema.branch_count;
948 sema.branch_quota = enum_sema.branch_quota;913 sema.branch_quota = enum_sema.branch_quota;
949 }914 }
950 var extra_index: usize = body_end + bit_bags_count;
951 var bit_bag_index: usize = body_end;915 var bit_bag_index: usize = body_end;
952 var cur_bit_bag: u32 = undefined;916 var cur_bit_bag: u32 = undefined;
953 var field_i: u32 = 0;917 var field_i: u32 = 0;
src/link/C.zig+23-15
...@@ -15,6 +15,10 @@ pub const base_tag: link.File.Tag = .c;...@@ -15,6 +15,10 @@ pub const base_tag: link.File.Tag = .c;
15pub const zig_h = @embedFile("C/zig.h");15pub const zig_h = @embedFile("C/zig.h");
1616
17base: link.File,17base: link.File,
18/// This linker backend does not try to incrementally link output C source code.
19/// Instead, it tracks all declarations in this table, and iterates over it
20/// in the flush function, stitching pre-rendered pieces of C code together.
21decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
1822
19/// Per-declaration data. For functions this is the body, and23/// Per-declaration data. For functions this is the body, and
20/// the forward declaration is stored in the FnBlock.24/// the forward declaration is stored in the FnBlock.
...@@ -66,10 +70,10 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -66,10 +70,10 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
66}70}
6771
68pub fn deinit(self: *C) void {72pub fn deinit(self: *C) void {
69 const module = self.base.options.module orelse return;73 for (self.decl_table.items()) |entry| {
70 for (module.decl_table.items()) |entry| {74 self.freeDecl(entry.key);
71 self.freeDecl(entry.value);
72 }75 }
76 self.decl_table.deinit(self.base.allocator);
73}77}
7478
75pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}79pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
...@@ -88,6 +92,9 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -88,6 +92,9 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
88 const tracy = trace(@src());92 const tracy = trace(@src());
89 defer tracy.end();93 defer tracy.end();
9094
95 // Keep track of all decls so we can iterate over them on flush().
96 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
97
91 const fwd_decl = &decl.fn_link.c.fwd_decl;98 const fwd_decl = &decl.fn_link.c.fwd_decl;
92 const typedefs = &decl.fn_link.c.typedefs;99 const typedefs = &decl.fn_link.c.typedefs;
93 const code = &decl.link.c.code;100 const code = &decl.link.c.code;
...@@ -168,7 +175,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -168,7 +175,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
168 defer all_buffers.deinit();175 defer all_buffers.deinit();
169176
170 // This is at least enough until we get to the function bodies without error handling.177 // This is at least enough until we get to the function bodies without error handling.
171 try all_buffers.ensureCapacity(module.decl_table.count() + 2);178 try all_buffers.ensureCapacity(self.decl_table.count() + 2);
172179
173 var file_size: u64 = zig_h.len;180 var file_size: u64 = zig_h.len;
174 all_buffers.appendAssumeCapacity(.{181 all_buffers.appendAssumeCapacity(.{
...@@ -197,8 +204,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -197,8 +204,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
197 // Typedefs, forward decls and non-functions first.204 // Typedefs, forward decls and non-functions first.
198 // TODO: performance investigation: would keeping a list of Decls that we should205 // TODO: performance investigation: would keeping a list of Decls that we should
199 // generate, rather than querying here, be faster?206 // generate, rather than querying here, be faster?
200 for (module.decl_table.items()) |kv| {207 for (self.decl_table.items()) |kv| {
201 const decl = kv.value;208 const decl = kv.key;
202 switch (decl.typed_value) {209 switch (decl.typed_value) {
203 .most_recent => |tvm| {210 .most_recent => |tvm| {
204 const buf = buf: {211 const buf = buf: {
...@@ -237,8 +244,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -237,8 +244,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
237244
238 // Now the function bodies.245 // Now the function bodies.
239 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);246 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
240 for (module.decl_table.items()) |kv| {247 for (self.decl_table.items()) |kv| {
241 const decl = kv.value;248 const decl = kv.key;
242 switch (decl.typed_value) {249 switch (decl.typed_value) {
243 .most_recent => |tvm| {250 .most_recent => |tvm| {
244 if (tvm.typed_value.val.castTag(.function)) |_| {251 if (tvm.typed_value.val.castTag(.function)) |_| {
...@@ -263,13 +270,13 @@ pub fn flushEmitH(module: *Module) !void {...@@ -263,13 +270,13 @@ pub fn flushEmitH(module: *Module) !void {
263 const tracy = trace(@src());270 const tracy = trace(@src());
264 defer tracy.end();271 defer tracy.end();
265272
266 const emit_h_loc = module.emit_h orelse return;273 const emit_h = module.emit_h orelse return;
267274
268 // We collect a list of buffers to write, and write them all at once with pwritev 😎275 // We collect a list of buffers to write, and write them all at once with pwritev 😎
269 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);276 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);
270 defer all_buffers.deinit();277 defer all_buffers.deinit();
271278
272 try all_buffers.ensureCapacity(module.decl_table.count() + 1);279 try all_buffers.ensureCapacity(emit_h.decl_table.count() + 1);
273280
274 var file_size: u64 = zig_h.len;281 var file_size: u64 = zig_h.len;
275 all_buffers.appendAssumeCapacity(.{282 all_buffers.appendAssumeCapacity(.{
...@@ -277,9 +284,10 @@ pub fn flushEmitH(module: *Module) !void {...@@ -277,9 +284,10 @@ pub fn flushEmitH(module: *Module) !void {
277 .iov_len = zig_h.len,284 .iov_len = zig_h.len,
278 });285 });
279286
280 for (module.decl_table.items()) |kv| {287 for (emit_h.decl_table.items()) |kv| {
281 const emit_h = kv.value.getEmitH(module);288 const decl = kv.key;
282 const buf = emit_h.fwd_decl.items;289 const decl_emit_h = decl.getEmitH(module);
290 const buf = decl_emit_h.fwd_decl.items;
283 all_buffers.appendAssumeCapacity(.{291 all_buffers.appendAssumeCapacity(.{
284 .iov_base = buf.ptr,292 .iov_base = buf.ptr,
285 .iov_len = buf.len,293 .iov_len = buf.len,
...@@ -287,8 +295,8 @@ pub fn flushEmitH(module: *Module) !void {...@@ -287,8 +295,8 @@ pub fn flushEmitH(module: *Module) !void {
287 file_size += buf.len;295 file_size += buf.len;
288 }296 }
289297
290 const directory = emit_h_loc.directory orelse module.comp.local_cache_directory;298 const directory = emit_h.loc.directory orelse module.comp.local_cache_directory;
291 const file = try directory.handle.createFile(emit_h_loc.basename, .{299 const file = try directory.handle.createFile(emit_h.loc.basename, .{
292 // We set the end position explicitly below; by not truncating the file, we possibly300 // We set the end position explicitly below; by not truncating the file, we possibly
293 // make it easier on the file system by doing 1 reallocation instead of two.301 // make it easier on the file system by doing 1 reallocation instead of two.
294 .truncate = false,302 .truncate = false,
src/link/SpirV.zig+13-4
...@@ -37,9 +37,14 @@ pub const FnData = struct {...@@ -37,9 +37,14 @@ pub const FnData = struct {
3737
38base: link.File,38base: link.File,
3939
40// TODO: Does this file need to support multiple independent modules?40/// TODO: Does this file need to support multiple independent modules?
41spirv_module: codegen.SPIRVModule,41spirv_module: codegen.SPIRVModule,
4242
43/// This linker backend does not try to incrementally link output SPIR-V code.
44/// Instead, it tracks all declarations in this table, and iterates over it
45/// in the flush function.
46decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
47
43pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {48pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {
44 const spirv = try gpa.create(SpirV);49 const spirv = try gpa.create(SpirV);
45 spirv.* = .{50 spirv.* = .{
...@@ -88,6 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -88,6 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
88}93}
8994
90pub fn deinit(self: *SpirV) void {95pub fn deinit(self: *SpirV) void {
96 self.decl_table.deinit(self.base.allocator);
91 self.spirv_module.deinit();97 self.spirv_module.deinit();
92}98}
9399
...@@ -95,6 +101,9 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {...@@ -95,6 +101,9 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
95 const tracy = trace(@src());101 const tracy = trace(@src());
96 defer tracy.end();102 defer tracy.end();
97103
104 // Keep track of all decls so we can iterate over them on flush().
105 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
106
98 const fn_data = &decl.fn_link.spirv;107 const fn_data = &decl.fn_link.spirv;
99 if (fn_data.id == null) {108 if (fn_data.id == null) {
100 fn_data.id = self.spirv_module.allocId();109 fn_data.id = self.spirv_module.allocId();
...@@ -164,12 +173,12 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -164,12 +173,12 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
164 defer all_buffers.deinit();173 defer all_buffers.deinit();
165174
166 // Pre-allocate enough for the binary info + all functions175 // Pre-allocate enough for the binary info + all functions
167 try all_buffers.ensureCapacity(module.decl_table.count() + 1);176 try all_buffers.ensureCapacity(self.decl_table.count() + 1);
168177
169 all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items));178 all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items));
170179
171 for (module.decl_table.items()) |entry| {180 for (self.decl_table.items()) |entry| {
172 const decl = entry.value;181 const decl = entry.key;
173 switch (decl.typed_value) {182 switch (decl.typed_value) {
174 .most_recent => |tvm| {183 .most_recent => |tvm| {
175 const fn_data = &decl.fn_link.spirv;184 const fn_data = &decl.fn_link.spirv;