From c0b55125443cab63945205b2f7c66bf12cae71e1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 24 Sep 2023 19:22:25 -0700 Subject: [PATCH] compiler: start handling anonymous decls differently Instead of explicitly creating a `Module.Decl` object for each anonymous declaration, each `InternPool.Index` value is implicitly understood to be an anonymous declaration when encountered by backend codegen. The memory management strategy for these anonymous decls then becomes to garbage collect them along with standard InternPool garbage. In the interest of a smooth transition, this commit only implements this new scheme for string literals and leaves all the previous mechanisms in place. --- src/InternPool.zig | 44 ++++++++++++++++-- src/Module.zig | 5 +- src/Sema.zig | 97 +++++++++++++++++++++++---------------- src/TypedValue.zig | 9 ++++ src/arch/wasm/CodeGen.zig | 1 + src/codegen.zig | 1 + src/codegen/c.zig | 2 + src/codegen/llvm.zig | 90 +++++++++++++++++++++++++++++++++--- src/codegen/spirv.zig | 1 + src/value.zig | 9 ++-- 10 files changed, 201 insertions(+), 58 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index d7c0e4861d589139683c273cbd396b3ce1ee35fb..14490c21dbb513c78db62b229f201c3956a2cfc7 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1074,6 +1074,7 @@ pub const Key = union(enum) { decl: Module.Decl.Index, mut_decl: MutDecl, + anon_decl: Index, comptime_field: Index, int: Index, eu_payload: Index, @@ -1230,10 +1231,12 @@ pub const Key = union(enum) { asBytes(&x.decl) ++ asBytes(&x.runtime_index), ), - .int, .eu_payload, .opt_payload, .comptime_field => |int| Hash.hash( - seed2, - asBytes(&int), - ), + .anon_decl, + .int, + .eu_payload, + .opt_payload, + .comptime_field, + => |int| Hash.hash(seed2, asBytes(&int)), .elem, .field => |x| Hash.hash( seed2, @@ -1497,6 +1500,7 @@ pub const Key = union(enum) { return switch (a_info.addr) { .decl => |a_decl| a_decl == b_info.addr.decl, .mut_decl => |a_mut_decl| std.meta.eql(a_mut_decl, b_info.addr.mut_decl), + .anon_decl => |a_decl| a_decl == b_info.addr.anon_decl, .int => |a_int| a_int == b_info.addr.int, .eu_payload => |a_eu_payload| a_eu_payload == b_info.addr.eu_payload, .opt_payload => |a_opt_payload| a_opt_payload == b_info.addr.opt_payload, @@ -2123,6 +2127,7 @@ pub const Index = enum(u32) { simple_value: struct { data: SimpleValue }, ptr_decl: struct { data: *PtrDecl }, ptr_mut_decl: struct { data: *PtrMutDecl }, + ptr_anon_decl: struct { data: *PtrAnonDecl }, ptr_comptime_field: struct { data: *PtrComptimeField }, ptr_int: struct { data: *PtrBase }, ptr_eu_payload: struct { data: *PtrBase }, @@ -2572,6 +2577,9 @@ pub const Tag = enum(u8) { /// A pointer to a decl that can be mutated at comptime. /// data is extra index of `PtrMutDecl`, which contains the type and address. ptr_mut_decl, + /// A pointer to an anonymous decl. + /// data is extra index of `PtrAnonDecl`, which contains the type and decl value. + ptr_anon_decl, /// data is extra index of `PtrComptimeField`, which contains the pointer type and field value. ptr_comptime_field, /// A pointer with an integer value. @@ -2767,6 +2775,7 @@ pub const Tag = enum(u8) { .simple_value => unreachable, .ptr_decl => PtrDecl, .ptr_mut_decl => PtrMutDecl, + .ptr_anon_decl => PtrAnonDecl, .ptr_comptime_field => PtrComptimeField, .ptr_int => PtrBase, .ptr_eu_payload => PtrBase, @@ -3364,6 +3373,11 @@ pub const PtrDecl = struct { decl: Module.Decl.Index, }; +pub const PtrAnonDecl = struct { + ty: Index, + val: Index, +}; + pub const PtrMutDecl = struct { ty: Index, decl: Module.Decl.Index, @@ -3713,6 +3727,13 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { } }, } }; }, + .ptr_anon_decl => { + const info = ip.extraData(PtrAnonDecl, data); + return .{ .ptr = .{ + .ty = info.ty, + .addr = .{ .anon_decl = info.val }, + } }; + }, .ptr_comptime_field => { const info = ip.extraData(PtrComptimeField, data); return .{ .ptr = .{ @@ -3790,6 +3811,9 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .runtime_index = sub_info.runtime_index, } }; }, + .ptr_anon_decl => .{ + .anon_decl = ip.extraData(PtrAnonDecl, ptr_item.data).val, + }, .ptr_comptime_field => .{ .comptime_field = ip.extraData(PtrComptimeField, ptr_item.data).field_val, }, @@ -4542,6 +4566,13 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .runtime_index = mut_decl.runtime_index, }), }), + .anon_decl => |anon_decl| ip.items.appendAssumeCapacity(.{ + .tag = .ptr_anon_decl, + .data = try ip.addExtra(gpa, PtrAnonDecl{ + .ty = ptr.ty, + .val = anon_decl, + }), + }), .comptime_field => |field_val| { assert(field_val != .none); ip.items.appendAssumeCapacity(.{ @@ -7147,6 +7178,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { .simple_value => 0, .ptr_decl => @sizeOf(PtrDecl), .ptr_mut_decl => @sizeOf(PtrMutDecl), + .ptr_anon_decl => @sizeOf(PtrAnonDecl), .ptr_comptime_field => @sizeOf(PtrComptimeField), .ptr_int => @sizeOf(PtrBase), .ptr_eu_payload => @sizeOf(PtrBase), @@ -7276,6 +7308,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { .runtime_value, .ptr_decl, .ptr_mut_decl, + .ptr_anon_decl, .ptr_comptime_field, .ptr_int, .ptr_eu_payload, @@ -7656,6 +7689,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { inline .ptr_decl, .ptr_mut_decl, + .ptr_anon_decl, .ptr_comptime_field, .ptr_int, .ptr_eu_payload, @@ -7816,6 +7850,7 @@ pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.Addr.Tag { switch (ip.items.items(.tag)[base]) { .ptr_decl => return .decl, .ptr_mut_decl => return .mut_decl, + .ptr_anon_decl => return .anon_decl, .ptr_comptime_field => return .comptime_field, .ptr_int => return .int, inline .ptr_eu_payload, @@ -7991,6 +8026,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois .simple_value, .ptr_decl, .ptr_mut_decl, + .ptr_anon_decl, .ptr_comptime_field, .ptr_int, .ptr_eu_payload, diff --git a/src/Module.zig b/src/Module.zig index c4c2b634f704da392d7e0dfa17265ede76d6140f..8c4035cc9af78f99f4fd72079202436b53215a85 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -109,9 +109,6 @@ comptime_capture_scopes: std.AutoArrayHashMapUnmanaged(CaptureScope.Key, InternP /// This memory lives until the Module is destroyed. tmp_hack_arena: std.heap.ArenaAllocator, -/// This is currently only used for string literals. -memoized_decls: std.AutoHashMapUnmanaged(InternPool.Index, Decl.Index) = .{}, - /// We optimize memory usage for a compilation with no compile errors by storing the /// error messages and mapping outside of `Decl`. /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. @@ -2627,7 +2624,6 @@ pub fn deinit(mod: *Module) void { mod.global_assembly.deinit(gpa); mod.reference_table.deinit(gpa); - mod.memoized_decls.deinit(gpa); mod.intern_pool.deinit(gpa); mod.tmp_hack_arena.deinit(); @@ -5814,6 +5810,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { .ptr => |ptr| { switch (ptr.addr) { .decl => |decl| try mod.markDeclIndexAlive(decl), + .anon_decl => {}, .mut_decl => |mut_decl| try mod.markDeclIndexAlive(mut_decl.decl), .int, .comptime_field => {}, .eu_payload, .opt_payload => |parent| try mod.markReferencedDeclsAlive(parent.toValue()), diff --git a/src/Sema.zig b/src/Sema.zig index d626de4d18a0871333995328491ab3a9335063b1..c1187c0591bd3ad49dbb2d8c2a491a7a2f0d6a1b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1091,7 +1091,7 @@ fn analyzeBodyInner( .slice_sentinel => try sema.zirSliceSentinel(block, inst), .slice_start => try sema.zirSliceStart(block, inst), .slice_length => try sema.zirSliceLength(block, inst), - .str => try sema.zirStr(block, inst), + .str => try sema.zirStr(inst), .switch_block => try sema.zirSwitchBlock(block, inst, false), .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), .type_info => try sema.zirTypeInfo(block, inst), @@ -2185,7 +2185,7 @@ fn resolveMaybeUndefValIntable(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Va if (val.ip_index == .none) return val; if (sema.mod.intern_pool.isVariable(val.toIntern())) return null; if (sema.mod.intern_pool.getBackingAddrTag(val.toIntern())) |addr| switch (addr) { - .decl, .mut_decl, .comptime_field => return null, + .decl, .anon_decl, .mut_decl, .comptime_field => return null, .int => {}, .eu_payload, .opt_payload, .elem, .field => unreachable, }; @@ -5501,38 +5501,40 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); } -fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - +fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); - return sema.addStrLit(block, bytes); + return sema.addStrLitNoAlias(bytes); } -fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const gpa = sema.gpa; - // TODO: write something like getCoercedInts to avoid needing to dupe +fn addStrLit(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { const duped_bytes = try sema.arena.dupe(u8, bytes); - const ty = try mod.arrayType(.{ + return addStrLitNoAlias(sema, duped_bytes); +} + +/// Safe to call when `bytes` does not point into `InternPool`. +fn addStrLitNoAlias(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const array_ty = try mod.arrayType(.{ .len = bytes.len, .sentinel = .zero_u8, .child = .u8_type, }); const val = try mod.intern(.{ .aggregate = .{ - .ty = ty.toIntern(), - .storage = .{ .bytes = duped_bytes }, + .ty = array_ty.toIntern(), + .storage = .{ .bytes = bytes }, } }); - const gop = try mod.memoized_decls.getOrPut(gpa, val); - if (!gop.found_existing) { - const new_decl_index = try mod.createAnonymousDecl(block, .{ - .ty = ty, - .val = val.toValue(), - }); - gop.value_ptr.* = new_decl_index; - try mod.finalizeAnonDecl(new_decl_index); - } - return sema.analyzeDeclRef(gop.value_ptr.*); + const ptr_ty = try sema.ptrType(.{ + .child = array_ty.toIntern(), + .flags = .{ + .alignment = .none, + .is_const = true, + .address_space = .generic, + }, + }); + return Air.internedToRef((try mod.intern(.{ .ptr = .{ + .ty = ptr_ty.toIntern(), + .addr = .{ .anon_decl = val }, + } }))); } fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -12907,7 +12909,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op try sema.zirSaveErrRetIndex(block, inst); continue; }, - .str => try sema.zirStr(block, inst), + .str => try sema.zirStr(inst), .as_node => try sema.zirAsNode(block, inst), .field_val => try sema.zirFieldVal(block, inst), .@"unreachable" => { @@ -20170,7 +20172,7 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name; - return sema.addStrLit(block, sema.mod.intern_pool.stringToSlice(err_name)); + return sema.addStrLit(sema.mod.intern_pool.stringToSlice(err_name)); } // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass @@ -20288,7 +20290,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .EnumLiteral => { const val = try sema.resolveConstValue(block, .unneeded, operand, undefined); const tag_name = ip.indexToKey(val.toIntern()).enum_literal; - return sema.addStrLit(block, ip.stringToSlice(tag_name)); + return sema.addStrLit(ip.stringToSlice(tag_name)); }, .Enum => operand_ty, .Union => operand_ty.unionTagType(mod) orelse { @@ -20330,7 +20332,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; // TODO: write something like getCoercedInts to avoid needing to dupe const field_name = enum_ty.enumFieldName(field_index, mod); - return sema.addStrLit(block, ip.stringToSlice(field_name)); + return sema.addStrLit(ip.stringToSlice(field_name)); } try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) { @@ -29859,7 +29861,7 @@ fn beginComptimePtrMutation( const mod = sema.mod; const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr; switch (ptr.addr) { - .decl, .int => unreachable, // isComptimeMutablePtr has been checked already + .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already .mut_decl => |mut_decl| { const decl = mod.declPtr(mut_decl.decl); return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl); @@ -30455,9 +30457,10 @@ fn beginComptimePtrLoad( maybe_array_ty: ?Type, ) ComptimePtrLoadError!ComptimePtrLoadKit { const mod = sema.mod; + const ip = &mod.intern_pool; const target = mod.getTarget(); - var deref: ComptimePtrLoadKit = switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { + var deref: ComptimePtrLoadKit = switch (ip.indexToKey(ptr_val.toIntern())) { .ptr => |ptr| switch (ptr.addr) { .decl, .mut_decl => blk: { const decl_index = switch (ptr.addr) { @@ -30478,9 +30481,21 @@ fn beginComptimePtrLoad( .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, }; }, + .anon_decl => |decl_val| blk: { + if (decl_val.toValue().getVariable(mod) != null) return error.RuntimeLoad; + const decl_ty = ip.typeOf(decl_val).toType(); + const decl_tv: TypedValue = .{ .ty = decl_ty, .val = decl_val.toValue() }; + const layout_defined = decl_ty.hasWellDefinedLayout(mod); + break :blk ComptimePtrLoadKit{ + .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, + .pointee = decl_tv, + .is_mutable = false, + .ty_without_well_defined_layout = if (!layout_defined) decl_ty else null, + }; + }, .int => return error.RuntimeLoad, .eu_payload, .opt_payload => |container_ptr| blk: { - const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod); + const container_ty = ip.typeOf(container_ptr).toType().childType(mod); const payload_ty = switch (ptr.addr) { .eu_payload => container_ty.errorUnionPayload(mod), .opt_payload => container_ty.optionalChild(mod), @@ -30502,13 +30517,13 @@ fn beginComptimePtrLoad( const payload_val = switch (tv.val.ip_index) { .none => tv.val.cast(Value.Payload.SubValue).?.data, .null_value => return sema.fail(block, src, "attempt to use null value", .{}), - else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) { + else => switch (ip.indexToKey(tv.val.toIntern())) { .error_union => |error_union| switch (error_union.val) { .err_name => |err_name| return sema.fail( block, src, "attempt to unwrap error: {}", - .{err_name.fmt(&mod.intern_pool)}, + .{err_name.fmt(ip)}, ), .payload => |payload| payload, }, @@ -30527,7 +30542,7 @@ fn beginComptimePtrLoad( break :blk deref; }, .comptime_field => |comptime_field| blk: { - const field_ty = mod.intern_pool.typeOf(comptime_field).toType(); + const field_ty = ip.typeOf(comptime_field).toType(); break :blk ComptimePtrLoadKit{ .parent = null, .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() }, @@ -30536,15 +30551,15 @@ fn beginComptimePtrLoad( }; }, .elem => |elem_ptr| blk: { - const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); + const elem_ty = ip.typeOf(elem_ptr.base).toType().elemType2(mod); var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null); // This code assumes that elem_ptrs have been "flattened" in order for direct dereference // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" - switch (mod.intern_pool.indexToKey(elem_ptr.base)) { + switch (ip.indexToKey(elem_ptr.base)) { .ptr => |base_ptr| switch (base_ptr.addr) { - .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)), + .elem => |base_elem| assert(!ip.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)), else => {}, }, else => {}, @@ -30616,7 +30631,7 @@ fn beginComptimePtrLoad( }, .field => |field_ptr| blk: { const field_index: u32 = @intCast(field_ptr.index); - const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); + const container_ty = ip.typeOf(field_ptr.base).toType().childType(mod); var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty); if (container_ty.hasWellDefinedLayout(mod)) { @@ -30655,7 +30670,7 @@ fn beginComptimePtrLoad( }, Value.slice_len_index => TypedValue{ .ty = Type.usize, - .val = mod.intern_pool.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(), + .val = ip.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(), }, else => unreachable, }; @@ -34529,7 +34544,7 @@ fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(), }; switch (ptr.addr) { - .decl, .mut_decl => return if (resolved_len == ptr.len) + .decl, .mut_decl, .anon_decl => return if (resolved_len == ptr.len) val else (try mod.intern(.{ .ptr = .{ @@ -34537,6 +34552,7 @@ fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { .addr = switch (ptr.addr) { .decl => |decl| .{ .decl = decl }, .mut_decl => |mut_decl| .{ .mut_decl = mut_decl }, + .anon_decl => |anon_decl| .{ .anon_decl = anon_decl }, else => unreachable, }, .len = resolved_len, @@ -36568,6 +36584,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .runtime_value, .simple_value, .ptr_decl, + .ptr_anon_decl, .ptr_mut_decl, .ptr_comptime_field, .ptr_int, diff --git a/src/TypedValue.zig b/src/TypedValue.zig index cef22543a36be16504dd347a6181bf5022eb92c8..3d77d6b8a06b48e84300e95888425309bb960021 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -321,6 +321,15 @@ pub fn print( .val = decl.val, }, writer, level - 1, mod); }, + .anon_decl => |decl_val| { + if (level == 0) return writer.print("(anon decl '{d}')", .{ + @intFromEnum(decl_val), + }); + return print(.{ + .ty = ip.typeOf(decl_val).toType(), + .val = decl_val.toValue(), + }, writer, level - 1, mod); + }, .mut_decl => |mut_decl| { const decl = mod.declPtr(mut_decl.decl); if (level == 0) return writer.print("(mut decl '{}')", .{decl.name.fmt(ip)}); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3d3054c61807e8fac89f12568977748f4c8a9396..92a292116ea32485a70e380e00791edfdadcad2c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3075,6 +3075,7 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue .decl => |decl_index| { return func.lowerParentPtrDecl(ptr_val, decl_index, offset); }, + .anon_decl => @panic("TODO"), .mut_decl => |mut_decl| { const decl_index = mut_decl.decl; return func.lowerParentPtrDecl(ptr_val, decl_index, offset); diff --git a/src/codegen.zig b/src/codegen.zig index 738281cf55208d906566d8c75ab266467bda6c36..ecbc05b1cc1439d44178709c8fe13793b5187b59 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -655,6 +655,7 @@ fn lowerParentPtr( debug_output, reloc_info, ), + .anon_decl => @panic("TODO"), .int => |int| try generateSymbol(bin_file, src_loc, .{ .ty = Type.usize, .val = int.toValue(), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b627d952e5402e92620422bc094cdc908b396083..a16096fa18ea1569e96c19b0d284b19d4175df03 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -604,6 +604,7 @@ pub const DeclGen = struct { }, location, ), + .anon_decl => @panic("TODO"), .int => |int| { try writer.writeByte('('); try dg.renderCType(writer, ptr_cty); @@ -1155,6 +1156,7 @@ pub const DeclGen = struct { }, ptr_location, ), + .anon_decl => @panic("TODO"), .int => |int| { try writer.writeAll("(("); try dg.renderType(writer, ptr_ty); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fa6282a71b79db5445e31247b8553ec0fc92fcef..b7943dd4aafe477a0e242d4d0f7c79425a078d3c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -810,6 +810,8 @@ pub const Object = struct { /// * it works for functions not all globals. /// Therefore, this table keeps track of the mapping. decl_map: std.AutoHashMapUnmanaged(Module.Decl.Index, Builder.Global.Index), + /// Same deal as `decl_map` but for anonymous declarations, which are always global constants. + anon_decl_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Global.Index), /// Serves the same purpose as `decl_map` but only used for the `is_named_enum_value` instruction. named_enum_map: std.AutoHashMapUnmanaged(Module.Decl.Index, Builder.Function.Index), /// Maps Zig types to LLVM types. The table memory is backed by the GPA of @@ -993,6 +995,7 @@ pub const Object = struct { .target_data = target_data, .target = options.target, .decl_map = .{}, + .anon_decl_map = .{}, .named_enum_map = .{}, .type_map = .{}, .di_type_map = .{}, @@ -1011,6 +1014,7 @@ pub const Object = struct { self.target_machine.dispose(); } self.decl_map.deinit(gpa); + self.anon_decl_map.deinit(gpa); self.named_enum_map.deinit(gpa); self.type_map.deinit(gpa); self.extern_collisions.deinit(gpa); @@ -3038,6 +3042,31 @@ pub const Object = struct { } } + fn resolveGlobalAnonDecl( + o: *Object, + decl_val: InternPool.Index, + llvm_addr_space: Builder.AddrSpace, + ) Error!Builder.Variable.Index { + const gop = try o.anon_decl_map.getOrPut(o.gpa, decl_val); + if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.variable; + errdefer assert(o.anon_decl_map.remove(decl_val)); + + const mod = o.module; + const decl_ty = mod.intern_pool.typeOf(decl_val); + + const variable_index = try o.builder.addVariable( + try o.builder.fmt("__anon_{d}", .{@intFromEnum(decl_val)}), + try o.lowerType(decl_ty.toType()), + llvm_addr_space, + ); + gop.value_ptr.* = variable_index.ptrConst(&o.builder).global; + + try variable_index.setInitializer(try o.lowerValue(decl_val), &o.builder); + variable_index.setLinkage(.internal, &o.builder); + variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); + return variable_index; + } + fn resolveGlobalDecl( o: *Object, decl_index: Module.Decl.Index, @@ -3764,6 +3793,7 @@ pub const Object = struct { const ptr_val = switch (ptr.addr) { .decl => |decl| try o.lowerDeclRefValue(ptr_ty, decl), .mut_decl => |mut_decl| try o.lowerDeclRefValue(ptr_ty, mut_decl.decl), + .anon_decl => |anon_decl| try o.lowerAnonDeclRef(ptr_ty, anon_decl), .int => |int| try o.lowerIntAsPtr(int), .eu_payload, .opt_payload, @@ -4216,10 +4246,12 @@ pub const Object = struct { return o.builder.bigIntConst(try o.builder.intType(ty.intInfo(mod).bits), bigint); } - const ParentPtr = struct { - ty: Type, - llvm_ptr: Builder.Value, - }; + fn lowerParentPtrAnonDecl(o: *Object, decl_val: InternPool.Index) Error!Builder.Constant { + const mod = o.module; + const decl_ty = mod.intern_pool.typeOf(decl_val).toType(); + const ptr_ty = try mod.singleMutPtrType(decl_ty); + return o.lowerAnonDeclRef(ptr_ty, decl_val); + } fn lowerParentPtrDecl(o: *Object, decl_index: Module.Decl.Index) Allocator.Error!Builder.Constant { const mod = o.module; @@ -4229,13 +4261,14 @@ pub const Object = struct { return o.lowerDeclRefValue(ptr_ty, decl_index); } - fn lowerParentPtr(o: *Object, ptr_val: Value) Allocator.Error!Builder.Constant { + fn lowerParentPtr(o: *Object, ptr_val: Value) Error!Builder.Constant { const mod = o.module; const ip = &mod.intern_pool; const ptr = ip.indexToKey(ptr_val.toIntern()).ptr; return switch (ptr.addr) { - .decl => |decl| o.lowerParentPtrDecl(decl), - .mut_decl => |mut_decl| o.lowerParentPtrDecl(mut_decl.decl), + .decl => |decl| try o.lowerParentPtrDecl(decl), + .mut_decl => |mut_decl| try o.lowerParentPtrDecl(mut_decl.decl), + .anon_decl => |anon_decl| try o.lowerParentPtrAnonDecl(anon_decl), .int => |int| try o.lowerIntAsPtr(int), .eu_payload => |eu_ptr| { const parent_ptr = try o.lowerParentPtr(eu_ptr.toValue()); @@ -4349,6 +4382,49 @@ pub const Object = struct { }; } + /// This logic is very similar to `lowerDeclRefValue` but for anonymous declarations. + /// Maybe the logic could be unified. + fn lowerAnonDeclRef( + o: *Object, + ptr_ty: Type, + decl_val: InternPool.Index, + ) Error!Builder.Constant { + const mod = o.module; + const ip = &mod.intern_pool; + const decl_ty = ip.typeOf(decl_val).toType(); + const target = mod.getTarget(); + + if (decl_val.toValue().getFunction(mod)) |func| { + _ = func; + @panic("TODO"); + } else if (decl_val.toValue().getExternFunc(mod)) |func| { + _ = func; + @panic("TODO"); + } + + const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; + if ((!is_fn_body and !decl_ty.hasRuntimeBits(mod)) or + (is_fn_body and mod.typeToFunc(decl_ty).?.is_generic)) return o.lowerPtrToVoid(ptr_ty); + + if (is_fn_body) + @panic("TODO"); + + const addr_space = target_util.defaultAddressSpace(target, .global_constant); + const llvm_addr_space = toLlvmAddressSpace(addr_space, target); + const llvm_global = (try o.resolveGlobalAnonDecl(decl_val, llvm_addr_space)).ptrConst(&o.builder).global; + + const llvm_val = try o.builder.convConst( + .unneeded, + llvm_global.toConst(), + try o.builder.ptrType(llvm_addr_space), + ); + + return o.builder.convConst(if (ptr_ty.isAbiInt(mod)) switch (ptr_ty.intInfo(mod).signedness) { + .signed => .signed, + .unsigned => .unsigned, + } else .unneeded, llvm_val, try o.lowerType(ptr_ty)); + } + fn lowerDeclRefValue(o: *Object, ty: Type, decl_index: Module.Decl.Index) Allocator.Error!Builder.Constant { const mod = o.module; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 016604e1f66ab9288d8a8d3cd7b3ecd35e163fae..a4d3ec7f461f2d524a789a763c7d7e38bed63ea3 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -818,6 +818,7 @@ pub const DeclGen = struct { const mod = self.module; switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { .decl => |decl| return try self.constructDeclRef(ptr_ty, decl), + .anon_decl => @panic("TODO"), .mut_decl => |decl_mut| return try self.constructDeclRef(ptr_ty, decl_mut.decl), .int => |int| { const ptr_id = self.spv.allocId(); diff --git a/src/value.zig b/src/value.zig index d37e039abf101f0ee313a27f9ee65be1e80306fa..2c4a88da17d145b6493ada0edfa04d9aff739b62 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1565,12 +1565,14 @@ pub const Value = struct { } pub fn sliceLen(val: Value, mod: *Module) u64 { - const ptr = mod.intern_pool.indexToKey(val.toIntern()).ptr; + const ip = &mod.intern_pool; + const ptr = ip.indexToKey(val.toIntern()).ptr; return switch (ptr.len) { - .none => switch (mod.intern_pool.indexToKey(switch (ptr.addr) { + .none => switch (ip.indexToKey(switch (ptr.addr) { .decl => |decl| mod.declPtr(decl).ty.toIntern(), .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(), - .comptime_field => |comptime_field| mod.intern_pool.typeOf(comptime_field), + .anon_decl => |anon_decl| ip.typeOf(anon_decl), + .comptime_field => |comptime_field| ip.typeOf(comptime_field), else => unreachable, })) { .array_type => |array_type| array_type.len, @@ -1602,6 +1604,7 @@ pub const Value = struct { })).toValue(), .ptr => |ptr| switch (ptr.addr) { .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index), + .anon_decl => |anon_decl| anon_decl.toValue().maybeElemValue(mod, index), .mut_decl => |mut_decl| (try mod.declPtr(mut_decl.decl).internValue(mod)) .toValue().maybeElemValue(mod, index), .int, .eu_payload => null, -- 2.54.0