diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-07-29 19:30:37 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-07-29 19:30:37 -0700 |
| commit | 040c6eaaa03bbcfcdeadbe835c1c2f209e9f401e (patch) | |
| tree | a78cb4081572b8e148f983642dedf7d0d1d9bda2 /src/codegen/llvm.zig | |
| parent | a5c6e51f03ab164e64b1a1d8370071dd1e670458 (diff) | |
| download | zig-040c6eaaa03bbcfcdeadbe835c1c2f209e9f401e.tar.gz zig-040c6eaaa03bbcfcdeadbe835c1c2f209e9f401e.zip | |
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.
Diffstat (limited to 'src/codegen/llvm.zig')
| -rw-r--r-- | src/codegen/llvm.zig | 51 |
1 files changed, 34 insertions, 17 deletions
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(), |
