authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-17 19:34:55+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-18 01:02:29+01:00
log6ae30662dc04d46165bb6c2481d1ee1d0cc60717
treec3670e3e68fc2f4923c10b611216c4a5e9a387e3
parent6f7840d589cd9abce1b8eb5be87288ba3d5ffc00

llvm: incremental updates for "is named enum value" functions

It was fairly straightforward to at least theoretically handle incremental updates to the fields of an enum type correctly: we just build a new set of instructions, and `std.zig.llvm.Builder` already knows how to replace the old function body with the new one when we call `WipFunction.finish`. Also tightened a few error sets.

1 files changed, 67 insertions(+), 58 deletions(-)

src/codegen/llvm.zig+67-58
......@@ -1625,13 +1625,7 @@ pub const Object = struct {
16251625 .pt = pt,
16261626 .err_msg = null,
16271627 };
1628 ng.genDecl() catch |err| switch (err) {
1629 error.CodegenFail => switch (pt.zcu.codegenFailMsg(nav_index, ng.err_msg.?)) {
1630 error.CodegenFail => return,
1631 error.OutOfMemory => |e| return e,
1632 },
1633 else => |e| return e,
1634 };
1628 try ng.genDecl();
16351629 try self.flushTypePool(pt);
16361630 }
16371631
......@@ -1713,10 +1707,7 @@ pub const Object = struct {
17131707 const global_index = variable_index.ptrConst(&o.builder).global;
17141708 gop.value_ptr.* = global_index;
17151709 // This line invalidates `gop`.
1716 const init_val = o.lowerValue(pt, exported_value) catch |err| switch (err) {
1717 error.OutOfMemory => return error.OutOfMemory,
1718 error.CodegenFail => return error.AnalysisFail,
1719 };
1710 const init_val = try o.lowerValue(pt, exported_value);
17201711 try variable_index.setInitializer(init_val, &o.builder);
17211712 break :i global_index;
17221713 };
......@@ -1834,6 +1825,9 @@ pub const Object = struct {
18341825
18351826 pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void {
18361827 try o.type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success);
1828 if (o.named_enum_map.get(ty)) |function_index| {
1829 try o.updateIsNamedEnumValueFunction(pt, .fromInterned(ty), function_index);
1830 }
18371831 }
18381832
18391833 /// Should only be called by the `link.ConstPool` implementation.
......@@ -2907,7 +2901,7 @@ pub const Object = struct {
29072901 uav: InternPool.Index,
29082902 llvm_addr_space: Builder.AddrSpace,
29092903 alignment: InternPool.Alignment,
2910 ) Error!Builder.Variable.Index {
2904 ) Allocator.Error!Builder.Variable.Index {
29112905 assert(alignment != .none);
29122906 // TODO: Add address space to the anon_decl_map
29132907 const gop = try o.uav_map.getOrPut(o.gpa, uav);
......@@ -3506,7 +3500,7 @@ pub const Object = struct {
35063500 );
35073501 }
35083502
3509 fn lowerValue(o: *Object, pt: Zcu.PerThread, arg_val: InternPool.Index) Error!Builder.Constant {
3503 fn lowerValue(o: *Object, pt: Zcu.PerThread, arg_val: InternPool.Index) Allocator.Error!Builder.Constant {
35103504 const zcu = pt.zcu;
35113505 const ip = &zcu.intern_pool;
35123506 const target = zcu.getTarget();
......@@ -4019,7 +4013,7 @@ pub const Object = struct {
40194013 pt: Zcu.PerThread,
40204014 ptr_val: InternPool.Index,
40214015 prev_offset: u64,
4022 ) Error!Builder.Constant {
4016 ) Allocator.Error!Builder.Constant {
40234017 const zcu = pt.zcu;
40244018 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
40254019 const offset: u64 = prev_offset + ptr.byte_offset;
......@@ -4086,7 +4080,7 @@ pub const Object = struct {
40864080 o: *Object,
40874081 pt: Zcu.PerThread,
40884082 uav: InternPool.Key.Ptr.BaseAddr.Uav,
4089 ) Error!Builder.Constant {
4083 ) Allocator.Error!Builder.Constant {
40904084 const zcu = pt.zcu;
40914085 const ip = &zcu.intern_pool;
40924086 const uav_val = uav.val;
......@@ -4363,6 +4357,58 @@ pub const Object = struct {
43634357 const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern());
43644358 return o.lazy_abi_aligns.items[@intFromEnum(index)];
43654359 }
4360
4361 fn updateIsNamedEnumValueFunction(
4362 o: *Object,
4363 pt: Zcu.PerThread,
4364 enum_ty: Type,
4365 function_index: Builder.Function.Index,
4366 ) Allocator.Error!void {
4367 const zcu = pt.zcu;
4368 const builder = &o.builder;
4369 const loaded_enum = zcu.intern_pool.loadEnumType(enum_ty.toIntern());
4370 function_index.ptrConst(builder).global.ptr(builder).type = try builder.fnType(
4371 .i1,
4372 &.{try o.lowerType(pt, .fromInterned(loaded_enum.int_tag_type))},
4373 .normal,
4374 );
4375
4376 var attributes: Builder.FunctionAttributes.Wip = .{};
4377 defer attributes.deinit(builder);
4378 try o.addCommonFnAttributes(&attributes, zcu.root_mod, zcu.root_mod.omit_frame_pointer);
4379
4380 function_index.setLinkage(.internal, builder);
4381 function_index.setCallConv(.fastcc, builder);
4382 function_index.setAttributes(try attributes.finish(builder), builder);
4383
4384 var wip: Builder.WipFunction = try .init(builder, .{
4385 .function = function_index,
4386 .strip = true,
4387 });
4388 defer wip.deinit();
4389 wip.cursor = .{ .block = try wip.block(0, "Entry") };
4390
4391 const named_block = try wip.block(@intCast(loaded_enum.field_names.len), "Named");
4392 const unnamed_block = try wip.block(1, "Unnamed");
4393 const tag_int_value = wip.arg(0);
4394 var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(loaded_enum.field_names.len), .none);
4395 defer wip_switch.finish(&wip);
4396
4397 for (0..loaded_enum.field_names.len) |field_index| {
4398 const this_tag_int_value = try o.lowerValue(
4399 pt,
4400 (try pt.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(),
4401 );
4402 try wip_switch.addCase(this_tag_int_value, named_block, &wip);
4403 }
4404 wip.cursor = .{ .block = named_block };
4405 _ = try wip.ret(.true);
4406
4407 wip.cursor = .{ .block = unnamed_block };
4408 _ = try wip.ret(.false);
4409
4410 try wip.finish();
4411 }
43664412};
43674413
43684414pub const NavGen = struct {
......@@ -10106,56 +10152,19 @@ pub const FuncGen = struct {
1010610152 const pt = self.ng.pt;
1010710153 const zcu = pt.zcu;
1010810154 const ip = &zcu.intern_pool;
10109 const enum_type = ip.loadEnumType(enum_ty.toIntern());
1011010155
10111 // TODO: detect when the type changes (`updateContainerType` will be called) and re-emit this function
1011210156 const gop = try o.named_enum_map.getOrPut(o.gpa, enum_ty.toIntern());
1011310157 if (gop.found_existing) return gop.value_ptr.*;
1011410158 errdefer assert(o.named_enum_map.remove(enum_ty.toIntern()));
10115
10116 const target = &zcu.root_mod.resolved_target.result;
1011710159 const function_index = try o.builder.addFunction(
10118 try o.builder.fnType(.i1, &.{try o.lowerType(pt, Type.fromInterned(enum_type.int_tag_type))}, .normal),
10119 try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_type.name.fmt(ip)}),
10120 toLlvmAddressSpace(.generic, target),
10160 // Dummy function type; `updateIsNamedEnumValue` will replace it with the correct type.
10161 // TODO: change the builder API so we don't need to do this.
10162 try o.builder.fnType(.void, &.{}, .normal),
10163 try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}),
10164 toLlvmAddressSpace(.generic, zcu.getTarget()),
1012110165 );
10122
10123 var attributes: Builder.FunctionAttributes.Wip = .{};
10124 defer attributes.deinit(&o.builder);
10125 try o.addCommonFnAttributes(&attributes, zcu.root_mod, zcu.root_mod.omit_frame_pointer);
10126
10127 function_index.setLinkage(.internal, &o.builder);
10128 function_index.setCallConv(.fastcc, &o.builder);
10129 function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
1013010166 gop.value_ptr.* = function_index;
10131
10132 var wip = try Builder.WipFunction.init(&o.builder, .{
10133 .function = function_index,
10134 .strip = true,
10135 });
10136 defer wip.deinit();
10137 wip.cursor = .{ .block = try wip.block(0, "Entry") };
10138
10139 const named_block = try wip.block(@intCast(enum_type.field_names.len), "Named");
10140 const unnamed_block = try wip.block(1, "Unnamed");
10141 const tag_int_value = wip.arg(0);
10142 var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.field_names.len), .none);
10143 defer wip_switch.finish(&wip);
10144
10145 for (0..enum_type.field_names.len) |field_index| {
10146 const this_tag_int_value = try o.lowerValue(
10147 pt,
10148 (try pt.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(),
10149 );
10150 try wip_switch.addCase(this_tag_int_value, named_block, &wip);
10151 }
10152 wip.cursor = .{ .block = named_block };
10153 _ = try wip.ret(.true);
10154
10155 wip.cursor = .{ .block = unnamed_block };
10156 _ = try wip.ret(.false);
10157
10158 try wip.finish();
10167 try o.updateIsNamedEnumValueFunction(pt, enum_ty, function_index);
1015910168 return function_index;
1016010169 }
1016110170