aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2024-06-29 01:36:25 +0100
committermlugg <mlugg@mlugg.co.uk>2024-07-04 21:01:41 +0100
commit7e552dc1e9a8388f71cc32083deb9dd848e79808 (patch)
treeb1c1086002c91be0e6c1195cc18966906a171ce3 /src/Sema.zig
parentbc8cd135987c7dc7419d034ba31178331d606cfa (diff)
downloadzig-7e552dc1e9a8388f71cc32083deb9dd848e79808.tar.gz
zig-7e552dc1e9a8388f71cc32083deb9dd848e79808.zip
Zcu: rework exports
This commit reworks our representation of exported Decls and values in Zcu to be memory-optimized and trivially serialized. All exports are now stored in the `all_exports` array on `Zcu`. An `AnalUnit` which performs an export (either through an `export` annotation or by containing an analyzed `@export`) gains an entry into `single_exports` if it performs only one export, or `multi_exports` if it performs multiple. We no longer store a persistent mapping from a `Decl`/value to all exports of that entity; this state is not necessary for the majority of the pipeline. Instead, we construct it in `Zcu.processExports`, just before flush. This does not affect the algorithmic complexity of `processExports`, since this function already iterates all exports in the `Zcu`. The elimination of `decl_exports` and `value_exports` led to a few non-trivial backend changes. The LLVM backend has been wrangled into a more reasonable state in general regarding exports and externs. The C backend is currently disabled in this commit, because its support for `export` was quite broken, and that was exposed by this work -- I'm hoping @jacobly0 will be able to pick this up!
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig114
1 files changed, 72 insertions, 42 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index df7ad3cd64..fafde99f47 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -117,6 +117,10 @@ maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAll
/// Backed by gpa.
comptime_allocs: std.ArrayListUnmanaged(ComptimeAlloc) = .{},
+/// A list of exports performed by this analysis. After this `Sema` terminates,
+/// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`.
+exports: std.ArrayListUnmanaged(Zcu.Export) = .{},
+
const MaybeComptimeAlloc = struct {
/// The runtime index of the `alloc` instruction.
runtime_index: Value.RuntimeIndex,
@@ -186,6 +190,7 @@ const build_options = @import("build_options");
const Compilation = @import("Compilation.zig");
const InternPool = @import("InternPool.zig");
const Alignment = InternPool.Alignment;
+const AnalUnit = InternPool.AnalUnit;
const ComptimeAllocIndex = InternPool.ComptimeAllocIndex;
pub const default_branch_quota = 1000;
@@ -875,6 +880,7 @@ pub fn deinit(sema: *Sema) void {
sema.base_allocs.deinit(gpa);
sema.maybe_comptime_allocs.deinit(gpa);
sema.comptime_allocs.deinit(gpa);
+ sema.exports.deinit(gpa);
sema.* = undefined;
}
@@ -2735,12 +2741,12 @@ fn maybeRemoveOutdatedType(sema: *Sema, ty: InternPool.Index) !bool {
if (!zcu.comp.debug_incremental) return false;
const decl_index = Type.fromInterned(ty).getOwnerDecl(zcu);
- const decl_as_depender = InternPool.AnalUnit.wrap(.{ .decl = decl_index });
+ const decl_as_depender = AnalUnit.wrap(.{ .decl = decl_index });
const was_outdated = zcu.outdated.swapRemove(decl_as_depender) or
zcu.potentially_outdated.swapRemove(decl_as_depender);
if (!was_outdated) return false;
_ = zcu.outdated_ready.swapRemove(decl_as_depender);
- zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.AnalUnit.wrap(.{ .decl = decl_index }));
+ zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, AnalUnit.wrap(.{ .decl = decl_index }));
zcu.intern_pool.remove(ty);
zcu.declPtr(decl_index).analysis = .dependency_failure;
try zcu.markDependeeOutdated(.{ .decl_val = decl_index });
@@ -2834,7 +2840,7 @@ fn zirStructDecl(
if (sema.mod.comp.debug_incremental) {
try ip.addDependency(
sema.gpa,
- InternPool.AnalUnit.wrap(.{ .decl = new_decl_index }),
+ AnalUnit.wrap(.{ .decl = new_decl_index }),
.{ .src_hash = try ip.trackZir(sema.gpa, block.getFileScope(mod), inst) },
);
}
@@ -3068,7 +3074,7 @@ fn zirEnumDecl(
if (sema.mod.comp.debug_incremental) {
try mod.intern_pool.addDependency(
sema.gpa,
- InternPool.AnalUnit.wrap(.{ .decl = new_decl_index }),
+ AnalUnit.wrap(.{ .decl = new_decl_index }),
.{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
);
}
@@ -3334,7 +3340,7 @@ fn zirUnionDecl(
if (sema.mod.comp.debug_incremental) {
try mod.intern_pool.addDependency(
sema.gpa,
- InternPool.AnalUnit.wrap(.{ .decl = new_decl_index }),
+ AnalUnit.wrap(.{ .decl = new_decl_index }),
.{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
);
}
@@ -3422,7 +3428,7 @@ fn zirOpaqueDecl(
if (sema.mod.comp.debug_incremental) {
try ip.addDependency(
gpa,
- InternPool.AnalUnit.wrap(.{ .decl = new_decl_index }),
+ AnalUnit.wrap(.{ .decl = new_decl_index }),
.{ .src_hash = try ip.trackZir(gpa, block.getFileScope(mod), inst) },
);
}
@@ -6423,10 +6429,9 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.analyzeExport(block, src, options, decl_index);
}
- try addExport(mod, .{
+ try sema.exports.append(mod.gpa, .{
.opts = options,
.src = src,
- .owner_decl = sema.owner_decl_index,
.exported = .{ .value = operand.toIntern() },
.status = .in_progress,
});
@@ -6469,46 +6474,14 @@ pub fn analyzeExport(
try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);
- try addExport(mod, .{
+ try sema.exports.append(gpa, .{
.opts = options,
.src = src,
- .owner_decl = sema.owner_decl_index,
.exported = .{ .decl_index = exported_decl_index },
.status = .in_progress,
});
}
-fn addExport(mod: *Module, export_init: Module.Export) error{OutOfMemory}!void {
- const gpa = mod.gpa;
-
- try mod.decl_exports.ensureUnusedCapacity(gpa, 1);
- try mod.value_exports.ensureUnusedCapacity(gpa, 1);
- try mod.export_owners.ensureUnusedCapacity(gpa, 1);
-
- const new_export = try gpa.create(Module.Export);
- errdefer gpa.destroy(new_export);
-
- new_export.* = export_init;
-
- const eo_gop = mod.export_owners.getOrPutAssumeCapacity(export_init.owner_decl);
- if (!eo_gop.found_existing) eo_gop.value_ptr.* = .{};
- try eo_gop.value_ptr.append(gpa, new_export);
- errdefer _ = eo_gop.value_ptr.pop();
-
- switch (export_init.exported) {
- .decl_index => |decl_index| {
- const de_gop = mod.decl_exports.getOrPutAssumeCapacity(decl_index);
- if (!de_gop.found_existing) de_gop.value_ptr.* = .{};
- try de_gop.value_ptr.append(gpa, new_export);
- },
- .value => |value| {
- const ve_gop = mod.value_exports.getOrPutAssumeCapacity(value);
- if (!ve_gop.found_existing) ve_gop.value_ptr.* = .{};
- try ve_gop.value_ptr.append(gpa, new_export);
- },
- }
-}
-
fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
const mod = sema.mod;
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
@@ -8411,6 +8384,9 @@ fn instantiateGenericCall(
});
sema.appendRefsAssumeCapacity(runtime_args.items);
+ // `child_sema` is owned by us, so just take its exports.
+ try sema.exports.appendSlice(sema.gpa, child_sema.exports.items);
+
if (ensure_result_used) {
try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
}
@@ -35263,6 +35239,8 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.LoadedStructType) Co
const backing_int_ty = try mod.intType(.unsigned, @intCast(fields_bit_sum));
struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
}
+
+ try sema.flushExports();
}
fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
@@ -36225,6 +36203,8 @@ fn semaStructFields(
struct_type.clearTypesWip(ip);
if (!any_inits) struct_type.setHaveFieldInits(ip);
+
+ try sema.flushExports();
}
// This logic must be kept in sync with `semaStructFields`
@@ -36365,6 +36345,8 @@ fn semaStructFieldInits(
struct_type.field_inits.get(ip)[field_i] = default_val.toIntern();
}
}
+
+ try sema.flushExports();
}
fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.LoadedUnionType) CompileError!void {
@@ -36738,6 +36720,8 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded
const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, mod.declPtr(union_type.decl));
union_type.tagTypePtr(ip).* = enum_ty;
}
+
+ try sema.flushExports();
}
fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value {
@@ -38362,7 +38346,7 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
return;
}
- const depender = InternPool.AnalUnit.wrap(
+ const depender = AnalUnit.wrap(
if (sema.owner_func_index != .none)
.{ .func = sema.owner_func_index }
else
@@ -38494,6 +38478,52 @@ fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check:
}
}
+/// This should be called exactly once, at the end of a `Sema`'s lifetime.
+/// It takes the exports stored in `sema.export` and flushes them to the `Zcu`
+/// to be processed by the linker after the update.
+pub fn flushExports(sema: *Sema) !void {
+ if (sema.exports.items.len == 0) return;
+
+ const zcu = sema.mod;
+ const gpa = zcu.gpa;
+
+ const unit: AnalUnit = if (sema.owner_func_index != .none)
+ AnalUnit.wrap(.{ .func = sema.owner_func_index })
+ else
+ AnalUnit.wrap(.{ .decl = sema.owner_decl_index });
+
+ // There may be existing exports. For instance, a struct may export
+ // things during both field type resolution and field default resolution.
+ //
+ // So, pick up and delete any existing exports. This strategy performs
+ // redundant work, but that's okay, because this case is exceedingly rare.
+ if (zcu.single_exports.get(unit)) |export_idx| {
+ try sema.exports.append(gpa, zcu.all_exports.items[export_idx]);
+ } else if (zcu.multi_exports.get(unit)) |info| {
+ try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]);
+ }
+ zcu.deleteUnitExports(unit);
+
+ // `sema.exports` is completed; store the data into the `Zcu`.
+ if (sema.exports.items.len == 1) {
+ try zcu.single_exports.ensureUnusedCapacity(gpa, 1);
+ const export_idx = zcu.free_exports.popOrNull() orelse idx: {
+ _ = try zcu.all_exports.addOne(gpa);
+ break :idx zcu.all_exports.items.len - 1;
+ };
+ zcu.all_exports.items[export_idx] = sema.exports.items[0];
+ zcu.single_exports.putAssumeCapacityNoClobber(unit, @intCast(export_idx));
+ } else {
+ try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);
+ const exports_base = zcu.all_exports.items.len;
+ try zcu.all_exports.appendSlice(gpa, sema.exports.items);
+ zcu.multi_exports.putAssumeCapacityNoClobber(unit, .{
+ .index = @intCast(exports_base),
+ .len = @intCast(sema.exports.items.len),
+ });
+ }
+}
+
pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;
pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;