Commit 8c6175c134
Changed files (7)
src
test
behavior
src/arch/wasm/CodeGen.zig
@@ -1047,7 +1047,7 @@ fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerErro
const offset = @intCast(u32, self.code.items.len);
const atom = &self.decl.link.wasm;
const target_sym_index = decl.link.wasm.sym_index;
- decl.alive = true;
+ markDeclAlive(decl);
if (decl.ty.zigTypeTag() == .Fn) {
// We found a function pointer, so add it to our table,
// as function pointers are not allowed to be stored inside the data section,
@@ -1876,7 +1876,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
try self.emitConstant(slice.data.len, Type.usize);
} else if (val.castTag(.decl_ref)) |payload| {
const decl = payload.data;
- decl.alive = true;
+ markDeclAlive(decl);
// Function pointers use a table index, rather than a memory address
if (decl.ty.zigTypeTag() == .Fn) {
const target_sym_index = decl.link.wasm.sym_index;
@@ -1985,6 +1985,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
}
}
+fn markDeclAlive(decl: *Decl) void {
+ if (decl.alive) return;
+ decl.alive = true;
+
+ // This is the first time we are marking this Decl alive. We must
+ // therefore recurse into its value and mark any Decl it references
+ // as also alive, so that any Decl referenced does not get garbage collected.
+
+ if (decl.val.pointerDecl()) |pointee| {
+ return markDeclAlive(pointee);
+ }
+}
+
fn emitUndefined(self: *Self, ty: Type) InnerError!void {
switch (ty.zigTypeTag()) {
.Int => switch (ty.intInfo(self.target).bits) {
src/codegen/c.zig
@@ -195,7 +195,7 @@ pub const DeclGen = struct {
val: Value,
decl: *Decl,
) error{ OutOfMemory, AnalysisFail }!void {
- decl.alive = true;
+ markDeclAlive(decl);
if (ty.isSlice()) {
try writer.writeByte('(');
@@ -227,6 +227,19 @@ pub const DeclGen = struct {
try dg.renderDeclName(decl, writer);
}
+ fn markDeclAlive(decl: *Decl) void {
+ if (decl.alive) return;
+ decl.alive = true;
+
+ // This is the first time we are marking this Decl alive. We must
+ // therefore recurse into its value and mark any Decl it references
+ // as also alive, so that any Decl referenced does not get garbage collected.
+
+ if (decl.val.pointerDecl()) |pointee| {
+ return markDeclAlive(pointee);
+ }
+ }
+
fn renderInt128(
writer: anytype,
int_val: anytype,
src/codegen/llvm.zig
@@ -749,7 +749,6 @@ pub const DeclGen = struct {
fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type {
const gpa = dg.gpa;
- log.debug("llvmType for {}", .{t});
switch (t.zigTypeTag()) {
.Void, .NoReturn => return dg.context.voidType(),
.Int => {
@@ -1168,7 +1167,7 @@ pub const DeclGen = struct {
.decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
.variable => {
const decl = tv.val.castTag(.variable).?.data.owner_decl;
- decl.alive = true;
+ dg.markDeclAlive(decl);
const val = try dg.resolveGlobalDecl(decl);
const llvm_var_type = try dg.llvmType(tv.ty);
const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
@@ -1317,7 +1316,7 @@ pub const DeclGen = struct {
.function => tv.val.castTag(.function).?.data.owner_decl,
else => unreachable,
};
- fn_decl.alive = true;
+ dg.markDeclAlive(fn_decl);
return dg.resolveLlvmFunction(fn_decl);
},
.ErrorSet => {
@@ -1625,7 +1624,7 @@ pub const DeclGen = struct {
ptr_val: Value,
decl: *Module.Decl,
) Error!ParentPtr {
- decl.alive = true;
+ dg.markDeclAlive(decl);
var ptr_ty_payload: Type.Payload.ElemType = .{
.base = .{ .tag = .single_mut_pointer },
.data = decl.ty,
@@ -1707,7 +1706,7 @@ pub const DeclGen = struct {
return self.lowerPtrToVoid(tv.ty);
}
- decl.alive = true;
+ self.markDeclAlive(decl);
const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
try self.resolveLlvmFunction(decl)
@@ -1718,6 +1717,24 @@ pub const DeclGen = struct {
return llvm_val.constBitCast(llvm_type);
}
+ fn markDeclAlive(dg: *DeclGen, decl: *Module.Decl) void {
+ if (decl.alive) return;
+ decl.alive = true;
+
+ log.debug("{*} ({s}) marked alive by {*} ({s})", .{
+ decl, decl.name,
+ dg.decl, dg.decl.name,
+ });
+
+ // This is the first time we are marking this Decl alive. We must
+ // therefore recurse into its value and mark any Decl it references
+ // as also alive, so that any Decl referenced does not get garbage collected.
+
+ if (decl.val.pointerDecl()) |pointee| {
+ return dg.markDeclAlive(pointee);
+ }
+ }
+
fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
const target = dg.module.getTarget();
const alignment = ptr_ty.ptrAlignment(target);
src/codegen.zig
@@ -464,7 +464,7 @@ fn lowerDeclRef(
}
if (decl.analysis != .complete) return error.AnalysisFail;
- decl.alive = true;
+ markDeclAlive(decl);
// 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);
@@ -478,3 +478,16 @@ fn lowerDeclRef(
return Result{ .appended = {} };
}
+
+fn markDeclAlive(decl: *Module.Decl) void {
+ if (decl.alive) return;
+ decl.alive = true;
+
+ // This is the first time we are marking this Decl alive. We must
+ // therefore recurse into its value and mark any Decl it references
+ // as also alive, so that any Decl referenced does not get garbage collected.
+
+ if (decl.val.pointerDecl()) |pointee| {
+ return markDeclAlive(pointee);
+ }
+}
src/Sema.zig
@@ -2427,7 +2427,57 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
if (var_is_mut) {
try sema.validateVarType(block, ty_src, final_elem_ty, false);
+ } else ct: {
+ // Detect if the value is comptime known. In such case, the
+ // last 3 AIR instructions of the block will look like this:
+ //
+ // %a = constant
+ // %b = bitcast(%a)
+ // %c = store(%b, %d)
+ //
+ // If `%d` is comptime-known, then we want to store the value
+ // inside an anonymous Decl and then erase these three AIR
+ // instructions from the block, replacing the inst_map entry
+ // corresponding to the ZIR alloc instruction with a constant
+ // decl_ref pointing at our new Decl.
+ if (block.instructions.items.len < 3) break :ct;
+ // zig fmt: off
+ const const_inst = block.instructions.items[block.instructions.items.len - 3];
+ const bitcast_inst = block.instructions.items[block.instructions.items.len - 2];
+ const store_inst = block.instructions.items[block.instructions.items.len - 1];
+ const air_tags = sema.air_instructions.items(.tag);
+ const air_datas = sema.air_instructions.items(.data);
+ if (air_tags[const_inst] != .constant) break :ct;
+ if (air_tags[bitcast_inst] != .bitcast ) break :ct;
+ if (air_tags[store_inst] != .store ) break :ct;
+ // zig fmt: on
+ const store_op = air_datas[store_inst].bin_op;
+ const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct;
+ if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
+ if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;
+
+ const bitcast_ty_ref = air_datas[bitcast_inst].ty_op.ty;
+
+ const new_decl = d: {
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+ const new_decl = try anon_decl.finish(
+ try final_elem_ty.copy(anon_decl.arena()),
+ try store_val.copy(anon_decl.arena()),
+ );
+ break :d new_decl;
+ };
+ try sema.mod.declareDeclDependency(sema.owner_decl, new_decl);
+
+ // Even though we reuse the constant instruction, we still remove it from the
+ // block so that codegen does not see it.
+ block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);
+ sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl);
+ air_datas[ptr_inst].ty_pl.ty = bitcast_ty_ref;
+
+ return;
}
+
// Change it to a normal alloc.
const final_ptr_ty = try Type.ptr(sema.arena, .{
.pointee_type = final_elem_ty,
test/behavior/struct_llvm.zig
@@ -285,3 +285,23 @@ fn getB(data: *const BitField1) u3 {
fn getC(data: *const BitField1) u2 {
return data.c;
}
+
+test "default struct initialization fields" {
+ const S = struct {
+ a: i32 = 1234,
+ b: i32,
+ };
+ const x = S{
+ .b = 5,
+ };
+ var five: i32 = 5;
+ const y = S{
+ .b = five,
+ };
+ if (x.a + x.b != 1239) {
+ @compileError("it should be comptime known");
+ }
+ try expect(y.a == x.a);
+ try expect(y.b == x.b);
+ try expect(1239 == x.a + x.b);
+}
test/behavior/struct_stage1.zig
@@ -166,25 +166,6 @@ test "packed struct with fp fields" {
try expectEqual(@as(f32, 20.0), s.data[2]);
}
-test "default struct initialization fields" {
- const S = struct {
- a: i32 = 1234,
- b: i32,
- };
- const x = S{
- .b = 5,
- };
- var five: i32 = 5;
- const y = S{
- .b = five,
- };
- if (x.a + x.b != 1239) {
- @compileError("it should be comptime known");
- }
- try expectEqual(y, x);
- try expectEqual(1239, x.a + x.b);
-}
-
test "fn with C calling convention returns struct by value" {
const S = struct {
fn entry() !void {