authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-29 19:30:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-29 19:30:37-07:00
log040c6eaaa03bbcfcdeadbe835c1c2f209e9f401e
treea78cb4081572b8e148f983642dedf7d0d1d9bda2
parenta5c6e51f03ab164e64b1a1d8370071dd1e670458

stage2: garbage collect unused anon decls

After this change, the frontend and backend cooperate to keep track of which Decls are actually emitted into the machine code. When any backend sees a `decl_ref` Value, it must mark the corresponding Decl `alive` field to true. This prevents unused comptime data from spilling into the output object files. For example, if you do an `inline for` loop, previously, any intermediate value calculations would have gone into the object file. Now they are garbage collected immediately after the owner Decl has its machine code generated. In the frontend, when it is time to send a Decl to the linker, if it has not been marked "alive" then it is deleted instead. Additional improvements: * Resolve type ABI layouts after successful semantic analysis of a Decl. This is needed so that the backend has access to struct fields. * Sema: fix incorrect logic in resolveMaybeUndefVal. It should return "not comptime known" instead of a compile error for global variables. * `Value.pointerDeref` now returns `null` in the case that the pointer deref cannot happen at compile-time. This is true for global variables, for example. Another example is if a comptime known pointer has a hard coded address value. * Binary arithmetic sets the requireRuntimeBlock source location to the lhs_src or rhs_src as appropriate instead of on the operator node. * Fix LLVM codegen for slice_elem_val which had the wrong logic for when the operand was not a pointer. As noted in the comment in the implementation of deleteUnusedDecl, a future improvement will be to rework the frontend/linker interface to remove the frontend's responsibility of calling allocateDeclIndexes. I discovered some issues with the plan9 linker backend that are related to this, and worked around them for now.

10 files changed, 234 insertions(+), 139 deletions(-)

src/Compilation.zig+9-1
......@@ -2061,11 +2061,19 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20612061 .complete, .codegen_failure_retryable => {
20622062 if (build_options.omit_stage2)
20632063 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2064
20642065 const module = self.bin_file.options.module.?;
20652066 assert(decl.has_tv);
20662067 assert(decl.ty.hasCodeGenBits());
20672068
2068 try module.linkerUpdateDecl(decl);
2069 if (decl.alive) {
2070 try module.linkerUpdateDecl(decl);
2071 continue;
2072 }
2073
2074 // Instead of sending this decl to the linker, we actually will delete it
2075 // because we found out that it in fact was never referenced.
2076 module.deleteUnusedDecl(decl);
20692077 },
20702078 },
20712079 .codegen_func => |func| switch (func.owner_decl.analysis) {
src/Module.zig+52-8
......@@ -255,6 +255,15 @@ pub const Decl = struct {
255255 has_align: bool,
256256 /// Whether the ZIR code provides a linksection instruction.
257257 has_linksection: bool,
258 /// Flag used by garbage collection to mark and sweep.
259 /// Decls which correspond to an AST node always have this field set to `true`.
260 /// Anonymous Decls are initialized with this field set to `false` and then it
261 /// is the responsibility of machine code backends to mark it `true` whenever
262 /// a `decl_ref` Value is encountered that points to this Decl.
263 /// When the `codegen_decl` job is encountered in the main work queue, if the
264 /// Decl is marked alive, then it sends the Decl to the linker. Otherwise it
265 /// deletes the Decl on the spot.
266 alive: bool,
258267
259268 /// Represents the position of the code in the output file.
260269 /// This is populated regardless of semantic analysis and code generation.
......@@ -2869,6 +2878,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
28692878 new_decl.val = struct_val;
28702879 new_decl.has_tv = true;
28712880 new_decl.owns_tv = true;
2881 new_decl.alive = true; // This Decl corresponds to a File and is therefore always alive.
28722882 new_decl.analysis = .in_progress;
28732883 new_decl.generation = mod.generation;
28742884
......@@ -2990,6 +3000,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29903000 if (linksection_ref == .none) break :blk Value.initTag(.null_value);
29913001 break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
29923002 };
3003 try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
29933004
29943005 // We need the memory for the Type to go into the arena for the Decl
29953006 var decl_arena = std.heap.ArenaAllocator.init(gpa);
......@@ -3027,8 +3038,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
30273038 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
30283039 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
30293040 // We don't fully codegen the decl until later, but we do need to reserve a global
3030 // offset table index for it. This allows us to codegen decls out of dependency order,
3031 // increasing how many computations can be done in parallel.
3041 // offset table index for it. This allows us to codegen decls out of dependency
3042 // order, increasing how many computations can be done in parallel.
30323043 try mod.comp.bin_file.allocateDeclIndexes(decl);
30333044 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
30343045 if (type_changed and mod.emit_h != null) {
......@@ -3387,6 +3398,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
33873398 new_decl.has_align = has_align;
33883399 new_decl.has_linksection = has_linksection;
33893400 new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
3401 new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
33903402 return;
33913403 }
33923404 gpa.free(decl_name);
......@@ -3526,6 +3538,43 @@ pub fn clearDecl(
35263538 decl.analysis = .unreferenced;
35273539}
35283540
3541pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
3542 log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name });
3543
3544 // TODO: remove `allocateDeclIndexes` and make the API that the linker backends
3545 // are required to notice the first time `updateDecl` happens and keep track
3546 // of it themselves. However they can rely on getting a `freeDecl` call if any
3547 // `updateDecl` or `updateFunc` calls happen. This will allow us to avoid any call
3548 // into the linker backend here, since the linker backend will never have been told
3549 // about the Decl in the first place.
3550 // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
3551 // must call `freeDecl` in the linker backend now.
3552 if (decl.has_tv) {
3553 if (decl.ty.hasCodeGenBits()) {
3554 mod.comp.bin_file.freeDecl(decl);
3555 }
3556 }
3557
3558 const dependants = decl.dependants.keys();
3559 assert(dependants[0].namespace.anon_decls.swapRemove(decl));
3560
3561 for (dependants) |dep| {
3562 dep.removeDependency(decl);
3563 }
3564
3565 for (decl.dependencies.keys()) |dep| {
3566 dep.removeDependant(decl);
3567 }
3568 decl.destroy(mod);
3569}
3570
3571pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
3572 log.debug("deleteAnonDecl {*} ({s})", .{ decl, decl.name });
3573 const scope_decl = scope.ownerDecl().?;
3574 assert(scope_decl.namespace.anon_decls.swapRemove(decl));
3575 decl.destroy(mod);
3576}
3577
35293578/// Delete all the Export objects that are caused by this Decl. Re-analysis of
35303579/// this Decl will cause them to be re-created (or not).
35313580fn deleteDeclExports(mod: *Module, decl: *Decl) void {
......@@ -3713,6 +3762,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
37133762 .is_exported = false,
37143763 .has_linksection = false,
37153764 .has_align = false,
3765 .alive = false,
37163766 };
37173767 return new_decl;
37183768}
......@@ -3802,12 +3852,6 @@ pub fn analyzeExport(
38023852 errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
38033853}
38043854
3805pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
3806 const scope_decl = scope.ownerDecl().?;
3807 assert(scope_decl.namespace.anon_decls.swapRemove(decl));
3808 decl.destroy(mod);
3809}
3810
38113855/// Takes ownership of `name` even if it returns an error.
38123856pub fn createAnonymousDeclNamed(
38133857 mod: *Module,
src/Sema.zig+80-74
......@@ -696,7 +696,7 @@ fn resolveMaybeUndefVal(
696696) CompileError!?Value {
697697 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
698698 if (val.tag() == .variable) {
699 return sema.failWithNeededComptime(block, src);
699 return null;
700700 }
701701 return val;
702702}
......@@ -2917,12 +2917,13 @@ fn zirOptionalPayloadPtr(
29172917 const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One);
29182918
29192919 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
2920 const val = try pointer_val.pointerDeref(sema.arena);
2921 if (val.isNull()) {
2922 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
2920 if (try pointer_val.pointerDeref(sema.arena)) |val| {
2921 if (val.isNull()) {
2922 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
2923 }
2924 // The same Value represents the pointer to the optional and the payload.
2925 return sema.addConstant(child_pointer, pointer_val);
29232926 }
2924 // The same Value represents the pointer to the optional and the payload.
2925 return sema.addConstant(child_pointer, pointer_val);
29262927 }
29272928
29282929 try sema.requireRuntimeBlock(block, src);
......@@ -3027,14 +3028,15 @@ fn zirErrUnionPayloadPtr(
30273028 const operand_pointer_ty = try Module.simplePtrType(sema.arena, payload_ty, !operand_ty.isConstPtr(), .One);
30283029
30293030 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
3030 const val = try pointer_val.pointerDeref(sema.arena);
3031 if (val.getError()) |name| {
3032 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
3031 if (try pointer_val.pointerDeref(sema.arena)) |val| {
3032 if (val.getError()) |name| {
3033 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
3034 }
3035 return sema.addConstant(
3036 operand_pointer_ty,
3037 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
3038 );
30333039 }
3034 return sema.addConstant(
3035 operand_pointer_ty,
3036 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
3037 );
30383040 }
30393041
30403042 try sema.requireRuntimeBlock(block, src);
......@@ -3086,10 +3088,11 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
30863088 const result_ty = operand_ty.elemType().errorUnionSet();
30873089
30883090 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
3089 const val = try pointer_val.pointerDeref(sema.arena);
3090 assert(val.getError() != null);
3091 const data = val.castTag(.error_union).?.data;
3092 return sema.addConstant(result_ty, data);
3091 if (try pointer_val.pointerDeref(sema.arena)) |val| {
3092 assert(val.getError() != null);
3093 const data = val.castTag(.error_union).?.data;
3094 return sema.addConstant(result_ty, data);
3095 }
30933096 }
30943097
30953098 try sema.requireRuntimeBlock(block, src);
......@@ -4920,10 +4923,13 @@ fn analyzeArithmetic(
49204923 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
49214924
49224925 return sema.addConstant(scalar_type, value);
4926 } else {
4927 try sema.requireRuntimeBlock(block, rhs_src);
49234928 }
4929 } else {
4930 try sema.requireRuntimeBlock(block, lhs_src);
49244931 }
49254932
4926 try sema.requireRuntimeBlock(block, src);
49274933 const air_tag: Air.Inst.Tag = switch (zir_tag) {
49284934 .add => .add,
49294935 .addwrap => .addwrap,
......@@ -6811,16 +6817,10 @@ fn fieldPtr(
68116817 if (mem.eql(u8, field_name, "len")) {
68126818 var anon_decl = try block.startAnonDecl();
68136819 defer anon_decl.deinit();
6814 return sema.addConstant(
6815 Type.initTag(.single_const_pointer_to_comptime_int),
6816 try Value.Tag.decl_ref.create(
6817 arena,
6818 try anon_decl.finish(
6819 Type.initTag(.comptime_int),
6820 try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
6821 ),
6822 ),
6823 );
6820 return sema.analyzeDeclRef(try anon_decl.finish(
6821 Type.initTag(.comptime_int),
6822 try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
6823 ));
68246824 } else {
68256825 return mod.fail(
68266826 &block.base,
......@@ -6867,16 +6867,10 @@ fn fieldPtr(
68676867 if (mem.eql(u8, field_name, "len")) {
68686868 var anon_decl = try block.startAnonDecl();
68696869 defer anon_decl.deinit();
6870 return sema.addConstant(
6871 Type.initTag(.single_const_pointer_to_comptime_int),
6872 try Value.Tag.decl_ref.create(
6873 arena,
6874 try anon_decl.finish(
6875 Type.initTag(.comptime_int),
6876 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
6877 ),
6878 ),
6879 );
6870 return sema.analyzeDeclRef(try anon_decl.finish(
6871 Type.initTag(.comptime_int),
6872 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
6873 ));
68806874 } else {
68816875 return mod.fail(
68826876 &block.base,
......@@ -6915,16 +6909,10 @@ fn fieldPtr(
69156909
69166910 var anon_decl = try block.startAnonDecl();
69176911 defer anon_decl.deinit();
6918 return sema.addConstant(
6919 try Module.simplePtrType(arena, child_type, false, .One),
6920 try Value.Tag.decl_ref.create(
6921 arena,
6922 try anon_decl.finish(
6923 child_type,
6924 try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
6925 ),
6926 ),
6927 );
6912 return sema.analyzeDeclRef(try anon_decl.finish(
6913 child_type,
6914 try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
6915 ));
69286916 },
69296917 .Struct, .Opaque, .Union => {
69306918 if (child_type.getNamespace()) |namespace| {
......@@ -6971,16 +6959,10 @@ fn fieldPtr(
69716959 const field_index_u32 = @intCast(u32, field_index);
69726960 var anon_decl = try block.startAnonDecl();
69736961 defer anon_decl.deinit();
6974 return sema.addConstant(
6975 try Module.simplePtrType(arena, child_type, false, .One),
6976 try Value.Tag.decl_ref.create(
6977 arena,
6978 try anon_decl.finish(
6979 child_type,
6980 try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
6981 ),
6982 ),
6983 );
6962 return sema.analyzeDeclRef(try anon_decl.finish(
6963 child_type,
6964 try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
6965 ));
69846966 },
69856967 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
69866968 }
......@@ -7671,21 +7653,18 @@ fn analyzeRef(
76717653 operand: Air.Inst.Ref,
76727654) CompileError!Air.Inst.Ref {
76737655 const operand_ty = sema.typeOf(operand);
7674 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
76757656
76767657 if (try sema.resolveMaybeUndefVal(block, src, operand)) |val| {
76777658 var anon_decl = try block.startAnonDecl();
76787659 defer anon_decl.deinit();
7679 return sema.addConstant(
7680 ptr_type,
7681 try Value.Tag.decl_ref.create(
7682 sema.arena,
7683 try anon_decl.finish(operand_ty, try val.copy(anon_decl.arena())),
7684 ),
7685 );
7660 return sema.analyzeDeclRef(try anon_decl.finish(
7661 operand_ty,
7662 try val.copy(anon_decl.arena()),
7663 ));
76867664 }
76877665
76887666 try sema.requireRuntimeBlock(block, src);
7667 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
76897668 const alloc = try block.addTy(.alloc, ptr_type);
76907669 try sema.storePtr(block, src, alloc, operand);
76917670 return alloc;
......@@ -7703,11 +7682,10 @@ fn analyzeLoad(
77037682 .Pointer => ptr_ty.elemType(),
77047683 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
77057684 };
7706 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {
7707 if (ptr_val.tag() == .int_u64)
7708 break :blk; // do it at runtime
7709
7710 return sema.addConstant(elem_ty, try ptr_val.pointerDeref(sema.arena));
7685 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
7686 if (try ptr_val.pointerDeref(sema.arena)) |elem_val| {
7687 return sema.addConstant(elem_ty, elem_val);
7688 }
77117689 }
77127690
77137691 try sema.requireRuntimeBlock(block, src);
......@@ -8215,6 +8193,36 @@ fn resolvePeerTypes(
82158193 return sema.typeOf(chosen);
82168194}
82178195
8196pub fn resolveTypeLayout(
8197 sema: *Sema,
8198 block: *Scope.Block,
8199 src: LazySrcLoc,
8200 ty: Type,
8201) CompileError!void {
8202 switch (ty.zigTypeTag()) {
8203 .Pointer => {
8204 return sema.resolveTypeLayout(block, src, ty.elemType());
8205 },
8206 .Struct => {
8207 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
8208 const struct_obj = resolved_ty.castTag(.@"struct").?.data;
8209 switch (struct_obj.status) {
8210 .none, .have_field_types => {},
8211 .field_types_wip, .layout_wip => {
8212 return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
8213 },
8214 .have_layout => return,
8215 }
8216 struct_obj.status = .layout_wip;
8217 for (struct_obj.fields.values()) |field| {
8218 try sema.resolveTypeLayout(block, src, field.ty);
8219 }
8220 struct_obj.status = .have_layout;
8221 },
8222 else => {},
8223 }
8224}
8225
82188226fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {
82198227 switch (ty.tag()) {
82208228 .@"struct" => {
......@@ -8222,9 +8230,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
82228230 switch (struct_obj.status) {
82238231 .none => {},
82248232 .field_types_wip => {
8225 return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{
8226 ty,
8227 });
8233 return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
82288234 },
82298235 .have_field_types, .have_layout, .layout_wip => return ty,
82308236 }
src/codegen.zig+3-4
......@@ -184,6 +184,7 @@ pub fn generateSymbol(
184184 if (typed_value.val.castTag(.decl_ref)) |payload| {
185185 const decl = payload.data;
186186 if (decl.analysis != .complete) return error.AnalysisFail;
187 decl.alive = true;
187188 // TODO handle the dependency of this symbol on the decl's vaddr.
188189 // If the decl changes vaddr, then this symbol needs to get regenerated.
189190 const vaddr = bin_file.getDeclVAddr(decl);
......@@ -4680,13 +4681,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46804681 },
46814682 else => {
46824683 if (typed_value.val.castTag(.decl_ref)) |payload| {
4684 const decl = payload.data;
4685 decl.alive = true;
46834686 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4684 const decl = payload.data;
46854687 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
46864688 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
46874689 return MCValue{ .memory = got_addr };
46884690 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4689 const decl = payload.data;
46904691 const got_addr = blk: {
46914692 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
46924693 const got = seg.sections.items[macho_file.got_section_index.?];
......@@ -4698,11 +4699,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46984699 };
46994700 return MCValue{ .memory = got_addr };
47004701 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4701 const decl = payload.data;
47024702 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
47034703 return MCValue{ .memory = got_addr };
47044704 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4705 const decl = payload.data;
47064705 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
47074706 return MCValue{ .memory = got_addr };
47084707 } else {
src/codegen/c.zig+7-17
......@@ -262,6 +262,7 @@ pub const DeclGen = struct {
262262 .one => try writer.writeAll("1"),
263263 .decl_ref => {
264264 const decl = val.castTag(.decl_ref).?.data;
265 decl.alive = true;
265266
266267 // Determine if we must pointer cast.
267268 assert(decl.has_tv);
......@@ -281,21 +282,7 @@ pub const DeclGen = struct {
281282 const decl = val.castTag(.extern_fn).?.data;
282283 try writer.print("{s}", .{decl.name});
283284 },
284 else => switch (t.ptrSize()) {
285 .Slice => unreachable,
286 .Many => unreachable,
287 .One => {
288 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
289 defer arena.deinit();
290
291 const elem_ty = t.elemType();
292 const elem_val = try val.pointerDeref(&arena.allocator);
293
294 try writer.writeAll("&");
295 try dg.renderValue(writer, elem_ty, elem_val);
296 },
297 .C => unreachable,
298 },
285 else => unreachable,
299286 },
300287 },
301288 .Array => {
......@@ -421,6 +408,7 @@ pub const DeclGen = struct {
421408 .one => try writer.writeAll("1"),
422409 .decl_ref => {
423410 const decl = val.castTag(.decl_ref).?.data;
411 decl.alive = true;
424412
425413 // Determine if we must pointer cast.
426414 assert(decl.has_tv);
......@@ -433,11 +421,13 @@ pub const DeclGen = struct {
433421 }
434422 },
435423 .function => {
436 const func = val.castTag(.function).?.data;
437 try writer.print("{s}", .{func.owner_decl.name});
424 const decl = val.castTag(.function).?.data.owner_decl;
425 decl.alive = true;
426 try writer.print("{s}", .{decl.name});
438427 },
439428 .extern_fn => {
440429 const decl = val.castTag(.extern_fn).?.data;
430 decl.alive = true;
441431 try writer.print("{s}", .{decl.name});
442432 },
443433 else => unreachable,
src/codegen/llvm.zig+34-17
......@@ -673,17 +673,21 @@ pub const DeclGen = struct {
673673 }
674674
675675 fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
676 const llvm_type = try self.llvmType(tv.ty);
677
678 if (tv.val.isUndef())
676 if (tv.val.isUndef()) {
677 const llvm_type = try self.llvmType(tv.ty);
679678 return llvm_type.getUndef();
679 }
680680
681681 switch (tv.ty.zigTypeTag()) {
682 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
682 .Bool => {
683 const llvm_type = try self.llvmType(tv.ty);
684 return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull();
685 },
683686 .Int => {
684687 var bigint_space: Value.BigIntSpace = undefined;
685688 const bigint = tv.val.toBigInt(&bigint_space);
686689
690 const llvm_type = try self.llvmType(tv.ty);
687691 if (bigint.eqZero()) return llvm_type.constNull();
688692
689693 if (bigint.limbs.len != 1) {
......@@ -698,12 +702,17 @@ pub const DeclGen = struct {
698702 .Pointer => switch (tv.val.tag()) {
699703 .decl_ref => {
700704 const decl = tv.val.castTag(.decl_ref).?.data;
705 decl.alive = true;
701706 const val = try self.resolveGlobalDecl(decl);
707 const llvm_type = try self.llvmType(tv.ty);
702708 return val.constBitCast(llvm_type);
703709 },
704710 .variable => {
705 const variable = tv.val.castTag(.variable).?.data;
706 const val = try self.resolveGlobalDecl(variable.owner_decl);
711 const decl = tv.val.castTag(.variable).?.data.owner_decl;
712 decl.alive = true;
713 const val = try self.resolveGlobalDecl(decl);
714 const llvm_var_type = try self.llvmType(tv.ty);
715 const llvm_type = llvm_var_type.pointerType(0);
707716 return val.constBitCast(llvm_type);
708717 },
709718 .slice => {
......@@ -783,6 +792,7 @@ pub const DeclGen = struct {
783792 .decl_ref => tv.val.castTag(.decl_ref).?.data,
784793 else => unreachable,
785794 };
795 fn_decl.alive = true;
786796 return self.resolveLlvmFunction(fn_decl);
787797 },
788798 .ErrorSet => {
......@@ -903,9 +913,7 @@ pub const FuncGen = struct {
903913 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });
904914 }
905915 const inst_index = Air.refToIndex(inst).?;
906 if (self.func_inst_table.get(inst_index)) |value| return value;
907
908 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
916 return self.func_inst_table.get(inst_index).?;
909917 }
910918
911919 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
......@@ -966,8 +974,8 @@ pub const FuncGen = struct {
966974 .struct_field_ptr => try self.airStructFieldPtr(inst),
967975 .struct_field_val => try self.airStructFieldVal(inst),
968976
969 .slice_elem_val => try self.airSliceElemVal(inst, false),
970 .ptr_slice_elem_val => try self.airSliceElemVal(inst, true),
977 .slice_elem_val => try self.airSliceElemVal(inst),
978 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
971979
972980 .optional_payload => try self.airOptionalPayload(inst, false),
973981 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
......@@ -1170,11 +1178,20 @@ pub const FuncGen = struct {
11701178 return self.builder.buildExtractValue(operand, index, "");
11711179 }
11721180
1173 fn airSliceElemVal(
1174 self: *FuncGen,
1175 inst: Air.Inst.Index,
1176 operand_is_ptr: bool,
1177 ) !?*const llvm.Value {
1181 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1182 if (self.liveness.isUnused(inst))
1183 return null;
1184
1185 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1186 const lhs = try self.resolveInst(bin_op.lhs);
1187 const rhs = try self.resolveInst(bin_op.rhs);
1188 const base_ptr = self.builder.buildExtractValue(lhs, 0, "");
1189 const indices: [1]*const llvm.Value = .{rhs};
1190 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1191 return self.builder.buildLoad(ptr, "");
1192 }
1193
1194 fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
11781195 if (self.liveness.isUnused(inst))
11791196 return null;
11801197
......@@ -1182,7 +1199,7 @@ pub const FuncGen = struct {
11821199 const lhs = try self.resolveInst(bin_op.lhs);
11831200 const rhs = try self.resolveInst(bin_op.rhs);
11841201
1185 const base_ptr = if (!operand_is_ptr) lhs else ptr: {
1202 const base_ptr = ptr: {
11861203 const index_type = self.context.intType(32);
11871204 const indices: [2]*const llvm.Value = .{
11881205 index_type.constNull(),
src/codegen/wasm.zig+1
......@@ -1016,6 +1016,7 @@ pub const Context = struct {
10161016 .Pointer => {
10171017 if (val.castTag(.decl_ref)) |payload| {
10181018 const decl = payload.data;
1019 decl.alive = true;
10191020
10201021 // offset into the offset table within the 'data' section
10211022 const ptr_width = self.target.cpu.arch.ptrBitWidth() / 8;
src/link/Plan9.zig+14-5
......@@ -224,7 +224,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
224224
225225 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
226226
227 assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count());
227 // TODO I changed this assert from == to >= but this code all needs to be audited; see
228 // the comment in `freeDecl`.
229 assert(self.got_len >= self.fn_decl_table.count() + self.data_decl_table.count());
228230 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
229231 var got_table = try self.base.allocator.alloc(u8, got_size);
230232 defer self.base.allocator.free(got_table);
......@@ -358,11 +360,18 @@ fn addDeclExports(
358360}
359361
360362pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
363 // TODO this is not the correct check for being function body,
364 // it could just be a function pointer.
365 // TODO audit the lifetimes of decls table entries. It's possible to get
366 // allocateDeclIndexes and then freeDecl without any updateDecl in between.
367 // However that is planned to change, see the TODO comment in Module.zig
368 // in the deleteUnusedDecl function.
361369 const is_fn = (decl.ty.zigTypeTag() == .Fn);
362 if (is_fn)
363 assert(self.fn_decl_table.swapRemove(decl))
364 else
365 assert(self.data_decl_table.swapRemove(decl));
370 if (is_fn) {
371 _ = self.fn_decl_table.swapRemove(decl);
372 } else {
373 _ = self.data_decl_table.swapRemove(decl);
374 }
366375}
367376
368377pub fn updateDeclExports(
src/value.zig+33-12
......@@ -103,6 +103,7 @@ pub const Value = extern union {
103103 /// Represents a comptime variables storage.
104104 comptime_alloc,
105105 /// Represents a pointer to a decl, not the value of the decl.
106 /// When machine codegen backend sees this, it must set the Decl's `alive` field to true.
106107 decl_ref,
107108 elem_ptr,
108109 field_ptr,
......@@ -1346,28 +1347,48 @@ pub const Value = extern union {
13461347
13471348 /// Asserts the value is a pointer and dereferences it.
13481349 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
1349 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
1350 return switch (self.tag()) {
1350 pub fn pointerDeref(
1351 self: Value,
1352 allocator: *Allocator,
1353 ) error{ AnalysisFail, OutOfMemory }!?Value {
1354 const sub_val: Value = switch (self.tag()) {
13511355 .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,
1352 .decl_ref => self.castTag(.decl_ref).?.data.value(),
1353 .elem_ptr => {
1356 .decl_ref => try self.castTag(.decl_ref).?.data.value(),
1357 .elem_ptr => blk: {
13541358 const elem_ptr = self.castTag(.elem_ptr).?.data;
1355 const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
1356 return array_val.elemValue(allocator, elem_ptr.index);
1359 const array_val = (try elem_ptr.array_ptr.pointerDeref(allocator)) orelse return null;
1360 break :blk try array_val.elemValue(allocator, elem_ptr.index);
13571361 },
1358 .field_ptr => {
1362 .field_ptr => blk: {
13591363 const field_ptr = self.castTag(.field_ptr).?.data;
1360 const container_val = try field_ptr.container_ptr.pointerDeref(allocator);
1361 return container_val.fieldValue(allocator, field_ptr.field_index);
1364 const container_val = (try field_ptr.container_ptr.pointerDeref(allocator)) orelse return null;
1365 break :blk try container_val.fieldValue(allocator, field_ptr.field_index);
13621366 },
1363 .eu_payload_ptr => {
1367 .eu_payload_ptr => blk: {
13641368 const err_union_ptr = self.castTag(.eu_payload_ptr).?.data;
1365 const err_union_val = try err_union_ptr.pointerDeref(allocator);
1366 return err_union_val.castTag(.error_union).?.data;
1369 const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null;
1370 break :blk err_union_val.castTag(.error_union).?.data;
13671371 },
13681372
1373 .zero,
1374 .one,
1375 .int_u64,
1376 .int_i64,
1377 .int_big_positive,
1378 .int_big_negative,
1379 .variable,
1380 .extern_fn,
1381 .function,
1382 => return null,
1383
13691384 else => unreachable,
13701385 };
1386 if (sub_val.tag() == .variable) {
1387 // This would be loading a runtime value at compile-time so we return
1388 // the indicator that this pointer dereference requires being done at runtime.
1389 return null;
1390 }
1391 return sub_val;
13711392 }
13721393
13731394 pub fn sliceLen(val: Value) u64 {
test/stage2/cbe.zig+1-1
......@@ -49,7 +49,7 @@ pub fn addCases(ctx: *TestContext) !void {
4949 \\export fn foo() callconv(y) c_int {
5050 \\ return 0;
5151 \\}
52 \\var y: i32 = 1234;
52 \\var y: @import("std").builtin.CallingConvention = .C;
5353 , &.{
5454 ":2:22: error: unable to resolve comptime value",
5555 ":5:26: error: unable to resolve comptime value",