diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2024-12-23 20:39:19 +0000 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2024-12-24 02:18:41 +0000 |
| commit | 3afda4322c34dedc2319701fdfac3505c8d311e9 (patch) | |
| tree | 467873c408750cb4223f3ccf31775e42ec9fbd5c /src/codegen | |
| parent | 40aafcd6a85d3c517f445f17149c17523c832420 (diff) | |
| download | zig-3afda4322c34dedc2319701fdfac3505c8d311e9.tar.gz zig-3afda4322c34dedc2319701fdfac3505c8d311e9.zip | |
compiler: analyze type and value of global declaration separately
This commit separates semantic analysis of the annotated type vs value
of a global declaration, therefore allowing recursive and mutually
recursive values to be declared.
Every `Nav` which undergoes analysis now has *two* corresponding
`AnalUnit`s: `.{ .nav_val = n }` and `.{ .nav_ty = n }`. The `nav_val`
unit is responsible for *fully resolving* the `Nav`: determining its
value, linksection, addrspace, etc. The `nav_ty` unit, on the other
hand, resolves only the information necessary to construct a *pointer*
to the `Nav`: its type, addrspace, etc. (It does also analyze its
linksection, but that could be moved to `nav_val` I think; it doesn't
make any difference).
Analyzing a `nav_ty` for a declaration with no type annotation will just
mark a dependency on the `nav_val`, analyze it, and finish. Conversely,
analyzing a `nav_val` for a declaration *with* a type annotation will
first mark a dependency on the `nav_ty` and analyze it, using this as
the result type when evaluating the value body.
The `nav_val` and `nav_ty` units always have references to one another:
so, if a `Nav`'s type is referenced, its value implicitly is too, and
vice versa. However, these dependencies are trivial, so, to save memory,
are only known implicitly by logic in `resolveReferences`.
In general, analyzing ZIR `decl_val` will only analyze `nav_ty` of the
corresponding `Nav`. There are two exceptions to this. If the
declaration is an `extern` declaration, then we immediately ensure the
`Nav` value is resolved (which doesn't actually require any more
analysis, since such a declaration has no value body anyway).
Additionally, if the resolved type has type tag `.@"fn"`, we again
immediately resolve the `Nav` value. The latter restriction is in place
for two reasons:
* Functions are special, in that their externs are allowed to trivially
alias; i.e. with a declaration `extern fn foo(...)`, you can write
`const bar = foo;`. This is not allowed for non-function externs, and
it means that function types are the only place where it is possible
for a declaration `Nav` to have a `.@"extern"` value without actually
being declared `extern`. We need to identify this situation
immediately so that the `decl_ref` can create a pointer to the *real*
extern `Nav`, not this alias.
* In certain situations, such as taking a pointer to a `Nav`, Sema needs
to queue analysis of a runtime function if the value is a function. To
do this, the function value needs to be known, so we need to resolve
the value immediately upon `&foo` where `foo` is a function.
This restriction is simple to codify into the eventual language
specification, and doesn't limit the utility of this feature in
practice.
A consequence of this commit is that codegen and linking logic needs to
be more careful when looking at `Nav`s. In general:
* When `updateNav` or `updateFunc` is called, it is safe to assume that
the `Nav` being updated (the owner `Nav` for `updateFunc`) is fully
resolved.
* Any `Nav` whose value is/will be an `@"extern"` or a function is fully
resolved; see `Nav.getExtern` for a helper for a common case here.
* Any other `Nav` may only have its type resolved.
This didn't seem to be too tricky to satisfy in any of the existing
codegen/linker backends.
Resolves: #131
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 61 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 64 | ||||
| -rw-r--r-- | src/codegen/spirv.zig | 29 |
3 files changed, 76 insertions, 78 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c3e3c7fbdc..2368f202da 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -770,11 +770,14 @@ pub const DeclGen = struct { const ctype_pool = &dg.ctype_pool; // Chase function values in order to be able to reference the original function. - const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) { - .variable => |variable| variable.owner_nav, - .func => |func| func.owner_nav, - .@"extern" => |@"extern"| @"extern".owner_nav, - else => nav_index, + const owner_nav = switch (ip.getNav(nav_index).status) { + .unresolved => unreachable, + .type_resolved => nav_index, // this can't be an extern or a function + .fully_resolved => |r| switch (ip.indexToKey(r.val)) { + .func => |f| f.owner_nav, + .@"extern" => |e| e.owner_nav, + else => nav_index, + }, }; // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. @@ -2237,7 +2240,7 @@ pub const DeclGen = struct { Type.fromInterned(nav.typeOf(ip)), .{ .nav = nav_index }, CQualifiers.init(.{ .@"const" = flags.is_const }), - nav.status.resolved.alignment, + nav.getAlignment(), .complete, ); try fwd.writeAll(";\n"); @@ -2246,19 +2249,19 @@ pub const DeclGen = struct { fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void { const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; - switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) { - .@"extern" => |@"extern"| try writer.print("{ }", .{ + const nav = ip.getNav(nav_index); + if (nav.getExtern(ip)) |@"extern"| { + try writer.print("{ }", .{ fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), - }), - else => { - // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), - // expand to 3x the length of its input, but let's cut it off at a much shorter limit. - const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); - try writer.print("{}__{d}", .{ - fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]), - @intFromEnum(nav_index), - }); - }, + }); + } else { + // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), + // expand to 3x the length of its input, but let's cut it off at a much shorter limit. + const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); + try writer.print("{}__{d}", .{ + fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]), + @intFromEnum(nav_index), + }); } } @@ -2826,7 +2829,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn const fwd = o.dg.fwdDeclWriter(); try fwd.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).status.resolved.alignment, .forward, .{ + try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{ .fmt_ctype_pool_string = fn_name, }); try fwd.writeAll(";\n"); @@ -2867,13 +2870,13 @@ pub fn genFunc(f: *Function) !void { try o.dg.renderFunctionSignature( fwd, nav_val, - nav.status.resolved.alignment, + nav.status.fully_resolved.alignment, .forward, .{ .nav = nav_index }, ); try fwd.writeAll(";\n"); - if (nav.status.resolved.@"linksection".toSlice(ip)) |s| + if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderFunctionSignature( o.writer(), @@ -2952,7 +2955,7 @@ pub fn genDecl(o: *Object) !void { const nav_ty = Type.fromInterned(nav.typeOf(ip)); if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return; - switch (ip.indexToKey(nav.status.resolved.val)) { + switch (ip.indexToKey(nav.status.fully_resolved.val)) { .@"extern" => |@"extern"| { if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{ .is_extern = true, @@ -2965,8 +2968,8 @@ pub fn genDecl(o: *Object) !void { try fwd.writeAll("zig_extern "); try o.dg.renderFunctionSignature( fwd, - Value.fromInterned(nav.status.resolved.val), - nav.status.resolved.alignment, + Value.fromInterned(nav.status.fully_resolved.val), + nav.status.fully_resolved.alignment, .forward, .{ .@"export" = .{ .main_name = nav.name, @@ -2985,14 +2988,14 @@ pub fn genDecl(o: *Object) !void { const w = o.writer(); if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage "); if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); - if (nav.status.resolved.@"linksection".toSlice(&zcu.intern_pool)) |s| + if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s| try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderTypeAndName( w, nav_ty, .{ .nav = o.dg.pass.nav }, .{}, - nav.status.resolved.alignment, + nav.status.fully_resolved.alignment, .complete, ); try w.writeAll(" = "); @@ -3002,10 +3005,10 @@ pub fn genDecl(o: *Object) !void { }, else => try genDeclValue( o, - Value.fromInterned(nav.status.resolved.val), + Value.fromInterned(nav.status.fully_resolved.val), .{ .nav = o.dg.pass.nav }, - nav.status.resolved.alignment, - nav.status.resolved.@"linksection", + nav.status.fully_resolved.alignment, + nav.status.fully_resolved.@"linksection", ), } } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 154a7114cf..5b36644019 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1476,7 +1476,7 @@ pub const Object = struct { } }, &o.builder); } - if (nav.status.resolved.@"linksection".toSlice(ip)) |section| + if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |section| function_index.setSection(try o.builder.string(section), &o.builder); var deinit_wip = true; @@ -1684,7 +1684,7 @@ pub const Object = struct { const file = try o.getDebugFile(file_scope); const line_number = zcu.navSrcLine(func.owner_nav) + 1; - const is_internal_linkage = ip.indexToKey(nav.status.resolved.val) != .@"extern"; + const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern"; const debug_decl_type = try o.lowerDebugType(fn_ty); const subprogram = try o.builder.debugSubprogram( @@ -2928,9 +2928,7 @@ pub const Object = struct { const gpa = o.gpa; const nav = ip.getNav(nav_index); const owner_mod = zcu.navFileScope(nav_index).mod; - const resolved = nav.status.resolved; - const val = Value.fromInterned(resolved.val); - const ty = val.typeOf(zcu); + const ty: Type = .fromInterned(nav.typeOf(ip)); const gop = try o.nav_map.getOrPut(gpa, nav_index); if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function; @@ -2938,14 +2936,14 @@ pub const Object = struct { const target = owner_mod.resolved_target.result; const sret = firstParamSRet(fn_info, zcu, target); - const is_extern, const lib_name = switch (ip.indexToKey(val.toIntern())) { - .@"extern" => |@"extern"| .{ true, @"extern".lib_name }, - else => .{ false, .none }, - }; + const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"| + .{ true, @"extern".lib_name } + else + .{ false, .none }; const function_index = try o.builder.addFunction( try o.lowerType(ty), try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)), - toLlvmAddressSpace(resolved.@"addrspace", target), + toLlvmAddressSpace(nav.getAddrspace(), target), ); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; @@ -3063,8 +3061,8 @@ pub const Object = struct { } } - if (resolved.alignment != .none) - function_index.setAlignment(resolved.alignment.toLlvm(), &o.builder); + if (nav.getAlignment() != .none) + function_index.setAlignment(nav.getAlignment().toLlvm(), &o.builder); // Function attributes that are independent of analysis results of the function body. try o.addCommonFnAttributes( @@ -3249,17 +3247,21 @@ pub const Object = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); - const resolved = nav.status.resolved; - const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) { - .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false }, - .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import }, - else => .{ false, false, false, false }, + const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) { + .unresolved => unreachable, + .fully_resolved => |r| switch (ip.indexToKey(r.val)) { + .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false }, + .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import }, + else => .{ false, false, false, false }, + }, + // This means it's a source declaration which is not `extern`! + .type_resolved => |r| .{ false, r.is_threadlocal, false, false }, }; const variable_index = try o.builder.addVariable( try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)), try o.lowerType(Type.fromInterned(nav.typeOf(ip))), - toLlvmGlobalAddressSpace(resolved.@"addrspace", zcu.getTarget()), + toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()), ); gop.value_ptr.* = variable_index.ptrConst(&o.builder).global; @@ -4528,20 +4530,10 @@ pub const Object = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; - // In the case of something like: - // fn foo() void {} - // const bar = foo; - // ... &bar; - // `bar` is just an alias and we actually want to lower a reference to `foo`. - const owner_nav_index = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) { - .func => |func| func.owner_nav, - .@"extern" => |@"extern"| @"extern".owner_nav, - else => nav_index, - }; - const owner_nav = ip.getNav(owner_nav_index); + const nav = ip.getNav(nav_index); - const nav_ty = Type.fromInterned(owner_nav.typeOf(ip)); - const ptr_ty = try pt.navPtrType(owner_nav_index); + const nav_ty = Type.fromInterned(nav.typeOf(ip)); + const ptr_ty = try pt.navPtrType(nav_index); const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or @@ -4551,13 +4543,13 @@ pub const Object = struct { } const llvm_global = if (is_fn_body) - (try o.resolveLlvmFunction(owner_nav_index)).ptrConst(&o.builder).global + (try o.resolveLlvmFunction(nav_index)).ptrConst(&o.builder).global else - (try o.resolveGlobalNav(owner_nav_index)).ptrConst(&o.builder).global; + (try o.resolveGlobalNav(nav_index)).ptrConst(&o.builder).global; const llvm_val = try o.builder.convConst( llvm_global.toConst(), - try o.builder.ptrType(toLlvmAddressSpace(owner_nav.status.resolved.@"addrspace", zcu.getTarget())), + try o.builder.ptrType(toLlvmAddressSpace(nav.getAddrspace(), zcu.getTarget())), ); return o.builder.convConst(llvm_val, try o.lowerType(ptr_ty)); @@ -4799,7 +4791,7 @@ pub const NavGen = struct { const ip = &zcu.intern_pool; const nav_index = ng.nav_index; const nav = ip.getNav(nav_index); - const resolved = nav.status.resolved; + const resolved = nav.status.fully_resolved; const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) { .variable => |variable| .{ false, .none, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav }, @@ -5765,7 +5757,7 @@ pub const FuncGen = struct { const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?; const msg_nav = ip.getNav(msg_nav_index); const msg_len = Type.fromInterned(msg_nav.typeOf(ip)).childType(zcu).arrayLen(zcu); - const msg_ptr = try o.lowerValue(msg_nav.status.resolved.val); + const msg_ptr = try o.lowerValue(msg_nav.status.fully_resolved.val); const null_opt_addr_global = try fg.resolveNullOptUsize(); const target = zcu.getTarget(); const llvm_usize = try o.lowerType(Type.usize); diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 16b4a6dfbd..91e2c4f7e7 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -268,7 +268,7 @@ pub const Object = struct { // TODO: Extern fn? const kind: SpvModule.Decl.Kind = if (ip.isFunctionType(nav.typeOf(ip))) .func - else switch (nav.status.resolved.@"addrspace") { + else switch (nav.getAddrspace()) { .generic => .invocation_global, else => .global, }; @@ -1279,17 +1279,20 @@ const NavGen = struct { const ip = &zcu.intern_pool; const ty_id = try self.resolveType(ty, .direct); const nav = ip.getNav(nav_index); - const nav_val = zcu.navValue(nav_index); - const nav_ty = nav_val.typeOf(zcu); - - switch (ip.indexToKey(nav_val.toIntern())) { - .func => { - // TODO: Properly lower function pointers. For now we are going to hack around it and - // just generate an empty pointer. Function pointers are represented by a pointer to usize. - return try self.spv.constUndef(ty_id); + const nav_ty: Type = .fromInterned(nav.typeOf(ip)); + + switch (nav.status) { + .unresolved => unreachable, + .type_resolved => {}, // this is not a function or extern + .fully_resolved => |r| switch (ip.indexToKey(r.val)) { + .func => { + // TODO: Properly lower function pointers. For now we are going to hack around it and + // just generate an empty pointer. Function pointers are represented by a pointer to usize. + return try self.spv.constUndef(ty_id); + }, + .@"extern" => if (ip.isFunctionType(nav_ty.toIntern())) @panic("TODO"), + else => {}, }, - .@"extern" => assert(!ip.isFunctionType(nav_ty.toIntern())), // TODO - else => {}, } if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { @@ -1305,7 +1308,7 @@ const NavGen = struct { .global, .invocation_global => spv_decl.result_id, }; - const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace"); + const storage_class = self.spvStorageClass(nav.getAddrspace()); try self.addFunctionDep(spv_decl_index, storage_class); const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class); @@ -3182,7 +3185,7 @@ const NavGen = struct { }; assert(maybe_init_val == null); // TODO - const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace"); + const storage_class = self.spvStorageClass(nav.getAddrspace()); assert(storage_class != .Generic); // These should be instance globals const ptr_ty_id = try self.ptrType(ty, storage_class); |
