aboutsummaryrefslogtreecommitdiff
path: root/src/value.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-07-29 19:30:37 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-07-29 19:30:37 -0700
commit040c6eaaa03bbcfcdeadbe835c1c2f209e9f401e (patch)
treea78cb4081572b8e148f983642dedf7d0d1d9bda2 /src/value.zig
parenta5c6e51f03ab164e64b1a1d8370071dd1e670458 (diff)
downloadzig-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/value.zig')
-rw-r--r--src/value.zig45
1 files changed, 33 insertions, 12 deletions
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 {