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...@@ -2061,11 +2061,19 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2061 .complete, .codegen_failure_retryable => {2061 .complete, .codegen_failure_retryable => {
2062 if (build_options.omit_stage2)2062 if (build_options.omit_stage2)
2063 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2063 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2064
2064 const module = self.bin_file.options.module.?;2065 const module = self.bin_file.options.module.?;
2065 assert(decl.has_tv);2066 assert(decl.has_tv);
2066 assert(decl.ty.hasCodeGenBits());2067 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);
2069 },2077 },
2070 },2078 },
2071 .codegen_func => |func| switch (func.owner_decl.analysis) {2079 .codegen_func => |func| switch (func.owner_decl.analysis) {
src/Module.zig+52-8
...@@ -255,6 +255,15 @@ pub const Decl = struct {...@@ -255,6 +255,15 @@ pub const Decl = struct {
255 has_align: bool,255 has_align: bool,
256 /// Whether the ZIR code provides a linksection instruction.256 /// Whether the ZIR code provides a linksection instruction.
257 has_linksection: bool,257 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
259 /// Represents the position of the code in the output file.268 /// Represents the position of the code in the output file.
260 /// This is populated regardless of semantic analysis and code generation.269 /// This is populated regardless of semantic analysis and code generation.
...@@ -2869,6 +2878,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -2869,6 +2878,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
2869 new_decl.val = struct_val;2878 new_decl.val = struct_val;
2870 new_decl.has_tv = true;2879 new_decl.has_tv = true;
2871 new_decl.owns_tv = true;2880 new_decl.owns_tv = true;
2881 new_decl.alive = true; // This Decl corresponds to a File and is therefore always alive.
2872 new_decl.analysis = .in_progress;2882 new_decl.analysis = .in_progress;
2873 new_decl.generation = mod.generation;2883 new_decl.generation = mod.generation;
28742884
...@@ -2990,6 +3000,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2990,6 +3000,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2990 if (linksection_ref == .none) break :blk Value.initTag(.null_value);3000 if (linksection_ref == .none) break :blk Value.initTag(.null_value);
2991 break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;3001 break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
2992 };3002 };
3003 try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
29933004
2994 // We need the memory for the Type to go into the arena for the Decl3005 // We need the memory for the Type to go into the arena for the Decl
2995 var decl_arena = std.heap.ArenaAllocator.init(gpa);3006 var decl_arena = std.heap.ArenaAllocator.init(gpa);
...@@ -3027,8 +3038,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3027,8 +3038,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3027 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;3038 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
3028 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {3039 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
3029 // We don't fully codegen the decl until later, but we do need to reserve a global3040 // 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,3041 // offset table index for it. This allows us to codegen decls out of dependency
3031 // increasing how many computations can be done in parallel.3042 // order, increasing how many computations can be done in parallel.
3032 try mod.comp.bin_file.allocateDeclIndexes(decl);3043 try mod.comp.bin_file.allocateDeclIndexes(decl);
3033 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });3044 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
3034 if (type_changed and mod.emit_h != null) {3045 if (type_changed and mod.emit_h != null) {
...@@ -3387,6 +3398,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi...@@ -3387,6 +3398,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3387 new_decl.has_align = has_align;3398 new_decl.has_align = has_align;
3388 new_decl.has_linksection = has_linksection;3399 new_decl.has_linksection = has_linksection;
3389 new_decl.zir_decl_index = @intCast(u32, decl_sub_index);3400 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.
3390 return;3402 return;
3391 }3403 }
3392 gpa.free(decl_name);3404 gpa.free(decl_name);
...@@ -3526,6 +3538,43 @@ pub fn clearDecl(...@@ -3526,6 +3538,43 @@ pub fn clearDecl(
3526 decl.analysis = .unreferenced;3538 decl.analysis = .unreferenced;
3527}3539}
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
3529/// Delete all the Export objects that are caused by this Decl. Re-analysis of3578/// Delete all the Export objects that are caused by this Decl. Re-analysis of
3530/// this Decl will cause them to be re-created (or not).3579/// this Decl will cause them to be re-created (or not).
3531fn deleteDeclExports(mod: *Module, decl: *Decl) void {3580fn deleteDeclExports(mod: *Module, decl: *Decl) void {
...@@ -3713,6 +3762,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node...@@ -3713,6 +3762,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
3713 .is_exported = false,3762 .is_exported = false,
3714 .has_linksection = false,3763 .has_linksection = false,
3715 .has_align = false,3764 .has_align = false,
3765 .alive = false,
3716 };3766 };
3717 return new_decl;3767 return new_decl;
3718}3768}
...@@ -3802,12 +3852,6 @@ pub fn analyzeExport(...@@ -3802,12 +3852,6 @@ pub fn analyzeExport(
3802 errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);3852 errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
3803}3853}
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
3811/// Takes ownership of `name` even if it returns an error.3855/// Takes ownership of `name` even if it returns an error.
3812pub fn createAnonymousDeclNamed(3856pub fn createAnonymousDeclNamed(
3813 mod: *Module,3857 mod: *Module,
src/Sema.zig+80-74
...@@ -696,7 +696,7 @@ fn resolveMaybeUndefVal(...@@ -696,7 +696,7 @@ fn resolveMaybeUndefVal(
696) CompileError!?Value {696) CompileError!?Value {
697 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;697 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
698 if (val.tag() == .variable) {698 if (val.tag() == .variable) {
699 return sema.failWithNeededComptime(block, src);699 return null;
700 }700 }
701 return val;701 return val;
702}702}
...@@ -2917,12 +2917,13 @@ fn zirOptionalPayloadPtr(...@@ -2917,12 +2917,13 @@ fn zirOptionalPayloadPtr(
2917 const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One);2917 const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One);
29182918
2919 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {2919 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
2920 const val = try pointer_val.pointerDeref(sema.arena);2920 if (try pointer_val.pointerDeref(sema.arena)) |val| {
2921 if (val.isNull()) {2921 if (val.isNull()) {
2922 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});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);
2923 }2926 }
2924 // The same Value represents the pointer to the optional and the payload.
2925 return sema.addConstant(child_pointer, pointer_val);
2926 }2927 }
29272928
2928 try sema.requireRuntimeBlock(block, src);2929 try sema.requireRuntimeBlock(block, src);
...@@ -3027,14 +3028,15 @@ fn zirErrUnionPayloadPtr(...@@ -3027,14 +3028,15 @@ fn zirErrUnionPayloadPtr(
3027 const operand_pointer_ty = try Module.simplePtrType(sema.arena, payload_ty, !operand_ty.isConstPtr(), .One);3028 const operand_pointer_ty = try Module.simplePtrType(sema.arena, payload_ty, !operand_ty.isConstPtr(), .One);
30283029
3029 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {3030 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
3030 const val = try pointer_val.pointerDeref(sema.arena);3031 if (try pointer_val.pointerDeref(sema.arena)) |val| {
3031 if (val.getError()) |name| {3032 if (val.getError()) |name| {
3032 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{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 );
3033 }3039 }
3034 return sema.addConstant(
3035 operand_pointer_ty,
3036 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
3037 );
3038 }3040 }
30393041
3040 try sema.requireRuntimeBlock(block, src);3042 try sema.requireRuntimeBlock(block, src);
...@@ -3086,10 +3088,11 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co...@@ -3086,10 +3088,11 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
3086 const result_ty = operand_ty.elemType().errorUnionSet();3088 const result_ty = operand_ty.elemType().errorUnionSet();
30873089
3088 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {3090 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
3089 const val = try pointer_val.pointerDeref(sema.arena);3091 if (try pointer_val.pointerDeref(sema.arena)) |val| {
3090 assert(val.getError() != null);3092 assert(val.getError() != null);
3091 const data = val.castTag(.error_union).?.data;3093 const data = val.castTag(.error_union).?.data;
3092 return sema.addConstant(result_ty, data);3094 return sema.addConstant(result_ty, data);
3095 }
3093 }3096 }
30943097
3095 try sema.requireRuntimeBlock(block, src);3098 try sema.requireRuntimeBlock(block, src);
...@@ -4920,10 +4923,13 @@ fn analyzeArithmetic(...@@ -4920,10 +4923,13 @@ fn analyzeArithmetic(
4920 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });4923 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
49214924
4922 return sema.addConstant(scalar_type, value);4925 return sema.addConstant(scalar_type, value);
4926 } else {
4927 try sema.requireRuntimeBlock(block, rhs_src);
4923 }4928 }
4929 } else {
4930 try sema.requireRuntimeBlock(block, lhs_src);
4924 }4931 }
49254932
4926 try sema.requireRuntimeBlock(block, src);
4927 const air_tag: Air.Inst.Tag = switch (zir_tag) {4933 const air_tag: Air.Inst.Tag = switch (zir_tag) {
4928 .add => .add,4934 .add => .add,
4929 .addwrap => .addwrap,4935 .addwrap => .addwrap,
...@@ -6811,16 +6817,10 @@ fn fieldPtr(...@@ -6811,16 +6817,10 @@ fn fieldPtr(
6811 if (mem.eql(u8, field_name, "len")) {6817 if (mem.eql(u8, field_name, "len")) {
6812 var anon_decl = try block.startAnonDecl();6818 var anon_decl = try block.startAnonDecl();
6813 defer anon_decl.deinit();6819 defer anon_decl.deinit();
6814 return sema.addConstant(6820 return sema.analyzeDeclRef(try anon_decl.finish(
6815 Type.initTag(.single_const_pointer_to_comptime_int),6821 Type.initTag(.comptime_int),
6816 try Value.Tag.decl_ref.create(6822 try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
6817 arena,6823 ));
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 );
6824 } else {6824 } else {
6825 return mod.fail(6825 return mod.fail(
6826 &block.base,6826 &block.base,
...@@ -6867,16 +6867,10 @@ fn fieldPtr(...@@ -6867,16 +6867,10 @@ fn fieldPtr(
6867 if (mem.eql(u8, field_name, "len")) {6867 if (mem.eql(u8, field_name, "len")) {
6868 var anon_decl = try block.startAnonDecl();6868 var anon_decl = try block.startAnonDecl();
6869 defer anon_decl.deinit();6869 defer anon_decl.deinit();
6870 return sema.addConstant(6870 return sema.analyzeDeclRef(try anon_decl.finish(
6871 Type.initTag(.single_const_pointer_to_comptime_int),6871 Type.initTag(.comptime_int),
6872 try Value.Tag.decl_ref.create(6872 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
6873 arena,6873 ));
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 );
6880 } else {6874 } else {
6881 return mod.fail(6875 return mod.fail(
6882 &block.base,6876 &block.base,
...@@ -6915,16 +6909,10 @@ fn fieldPtr(...@@ -6915,16 +6909,10 @@ fn fieldPtr(
69156909
6916 var anon_decl = try block.startAnonDecl();6910 var anon_decl = try block.startAnonDecl();
6917 defer anon_decl.deinit();6911 defer anon_decl.deinit();
6918 return sema.addConstant(6912 return sema.analyzeDeclRef(try anon_decl.finish(
6919 try Module.simplePtrType(arena, child_type, false, .One),6913 child_type,
6920 try Value.Tag.decl_ref.create(6914 try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
6921 arena,6915 ));
6922 try anon_decl.finish(
6923 child_type,
6924 try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
6925 ),
6926 ),
6927 );
6928 },6916 },
6929 .Struct, .Opaque, .Union => {6917 .Struct, .Opaque, .Union => {
6930 if (child_type.getNamespace()) |namespace| {6918 if (child_type.getNamespace()) |namespace| {
...@@ -6971,16 +6959,10 @@ fn fieldPtr(...@@ -6971,16 +6959,10 @@ fn fieldPtr(
6971 const field_index_u32 = @intCast(u32, field_index);6959 const field_index_u32 = @intCast(u32, field_index);
6972 var anon_decl = try block.startAnonDecl();6960 var anon_decl = try block.startAnonDecl();
6973 defer anon_decl.deinit();6961 defer anon_decl.deinit();
6974 return sema.addConstant(6962 return sema.analyzeDeclRef(try anon_decl.finish(
6975 try Module.simplePtrType(arena, child_type, false, .One),6963 child_type,
6976 try Value.Tag.decl_ref.create(6964 try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
6977 arena,6965 ));
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 );
6984 },6966 },
6985 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),6967 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
6986 }6968 }
...@@ -7671,21 +7653,18 @@ fn analyzeRef(...@@ -7671,21 +7653,18 @@ fn analyzeRef(
7671 operand: Air.Inst.Ref,7653 operand: Air.Inst.Ref,
7672) CompileError!Air.Inst.Ref {7654) CompileError!Air.Inst.Ref {
7673 const operand_ty = sema.typeOf(operand);7655 const operand_ty = sema.typeOf(operand);
7674 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
76757656
7676 if (try sema.resolveMaybeUndefVal(block, src, operand)) |val| {7657 if (try sema.resolveMaybeUndefVal(block, src, operand)) |val| {
7677 var anon_decl = try block.startAnonDecl();7658 var anon_decl = try block.startAnonDecl();
7678 defer anon_decl.deinit();7659 defer anon_decl.deinit();
7679 return sema.addConstant(7660 return sema.analyzeDeclRef(try anon_decl.finish(
7680 ptr_type,7661 operand_ty,
7681 try Value.Tag.decl_ref.create(7662 try val.copy(anon_decl.arena()),
7682 sema.arena,7663 ));
7683 try anon_decl.finish(operand_ty, try val.copy(anon_decl.arena())),
7684 ),
7685 );
7686 }7664 }
76877665
7688 try sema.requireRuntimeBlock(block, src);7666 try sema.requireRuntimeBlock(block, src);
7667 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
7689 const alloc = try block.addTy(.alloc, ptr_type);7668 const alloc = try block.addTy(.alloc, ptr_type);
7690 try sema.storePtr(block, src, alloc, operand);7669 try sema.storePtr(block, src, alloc, operand);
7691 return alloc;7670 return alloc;
...@@ -7703,11 +7682,10 @@ fn analyzeLoad(...@@ -7703,11 +7682,10 @@ fn analyzeLoad(
7703 .Pointer => ptr_ty.elemType(),7682 .Pointer => ptr_ty.elemType(),
7704 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),7683 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
7705 };7684 };
7706 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {7685 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
7707 if (ptr_val.tag() == .int_u64)7686 if (try ptr_val.pointerDeref(sema.arena)) |elem_val| {
7708 break :blk; // do it at runtime7687 return sema.addConstant(elem_ty, elem_val);
77097688 }
7710 return sema.addConstant(elem_ty, try ptr_val.pointerDeref(sema.arena));
7711 }7689 }
77127690
7713 try sema.requireRuntimeBlock(block, src);7691 try sema.requireRuntimeBlock(block, src);
...@@ -8215,6 +8193,36 @@ fn resolvePeerTypes(...@@ -8215,6 +8193,36 @@ fn resolvePeerTypes(
8215 return sema.typeOf(chosen);8193 return sema.typeOf(chosen);
8216}8194}
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
8218fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {8226fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {
8219 switch (ty.tag()) {8227 switch (ty.tag()) {
8220 .@"struct" => {8228 .@"struct" => {
...@@ -8222,9 +8230,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type...@@ -8222,9 +8230,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
8222 switch (struct_obj.status) {8230 switch (struct_obj.status) {
8223 .none => {},8231 .none => {},
8224 .field_types_wip => {8232 .field_types_wip => {
8225 return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{8233 return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
8226 ty,
8227 });
8228 },8234 },
8229 .have_field_types, .have_layout, .layout_wip => return ty,8235 .have_field_types, .have_layout, .layout_wip => return ty,
8230 }8236 }
src/codegen.zig+3-4
...@@ -184,6 +184,7 @@ pub fn generateSymbol(...@@ -184,6 +184,7 @@ pub fn generateSymbol(
184 if (typed_value.val.castTag(.decl_ref)) |payload| {184 if (typed_value.val.castTag(.decl_ref)) |payload| {
185 const decl = payload.data;185 const decl = payload.data;
186 if (decl.analysis != .complete) return error.AnalysisFail;186 if (decl.analysis != .complete) return error.AnalysisFail;
187 decl.alive = true;
187 // TODO handle the dependency of this symbol on the decl's vaddr.188 // TODO handle the dependency of this symbol on the decl's vaddr.
188 // If the decl changes vaddr, then this symbol needs to get regenerated.189 // If the decl changes vaddr, then this symbol needs to get regenerated.
189 const vaddr = bin_file.getDeclVAddr(decl);190 const vaddr = bin_file.getDeclVAddr(decl);
...@@ -4680,13 +4681,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4680,13 +4681,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4680 },4681 },
4681 else => {4682 else => {
4682 if (typed_value.val.castTag(.decl_ref)) |payload| {4683 if (typed_value.val.castTag(.decl_ref)) |payload| {
4684 const decl = payload.data;
4685 decl.alive = true;
4683 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4686 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4684 const decl = payload.data;
4685 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];4687 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4686 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;4688 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
4687 return MCValue{ .memory = got_addr };4689 return MCValue{ .memory = got_addr };
4688 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4690 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4689 const decl = payload.data;
4690 const got_addr = blk: {4691 const got_addr = blk: {
4691 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;4692 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
4692 const got = seg.sections.items[macho_file.got_section_index.?];4693 const got = seg.sections.items[macho_file.got_section_index.?];
...@@ -4698,11 +4699,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4698,11 +4699,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4698 };4699 };
4699 return MCValue{ .memory = got_addr };4700 return MCValue{ .memory = got_addr };
4700 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4701 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4701 const decl = payload.data;
4702 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;4702 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4703 return MCValue{ .memory = got_addr };4703 return MCValue{ .memory = got_addr };
4704 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {4704 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4705 const decl = payload.data;
4706 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;4705 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
4707 return MCValue{ .memory = got_addr };4706 return MCValue{ .memory = got_addr };
4708 } else {4707 } else {
src/codegen/c.zig+7-17
...@@ -262,6 +262,7 @@ pub const DeclGen = struct {...@@ -262,6 +262,7 @@ pub const DeclGen = struct {
262 .one => try writer.writeAll("1"),262 .one => try writer.writeAll("1"),
263 .decl_ref => {263 .decl_ref => {
264 const decl = val.castTag(.decl_ref).?.data;264 const decl = val.castTag(.decl_ref).?.data;
265 decl.alive = true;
265266
266 // Determine if we must pointer cast.267 // Determine if we must pointer cast.
267 assert(decl.has_tv);268 assert(decl.has_tv);
...@@ -281,21 +282,7 @@ pub const DeclGen = struct {...@@ -281,21 +282,7 @@ pub const DeclGen = struct {
281 const decl = val.castTag(.extern_fn).?.data;282 const decl = val.castTag(.extern_fn).?.data;
282 try writer.print("{s}", .{decl.name});283 try writer.print("{s}", .{decl.name});
283 },284 },
284 else => switch (t.ptrSize()) {285 else => unreachable,
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 },
299 },286 },
300 },287 },
301 .Array => {288 .Array => {
...@@ -421,6 +408,7 @@ pub const DeclGen = struct {...@@ -421,6 +408,7 @@ pub const DeclGen = struct {
421 .one => try writer.writeAll("1"),408 .one => try writer.writeAll("1"),
422 .decl_ref => {409 .decl_ref => {
423 const decl = val.castTag(.decl_ref).?.data;410 const decl = val.castTag(.decl_ref).?.data;
411 decl.alive = true;
424412
425 // Determine if we must pointer cast.413 // Determine if we must pointer cast.
426 assert(decl.has_tv);414 assert(decl.has_tv);
...@@ -433,11 +421,13 @@ pub const DeclGen = struct {...@@ -433,11 +421,13 @@ pub const DeclGen = struct {
433 }421 }
434 },422 },
435 .function => {423 .function => {
436 const func = val.castTag(.function).?.data;424 const decl = val.castTag(.function).?.data.owner_decl;
437 try writer.print("{s}", .{func.owner_decl.name});425 decl.alive = true;
426 try writer.print("{s}", .{decl.name});
438 },427 },
439 .extern_fn => {428 .extern_fn => {
440 const decl = val.castTag(.extern_fn).?.data;429 const decl = val.castTag(.extern_fn).?.data;
430 decl.alive = true;
441 try writer.print("{s}", .{decl.name});431 try writer.print("{s}", .{decl.name});
442 },432 },
443 else => unreachable,433 else => unreachable,
src/codegen/llvm.zig+34-17
...@@ -673,17 +673,21 @@ pub const DeclGen = struct {...@@ -673,17 +673,21 @@ pub const DeclGen = struct {
673 }673 }
674674
675 fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {675 fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
676 const llvm_type = try self.llvmType(tv.ty);676 if (tv.val.isUndef()) {
677677 const llvm_type = try self.llvmType(tv.ty);
678 if (tv.val.isUndef())
679 return llvm_type.getUndef();678 return llvm_type.getUndef();
679 }
680680
681 switch (tv.ty.zigTypeTag()) {681 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 },
683 .Int => {686 .Int => {
684 var bigint_space: Value.BigIntSpace = undefined;687 var bigint_space: Value.BigIntSpace = undefined;
685 const bigint = tv.val.toBigInt(&bigint_space);688 const bigint = tv.val.toBigInt(&bigint_space);
686689
690 const llvm_type = try self.llvmType(tv.ty);
687 if (bigint.eqZero()) return llvm_type.constNull();691 if (bigint.eqZero()) return llvm_type.constNull();
688692
689 if (bigint.limbs.len != 1) {693 if (bigint.limbs.len != 1) {
...@@ -698,12 +702,17 @@ pub const DeclGen = struct {...@@ -698,12 +702,17 @@ pub const DeclGen = struct {
698 .Pointer => switch (tv.val.tag()) {702 .Pointer => switch (tv.val.tag()) {
699 .decl_ref => {703 .decl_ref => {
700 const decl = tv.val.castTag(.decl_ref).?.data;704 const decl = tv.val.castTag(.decl_ref).?.data;
705 decl.alive = true;
701 const val = try self.resolveGlobalDecl(decl);706 const val = try self.resolveGlobalDecl(decl);
707 const llvm_type = try self.llvmType(tv.ty);
702 return val.constBitCast(llvm_type);708 return val.constBitCast(llvm_type);
703 },709 },
704 .variable => {710 .variable => {
705 const variable = tv.val.castTag(.variable).?.data;711 const decl = tv.val.castTag(.variable).?.data.owner_decl;
706 const val = try self.resolveGlobalDecl(variable.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);
707 return val.constBitCast(llvm_type);716 return val.constBitCast(llvm_type);
708 },717 },
709 .slice => {718 .slice => {
...@@ -783,6 +792,7 @@ pub const DeclGen = struct {...@@ -783,6 +792,7 @@ pub const DeclGen = struct {
783 .decl_ref => tv.val.castTag(.decl_ref).?.data,792 .decl_ref => tv.val.castTag(.decl_ref).?.data,
784 else => unreachable,793 else => unreachable,
785 };794 };
795 fn_decl.alive = true;
786 return self.resolveLlvmFunction(fn_decl);796 return self.resolveLlvmFunction(fn_decl);
787 },797 },
788 .ErrorSet => {798 .ErrorSet => {
...@@ -903,9 +913,7 @@ pub const FuncGen = struct {...@@ -903,9 +913,7 @@ pub const FuncGen = struct {
903 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });913 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });
904 }914 }
905 const inst_index = Air.refToIndex(inst).?;915 const inst_index = Air.refToIndex(inst).?;
906 if (self.func_inst_table.get(inst_index)) |value| return value;916 return self.func_inst_table.get(inst_index).?;
907
908 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
909 }917 }
910918
911 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {919 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
...@@ -966,8 +974,8 @@ pub const FuncGen = struct {...@@ -966,8 +974,8 @@ pub const FuncGen = struct {
966 .struct_field_ptr => try self.airStructFieldPtr(inst),974 .struct_field_ptr => try self.airStructFieldPtr(inst),
967 .struct_field_val => try self.airStructFieldVal(inst),975 .struct_field_val => try self.airStructFieldVal(inst),
968976
969 .slice_elem_val => try self.airSliceElemVal(inst, false),977 .slice_elem_val => try self.airSliceElemVal(inst),
970 .ptr_slice_elem_val => try self.airSliceElemVal(inst, true),978 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
971979
972 .optional_payload => try self.airOptionalPayload(inst, false),980 .optional_payload => try self.airOptionalPayload(inst, false),
973 .optional_payload_ptr => try self.airOptionalPayload(inst, true),981 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
...@@ -1170,11 +1178,20 @@ pub const FuncGen = struct {...@@ -1170,11 +1178,20 @@ pub const FuncGen = struct {
1170 return self.builder.buildExtractValue(operand, index, "");1178 return self.builder.buildExtractValue(operand, index, "");
1171 }1179 }
11721180
1173 fn airSliceElemVal(1181 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1174 self: *FuncGen,1182 if (self.liveness.isUnused(inst))
1175 inst: Air.Inst.Index,1183 return null;
1176 operand_is_ptr: bool,1184
1177 ) !?*const llvm.Value {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 {
1178 if (self.liveness.isUnused(inst))1195 if (self.liveness.isUnused(inst))
1179 return null;1196 return null;
11801197
...@@ -1182,7 +1199,7 @@ pub const FuncGen = struct {...@@ -1182,7 +1199,7 @@ pub const FuncGen = struct {
1182 const lhs = try self.resolveInst(bin_op.lhs);1199 const lhs = try self.resolveInst(bin_op.lhs);
1183 const rhs = try self.resolveInst(bin_op.rhs);1200 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: {
1186 const index_type = self.context.intType(32);1203 const index_type = self.context.intType(32);
1187 const indices: [2]*const llvm.Value = .{1204 const indices: [2]*const llvm.Value = .{
1188 index_type.constNull(),1205 index_type.constNull(),
src/codegen/wasm.zig+1
...@@ -1016,6 +1016,7 @@ pub const Context = struct {...@@ -1016,6 +1016,7 @@ pub const Context = struct {
1016 .Pointer => {1016 .Pointer => {
1017 if (val.castTag(.decl_ref)) |payload| {1017 if (val.castTag(.decl_ref)) |payload| {
1018 const decl = payload.data;1018 const decl = payload.data;
1019 decl.alive = true;
10191020
1020 // offset into the offset table within the 'data' section1021 // offset into the offset table within the 'data' section
1021 const ptr_width = self.target.cpu.arch.ptrBitWidth() / 8;1022 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 {...@@ -224,7 +224,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
224224
225 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;225 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());
228 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;230 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
229 var got_table = try self.base.allocator.alloc(u8, got_size);231 var got_table = try self.base.allocator.alloc(u8, got_size);
230 defer self.base.allocator.free(got_table);232 defer self.base.allocator.free(got_table);
...@@ -358,11 +360,18 @@ fn addDeclExports(...@@ -358,11 +360,18 @@ fn addDeclExports(
358}360}
359361
360pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {362pub 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.
361 const is_fn = (decl.ty.zigTypeTag() == .Fn);369 const is_fn = (decl.ty.zigTypeTag() == .Fn);
362 if (is_fn)370 if (is_fn) {
363 assert(self.fn_decl_table.swapRemove(decl))371 _ = self.fn_decl_table.swapRemove(decl);
364 else372 } else {
365 assert(self.data_decl_table.swapRemove(decl));373 _ = self.data_decl_table.swapRemove(decl);
374 }
366}375}
367376
368pub fn updateDeclExports(377pub fn updateDeclExports(
src/value.zig+33-12
...@@ -103,6 +103,7 @@ pub const Value = extern union {...@@ -103,6 +103,7 @@ pub const Value = extern union {
103 /// Represents a comptime variables storage.103 /// Represents a comptime variables storage.
104 comptime_alloc,104 comptime_alloc,
105 /// Represents a pointer to a decl, not the value of the decl.105 /// 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.
106 decl_ref,107 decl_ref,
107 elem_ptr,108 elem_ptr,
108 field_ptr,109 field_ptr,
...@@ -1346,28 +1347,48 @@ pub const Value = extern union {...@@ -1346,28 +1347,48 @@ pub const Value = extern union {
13461347
1347 /// Asserts the value is a pointer and dereferences it.1348 /// Asserts the value is a pointer and dereferences it.
1348 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.1349 /// 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 pub fn pointerDeref(
1350 return switch (self.tag()) {1351 self: Value,
1352 allocator: *Allocator,
1353 ) error{ AnalysisFail, OutOfMemory }!?Value {
1354 const sub_val: Value = switch (self.tag()) {
1351 .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,1355 .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,
1352 .decl_ref => self.castTag(.decl_ref).?.data.value(),1356 .decl_ref => try self.castTag(.decl_ref).?.data.value(),
1353 .elem_ptr => {1357 .elem_ptr => blk: {
1354 const elem_ptr = self.castTag(.elem_ptr).?.data;1358 const elem_ptr = self.castTag(.elem_ptr).?.data;
1355 const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);1359 const array_val = (try elem_ptr.array_ptr.pointerDeref(allocator)) orelse return null;
1356 return array_val.elemValue(allocator, elem_ptr.index);1360 break :blk try array_val.elemValue(allocator, elem_ptr.index);
1357 },1361 },
1358 .field_ptr => {1362 .field_ptr => blk: {
1359 const field_ptr = self.castTag(.field_ptr).?.data;1363 const field_ptr = self.castTag(.field_ptr).?.data;
1360 const container_val = try field_ptr.container_ptr.pointerDeref(allocator);1364 const container_val = (try field_ptr.container_ptr.pointerDeref(allocator)) orelse return null;
1361 return container_val.fieldValue(allocator, field_ptr.field_index);1365 break :blk try container_val.fieldValue(allocator, field_ptr.field_index);
1362 },1366 },
1363 .eu_payload_ptr => {1367 .eu_payload_ptr => blk: {
1364 const err_union_ptr = self.castTag(.eu_payload_ptr).?.data;1368 const err_union_ptr = self.castTag(.eu_payload_ptr).?.data;
1365 const err_union_val = try err_union_ptr.pointerDeref(allocator);1369 const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null;
1366 return err_union_val.castTag(.error_union).?.data;1370 break :blk err_union_val.castTag(.error_union).?.data;
1367 },1371 },
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
1369 else => unreachable,1384 else => unreachable,
1370 };1385 };
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;
1371 }1392 }
13721393
1373 pub fn sliceLen(val: Value) u64 {1394 pub fn sliceLen(val: Value) u64 {
test/stage2/cbe.zig+1-1
...@@ -49,7 +49,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -49,7 +49,7 @@ pub fn addCases(ctx: *TestContext) !void {
49 \\export fn foo() callconv(y) c_int {49 \\export fn foo() callconv(y) c_int {
50 \\ return 0;50 \\ return 0;
51 \\}51 \\}
52 \\var y: i32 = 1234;52 \\var y: @import("std").builtin.CallingConvention = .C;
53 , &.{53 , &.{
54 ":2:22: error: unable to resolve comptime value",54 ":2:22: error: unable to resolve comptime value",
55 ":5:26: error: unable to resolve comptime value",55 ":5:26: error: unable to resolve comptime value",