aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig10
-rw-r--r--src/Module.zig60
-rw-r--r--src/Sema.zig154
-rw-r--r--src/codegen.zig7
-rw-r--r--src/codegen/c.zig24
-rw-r--r--src/codegen/llvm.zig51
-rw-r--r--src/codegen/wasm.zig1
-rw-r--r--src/link/Plan9.zig19
-rw-r--r--src/value.zig45
9 files changed, 233 insertions, 138 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 8672a346c3..f8f8cea328 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -2061,11 +2061,19 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
.complete, .codegen_failure_retryable => {
if (build_options.omit_stage2)
@panic("sadly stage2 is omitted from this build to save memory on the CI server");
+
const module = self.bin_file.options.module.?;
assert(decl.has_tv);
assert(decl.ty.hasCodeGenBits());
- try module.linkerUpdateDecl(decl);
+ if (decl.alive) {
+ try module.linkerUpdateDecl(decl);
+ continue;
+ }
+
+ // Instead of sending this decl to the linker, we actually will delete it
+ // because we found out that it in fact was never referenced.
+ module.deleteUnusedDecl(decl);
},
},
.codegen_func => |func| switch (func.owner_decl.analysis) {
diff --git a/src/Module.zig b/src/Module.zig
index f89f72bc65..909e54ffa2 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -255,6 +255,15 @@ pub const Decl = struct {
has_align: bool,
/// Whether the ZIR code provides a linksection instruction.
has_linksection: bool,
+ /// Flag used by garbage collection to mark and sweep.
+ /// Decls which correspond to an AST node always have this field set to `true`.
+ /// Anonymous Decls are initialized with this field set to `false` and then it
+ /// is the responsibility of machine code backends to mark it `true` whenever
+ /// a `decl_ref` Value is encountered that points to this Decl.
+ /// When the `codegen_decl` job is encountered in the main work queue, if the
+ /// Decl is marked alive, then it sends the Decl to the linker. Otherwise it
+ /// deletes the Decl on the spot.
+ alive: bool,
/// Represents the position of the code in the output file.
/// This is populated regardless of semantic analysis and code generation.
@@ -2869,6 +2878,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
new_decl.val = struct_val;
new_decl.has_tv = true;
new_decl.owns_tv = true;
+ new_decl.alive = true; // This Decl corresponds to a File and is therefore always alive.
new_decl.analysis = .in_progress;
new_decl.generation = mod.generation;
@@ -2990,6 +3000,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
if (linksection_ref == .none) break :blk Value.initTag(.null_value);
break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
};
+ try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
// We need the memory for the Type to go into the arena for the Decl
var decl_arena = std.heap.ArenaAllocator.init(gpa);
@@ -3027,8 +3038,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
// We don't fully codegen the decl until later, but we do need to reserve a global
- // offset table index for it. This allows us to codegen decls out of dependency order,
- // increasing how many computations can be done in parallel.
+ // offset table index for it. This allows us to codegen decls out of dependency
+ // order, increasing how many computations can be done in parallel.
try mod.comp.bin_file.allocateDeclIndexes(decl);
try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
if (type_changed and mod.emit_h != null) {
@@ -3387,6 +3398,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
new_decl.has_align = has_align;
new_decl.has_linksection = has_linksection;
new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
+ new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
return;
}
gpa.free(decl_name);
@@ -3526,6 +3538,43 @@ pub fn clearDecl(
decl.analysis = .unreferenced;
}
+pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
+ log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name });
+
+ // TODO: remove `allocateDeclIndexes` and make the API that the linker backends
+ // are required to notice the first time `updateDecl` happens and keep track
+ // of it themselves. However they can rely on getting a `freeDecl` call if any
+ // `updateDecl` or `updateFunc` calls happen. This will allow us to avoid any call
+ // into the linker backend here, since the linker backend will never have been told
+ // about the Decl in the first place.
+ // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
+ // must call `freeDecl` in the linker backend now.
+ if (decl.has_tv) {
+ if (decl.ty.hasCodeGenBits()) {
+ mod.comp.bin_file.freeDecl(decl);
+ }
+ }
+
+ const dependants = decl.dependants.keys();
+ assert(dependants[0].namespace.anon_decls.swapRemove(decl));
+
+ for (dependants) |dep| {
+ dep.removeDependency(decl);
+ }
+
+ for (decl.dependencies.keys()) |dep| {
+ dep.removeDependant(decl);
+ }
+ decl.destroy(mod);
+}
+
+pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
+ log.debug("deleteAnonDecl {*} ({s})", .{ decl, decl.name });
+ const scope_decl = scope.ownerDecl().?;
+ assert(scope_decl.namespace.anon_decls.swapRemove(decl));
+ decl.destroy(mod);
+}
+
/// Delete all the Export objects that are caused by this Decl. Re-analysis of
/// this Decl will cause them to be re-created (or not).
fn deleteDeclExports(mod: *Module, decl: *Decl) void {
@@ -3713,6 +3762,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
.is_exported = false,
.has_linksection = false,
.has_align = false,
+ .alive = false,
};
return new_decl;
}
@@ -3802,12 +3852,6 @@ pub fn analyzeExport(
errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
}
-pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
- const scope_decl = scope.ownerDecl().?;
- assert(scope_decl.namespace.anon_decls.swapRemove(decl));
- decl.destroy(mod);
-}
-
/// Takes ownership of `name` even if it returns an error.
pub fn createAnonymousDeclNamed(
mod: *Module,
diff --git a/src/Sema.zig b/src/Sema.zig
index 95baae7e92..0da38d9a76 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -696,7 +696,7 @@ fn resolveMaybeUndefVal(
) CompileError!?Value {
const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
if (val.tag() == .variable) {
- return sema.failWithNeededComptime(block, src);
+ return null;
}
return val;
}
@@ -2917,12 +2917,13 @@ fn zirOptionalPayloadPtr(
const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One);
if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
- const val = try pointer_val.pointerDeref(sema.arena);
- if (val.isNull()) {
- return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
+ if (try pointer_val.pointerDeref(sema.arena)) |val| {
+ if (val.isNull()) {
+ return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
+ }
+ // The same Value represents the pointer to the optional and the payload.
+ return sema.addConstant(child_pointer, pointer_val);
}
- // The same Value represents the pointer to the optional and the payload.
- return sema.addConstant(child_pointer, pointer_val);
}
try sema.requireRuntimeBlock(block, src);
@@ -3027,14 +3028,15 @@ fn zirErrUnionPayloadPtr(
const operand_pointer_ty = try Module.simplePtrType(sema.arena, payload_ty, !operand_ty.isConstPtr(), .One);
if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
- const val = try pointer_val.pointerDeref(sema.arena);
- if (val.getError()) |name| {
- return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
+ if (try pointer_val.pointerDeref(sema.arena)) |val| {
+ if (val.getError()) |name| {
+ return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
+ }
+ return sema.addConstant(
+ operand_pointer_ty,
+ try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
+ );
}
- return sema.addConstant(
- operand_pointer_ty,
- try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
- );
}
try sema.requireRuntimeBlock(block, src);
@@ -3086,10 +3088,11 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
const result_ty = operand_ty.elemType().errorUnionSet();
if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
- const val = try pointer_val.pointerDeref(sema.arena);
- assert(val.getError() != null);
- const data = val.castTag(.error_union).?.data;
- return sema.addConstant(result_ty, data);
+ if (try pointer_val.pointerDeref(sema.arena)) |val| {
+ assert(val.getError() != null);
+ const data = val.castTag(.error_union).?.data;
+ return sema.addConstant(result_ty, data);
+ }
}
try sema.requireRuntimeBlock(block, src);
@@ -4920,10 +4923,13 @@ fn analyzeArithmetic(
log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
return sema.addConstant(scalar_type, value);
+ } else {
+ try sema.requireRuntimeBlock(block, rhs_src);
}
+ } else {
+ try sema.requireRuntimeBlock(block, lhs_src);
}
- try sema.requireRuntimeBlock(block, src);
const air_tag: Air.Inst.Tag = switch (zir_tag) {
.add => .add,
.addwrap => .addwrap,
@@ -6811,16 +6817,10 @@ fn fieldPtr(
if (mem.eql(u8, field_name, "len")) {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
- return sema.addConstant(
- Type.initTag(.single_const_pointer_to_comptime_int),
- try Value.Tag.decl_ref.create(
- arena,
- try anon_decl.finish(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
- ),
- ),
- );
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ Type.initTag(.comptime_int),
+ try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
+ ));
} else {
return mod.fail(
&block.base,
@@ -6867,16 +6867,10 @@ fn fieldPtr(
if (mem.eql(u8, field_name, "len")) {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
- return sema.addConstant(
- Type.initTag(.single_const_pointer_to_comptime_int),
- try Value.Tag.decl_ref.create(
- arena,
- try anon_decl.finish(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
- ),
- ),
- );
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ Type.initTag(.comptime_int),
+ try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
+ ));
} else {
return mod.fail(
&block.base,
@@ -6915,16 +6909,10 @@ fn fieldPtr(
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
- return sema.addConstant(
- try Module.simplePtrType(arena, child_type, false, .One),
- try Value.Tag.decl_ref.create(
- arena,
- try anon_decl.finish(
- child_type,
- try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
- ),
- ),
- );
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ child_type,
+ try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
+ ));
},
.Struct, .Opaque, .Union => {
if (child_type.getNamespace()) |namespace| {
@@ -6971,16 +6959,10 @@ fn fieldPtr(
const field_index_u32 = @intCast(u32, field_index);
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
- return sema.addConstant(
- try Module.simplePtrType(arena, child_type, false, .One),
- try Value.Tag.decl_ref.create(
- arena,
- try anon_decl.finish(
- child_type,
- try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
- ),
- ),
- );
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ child_type,
+ try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
+ ));
},
else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
}
@@ -7671,21 +7653,18 @@ fn analyzeRef(
operand: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
const operand_ty = sema.typeOf(operand);
- const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
if (try sema.resolveMaybeUndefVal(block, src, operand)) |val| {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
- return sema.addConstant(
- ptr_type,
- try Value.Tag.decl_ref.create(
- sema.arena,
- try anon_decl.finish(operand_ty, try val.copy(anon_decl.arena())),
- ),
- );
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ operand_ty,
+ try val.copy(anon_decl.arena()),
+ ));
}
try sema.requireRuntimeBlock(block, src);
+ const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
const alloc = try block.addTy(.alloc, ptr_type);
try sema.storePtr(block, src, alloc, operand);
return alloc;
@@ -7703,11 +7682,10 @@ fn analyzeLoad(
.Pointer => ptr_ty.elemType(),
else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
};
- if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {
- if (ptr_val.tag() == .int_u64)
- break :blk; // do it at runtime
-
- return sema.addConstant(elem_ty, try ptr_val.pointerDeref(sema.arena));
+ if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
+ if (try ptr_val.pointerDeref(sema.arena)) |elem_val| {
+ return sema.addConstant(elem_ty, elem_val);
+ }
}
try sema.requireRuntimeBlock(block, src);
@@ -8215,6 +8193,36 @@ fn resolvePeerTypes(
return sema.typeOf(chosen);
}
+pub fn resolveTypeLayout(
+ sema: *Sema,
+ block: *Scope.Block,
+ src: LazySrcLoc,
+ ty: Type,
+) CompileError!void {
+ switch (ty.zigTypeTag()) {
+ .Pointer => {
+ return sema.resolveTypeLayout(block, src, ty.elemType());
+ },
+ .Struct => {
+ const resolved_ty = try sema.resolveTypeFields(block, src, ty);
+ const struct_obj = resolved_ty.castTag(.@"struct").?.data;
+ switch (struct_obj.status) {
+ .none, .have_field_types => {},
+ .field_types_wip, .layout_wip => {
+ return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
+ },
+ .have_layout => return,
+ }
+ struct_obj.status = .layout_wip;
+ for (struct_obj.fields.values()) |field| {
+ try sema.resolveTypeLayout(block, src, field.ty);
+ }
+ struct_obj.status = .have_layout;
+ },
+ else => {},
+ }
+}
+
fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {
switch (ty.tag()) {
.@"struct" => {
@@ -8222,9 +8230,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
switch (struct_obj.status) {
.none => {},
.field_types_wip => {
- return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{
- ty,
- });
+ return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
},
.have_field_types, .have_layout, .layout_wip => return ty,
}
diff --git a/src/codegen.zig b/src/codegen.zig
index b7ba367d54..d16a87adca 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -184,6 +184,7 @@ pub fn generateSymbol(
if (typed_value.val.castTag(.decl_ref)) |payload| {
const decl = payload.data;
if (decl.analysis != .complete) return error.AnalysisFail;
+ decl.alive = true;
// TODO handle the dependency of this symbol on the decl's vaddr.
// If the decl changes vaddr, then this symbol needs to get regenerated.
const vaddr = bin_file.getDeclVAddr(decl);
@@ -4680,13 +4681,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
},
else => {
if (typed_value.val.castTag(.decl_ref)) |payload| {
+ const decl = payload.data;
+ decl.alive = true;
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const decl = payload.data;
const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
return MCValue{ .memory = got_addr };
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const decl = payload.data;
const got_addr = blk: {
const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
const got = seg.sections.items[macho_file.got_section_index.?];
@@ -4698,11 +4699,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
};
return MCValue{ .memory = got_addr };
} else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
- const decl = payload.data;
const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
return MCValue{ .memory = got_addr };
} else if (self.bin_file.cast(link.File.Plan9)) |p9| {
- const decl = payload.data;
const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
return MCValue{ .memory = got_addr };
} else {
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index a8ec677753..826b73317c 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -262,6 +262,7 @@ pub const DeclGen = struct {
.one => try writer.writeAll("1"),
.decl_ref => {
const decl = val.castTag(.decl_ref).?.data;
+ decl.alive = true;
// Determine if we must pointer cast.
assert(decl.has_tv);
@@ -281,21 +282,7 @@ pub const DeclGen = struct {
const decl = val.castTag(.extern_fn).?.data;
try writer.print("{s}", .{decl.name});
},
- else => switch (t.ptrSize()) {
- .Slice => unreachable,
- .Many => unreachable,
- .One => {
- var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
- defer arena.deinit();
-
- const elem_ty = t.elemType();
- const elem_val = try val.pointerDeref(&arena.allocator);
-
- try writer.writeAll("&");
- try dg.renderValue(writer, elem_ty, elem_val);
- },
- .C => unreachable,
- },
+ else => unreachable,
},
},
.Array => {
@@ -421,6 +408,7 @@ pub const DeclGen = struct {
.one => try writer.writeAll("1"),
.decl_ref => {
const decl = val.castTag(.decl_ref).?.data;
+ decl.alive = true;
// Determine if we must pointer cast.
assert(decl.has_tv);
@@ -433,11 +421,13 @@ pub const DeclGen = struct {
}
},
.function => {
- const func = val.castTag(.function).?.data;
- try writer.print("{s}", .{func.owner_decl.name});
+ const decl = val.castTag(.function).?.data.owner_decl;
+ decl.alive = true;
+ try writer.print("{s}", .{decl.name});
},
.extern_fn => {
const decl = val.castTag(.extern_fn).?.data;
+ decl.alive = true;
try writer.print("{s}", .{decl.name});
},
else => unreachable,
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 0e9a572bea..961ed7ee99 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -673,17 +673,21 @@ pub const DeclGen = struct {
}
fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {
- const llvm_type = try self.llvmType(tv.ty);
-
- if (tv.val.isUndef())
+ if (tv.val.isUndef()) {
+ const llvm_type = try self.llvmType(tv.ty);
return llvm_type.getUndef();
+ }
switch (tv.ty.zigTypeTag()) {
- .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
+ .Bool => {
+ const llvm_type = try self.llvmType(tv.ty);
+ return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull();
+ },
.Int => {
var bigint_space: Value.BigIntSpace = undefined;
const bigint = tv.val.toBigInt(&bigint_space);
+ const llvm_type = try self.llvmType(tv.ty);
if (bigint.eqZero()) return llvm_type.constNull();
if (bigint.limbs.len != 1) {
@@ -698,12 +702,17 @@ pub const DeclGen = struct {
.Pointer => switch (tv.val.tag()) {
.decl_ref => {
const decl = tv.val.castTag(.decl_ref).?.data;
+ decl.alive = true;
const val = try self.resolveGlobalDecl(decl);
+ const llvm_type = try self.llvmType(tv.ty);
return val.constBitCast(llvm_type);
},
.variable => {
- const variable = tv.val.castTag(.variable).?.data;
- const val = try self.resolveGlobalDecl(variable.owner_decl);
+ const decl = tv.val.castTag(.variable).?.data.owner_decl;
+ decl.alive = true;
+ const val = try self.resolveGlobalDecl(decl);
+ const llvm_var_type = try self.llvmType(tv.ty);
+ const llvm_type = llvm_var_type.pointerType(0);
return val.constBitCast(llvm_type);
},
.slice => {
@@ -783,6 +792,7 @@ pub const DeclGen = struct {
.decl_ref => tv.val.castTag(.decl_ref).?.data,
else => unreachable,
};
+ fn_decl.alive = true;
return self.resolveLlvmFunction(fn_decl);
},
.ErrorSet => {
@@ -903,9 +913,7 @@ pub const FuncGen = struct {
return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });
}
const inst_index = Air.refToIndex(inst).?;
- if (self.func_inst_table.get(inst_index)) |value| return value;
-
- return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
+ return self.func_inst_table.get(inst_index).?;
}
fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
@@ -966,8 +974,8 @@ pub const FuncGen = struct {
.struct_field_ptr => try self.airStructFieldPtr(inst),
.struct_field_val => try self.airStructFieldVal(inst),
- .slice_elem_val => try self.airSliceElemVal(inst, false),
- .ptr_slice_elem_val => try self.airSliceElemVal(inst, true),
+ .slice_elem_val => try self.airSliceElemVal(inst),
+ .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
.optional_payload => try self.airOptionalPayload(inst, false),
.optional_payload_ptr => try self.airOptionalPayload(inst, true),
@@ -1170,11 +1178,20 @@ pub const FuncGen = struct {
return self.builder.buildExtractValue(operand, index, "");
}
- fn airSliceElemVal(
- self: *FuncGen,
- inst: Air.Inst.Index,
- operand_is_ptr: bool,
- ) !?*const llvm.Value {
+ fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const base_ptr = self.builder.buildExtractValue(lhs, 0, "");
+ const indices: [1]*const llvm.Value = .{rhs};
+ const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ return self.builder.buildLoad(ptr, "");
+ }
+
+ fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
@@ -1182,7 +1199,7 @@ pub const FuncGen = struct {
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
- const base_ptr = if (!operand_is_ptr) lhs else ptr: {
+ const base_ptr = ptr: {
const index_type = self.context.intType(32);
const indices: [2]*const llvm.Value = .{
index_type.constNull(),
diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig
index 37cc6bc59c..2f1632e0fc 100644
--- a/src/codegen/wasm.zig
+++ b/src/codegen/wasm.zig
@@ -1016,6 +1016,7 @@ pub const Context = struct {
.Pointer => {
if (val.castTag(.decl_ref)) |payload| {
const decl = payload.data;
+ decl.alive = true;
// offset into the offset table within the 'data' section
const ptr_width = self.target.cpu.arch.ptrBitWidth() / 8;
diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig
index 135b59f82b..3b2aae85bc 100644
--- a/src/link/Plan9.zig
+++ b/src/link/Plan9.zig
@@ -224,7 +224,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
- assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count());
+ // TODO I changed this assert from == to >= but this code all needs to be audited; see
+ // the comment in `freeDecl`.
+ assert(self.got_len >= self.fn_decl_table.count() + self.data_decl_table.count());
const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
var got_table = try self.base.allocator.alloc(u8, got_size);
defer self.base.allocator.free(got_table);
@@ -358,11 +360,18 @@ fn addDeclExports(
}
pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
+ // TODO this is not the correct check for being function body,
+ // it could just be a function pointer.
+ // TODO audit the lifetimes of decls table entries. It's possible to get
+ // allocateDeclIndexes and then freeDecl without any updateDecl in between.
+ // However that is planned to change, see the TODO comment in Module.zig
+ // in the deleteUnusedDecl function.
const is_fn = (decl.ty.zigTypeTag() == .Fn);
- if (is_fn)
- assert(self.fn_decl_table.swapRemove(decl))
- else
- assert(self.data_decl_table.swapRemove(decl));
+ if (is_fn) {
+ _ = self.fn_decl_table.swapRemove(decl);
+ } else {
+ _ = self.data_decl_table.swapRemove(decl);
+ }
}
pub fn updateDeclExports(
diff --git a/src/value.zig b/src/value.zig
index d3317ef31d..32be34ee2c 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -103,6 +103,7 @@ pub const Value = extern union {
/// Represents a comptime variables storage.
comptime_alloc,
/// Represents a pointer to a decl, not the value of the decl.
+ /// When machine codegen backend sees this, it must set the Decl's `alive` field to true.
decl_ref,
elem_ptr,
field_ptr,
@@ -1346,28 +1347,48 @@ pub const Value = extern union {
/// Asserts the value is a pointer and dereferences it.
/// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
- pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
- return switch (self.tag()) {
+ pub fn pointerDeref(
+ self: Value,
+ allocator: *Allocator,
+ ) error{ AnalysisFail, OutOfMemory }!?Value {
+ const sub_val: Value = switch (self.tag()) {
.comptime_alloc => self.castTag(.comptime_alloc).?.data.val,
- .decl_ref => self.castTag(.decl_ref).?.data.value(),
- .elem_ptr => {
+ .decl_ref => try self.castTag(.decl_ref).?.data.value(),
+ .elem_ptr => blk: {
const elem_ptr = self.castTag(.elem_ptr).?.data;
- const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
- return array_val.elemValue(allocator, elem_ptr.index);
+ const array_val = (try elem_ptr.array_ptr.pointerDeref(allocator)) orelse return null;
+ break :blk try array_val.elemValue(allocator, elem_ptr.index);
},
- .field_ptr => {
+ .field_ptr => blk: {
const field_ptr = self.castTag(.field_ptr).?.data;
- const container_val = try field_ptr.container_ptr.pointerDeref(allocator);
- return container_val.fieldValue(allocator, field_ptr.field_index);
+ const container_val = (try field_ptr.container_ptr.pointerDeref(allocator)) orelse return null;
+ break :blk try container_val.fieldValue(allocator, field_ptr.field_index);
},
- .eu_payload_ptr => {
+ .eu_payload_ptr => blk: {
const err_union_ptr = self.castTag(.eu_payload_ptr).?.data;
- const err_union_val = try err_union_ptr.pointerDeref(allocator);
- return err_union_val.castTag(.error_union).?.data;
+ const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null;
+ break :blk err_union_val.castTag(.error_union).?.data;
},
+ .zero,
+ .one,
+ .int_u64,
+ .int_i64,
+ .int_big_positive,
+ .int_big_negative,
+ .variable,
+ .extern_fn,
+ .function,
+ => return null,
+
else => unreachable,
};
+ if (sub_val.tag() == .variable) {
+ // This would be loading a runtime value at compile-time so we return
+ // the indicator that this pointer dereference requires being done at runtime.
+ return null;
+ }
+ return sub_val;
}
pub fn sliceLen(val: Value) u64 {