Commit c98d229844

Jakub Konka <kubkon@jakubkonka.com>
2024-01-18 17:13:19
macho: fill in more blanks in ZigObject
1 parent 6cd4c76
Changed files (1)
src
link
src/link/MachO/ZigObject.zig
@@ -259,12 +259,47 @@ pub fn lowerAnonDecl(
     explicit_alignment: InternPool.Alignment,
     src_loc: Module.SrcLoc,
 ) !codegen.Result {
-    _ = self;
-    _ = macho_file;
-    _ = decl_val;
-    _ = explicit_alignment;
-    _ = src_loc;
-    @panic("TODO lowerAnonDecl");
+    const gpa = macho_file.base.comp.gpa;
+    const mod = macho_file.base.comp.module.?;
+    const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
+    const decl_alignment = switch (explicit_alignment) {
+        .none => ty.abiAlignment(mod),
+        else => explicit_alignment,
+    };
+    if (self.anon_decls.get(decl_val)) |metadata| {
+        const existing_alignment = macho_file.getSymbol(metadata.symbol_index).getAtom(macho_file).?.alignment;
+        if (decl_alignment.order(existing_alignment).compare(.lte))
+            return .ok;
+    }
+
+    const val = Value.fromInterned(decl_val);
+    const tv = TypedValue{ .ty = ty, .val = val };
+    var name_buf: [32]u8 = undefined;
+    const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
+        @intFromEnum(decl_val),
+    }) catch unreachable;
+    const res = self.lowerConst(
+        macho_file,
+        name,
+        tv,
+        decl_alignment,
+        macho_file.zig_const_section_index.?,
+        src_loc,
+    ) catch |err| switch (err) {
+        error.OutOfMemory => return error.OutOfMemory,
+        else => |e| return .{ .fail = try Module.ErrorMsg.create(
+            gpa,
+            src_loc,
+            "unable to lower constant value: {s}",
+            .{@errorName(e)},
+        ) },
+    };
+    const sym_index = switch (res) {
+        .ok => |sym_index| sym_index,
+        .fail => |em| return .{ .fail = em },
+    };
+    try self.anon_decls.put(gpa, decl_val, .{ .symbol_index = sym_index });
+    return .ok;
 }
 
 fn freeUnnamedConsts(self: *ZigObject, macho_file: *MachO, decl_index: InternPool.DeclIndex) void {
@@ -591,11 +626,100 @@ pub fn lowerUnnamedConst(
     typed_value: TypedValue,
     decl_index: InternPool.DeclIndex,
 ) !u32 {
-    _ = self;
-    _ = macho_file;
-    _ = typed_value;
-    _ = decl_index;
-    @panic("TODO lowerUnnamedConst");
+    const gpa = macho_file.base.comp.gpa;
+    const mod = macho_file.base.comp.module.?;
+    const gop = try self.unnamed_consts.getOrPut(gpa, decl_index);
+    if (!gop.found_existing) {
+        gop.value_ptr.* = .{};
+    }
+    const unnamed_consts = gop.value_ptr;
+    const decl = mod.declPtr(decl_index);
+    const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
+    const index = unnamed_consts.items.len;
+    const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
+    defer gpa.free(name);
+    const sym_index = switch (try self.lowerConst(
+        macho_file,
+        name,
+        typed_value,
+        typed_value.ty.abiAlignment(mod),
+        macho_file.zig_const_section_index.?,
+        decl.srcLoc(mod),
+    )) {
+        .ok => |sym_index| sym_index,
+        .fail => |em| {
+            decl.analysis = .codegen_failure;
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
+            log.err("{s}", .{em.msg});
+            return error.CodegenFail;
+        },
+    };
+    const sym = macho_file.getSymbol(sym_index);
+    try unnamed_consts.append(gpa, sym.atom);
+    return sym_index;
+}
+
+const LowerConstResult = union(enum) {
+    ok: Symbol.Index,
+    fail: *Module.ErrorMsg,
+};
+
+fn lowerConst(
+    self: *ZigObject,
+    macho_file: *MachO,
+    name: []const u8,
+    tv: TypedValue,
+    required_alignment: InternPool.Alignment,
+    output_section_index: u8,
+    src_loc: Module.SrcLoc,
+) !LowerConstResult {
+    const gpa = macho_file.base.comp.gpa;
+
+    var code_buffer = std.ArrayList(u8).init(gpa);
+    defer code_buffer.deinit();
+
+    const sym_index = try self.addAtom(macho_file);
+
+    const res = try codegen.generateSymbol(&macho_file.base, src_loc, tv, &code_buffer, .{
+        .none = {},
+    }, .{
+        .parent_atom_index = sym_index,
+    });
+    const code = switch (res) {
+        .ok => code_buffer.items,
+        .fail => |em| return .{ .fail = em },
+    };
+
+    const sym = macho_file.getSymbol(sym_index);
+    const name_str_index = try macho_file.strings.insert(gpa, name);
+    sym.name = name_str_index;
+    sym.out_n_sect = output_section_index;
+
+    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
+    nlist.n_strx = name_str_index;
+    nlist.n_type = macho.N_SECT;
+    nlist.n_sect = output_section_index + 1;
+    self.symtab.items(.size)[sym.nlist_idx] = code.len;
+
+    const atom = sym.getAtom(macho_file).?;
+    atom.flags.alive = true;
+    atom.name = name_str_index;
+    atom.alignment = required_alignment;
+    atom.size = code.len;
+    atom.out_n_sect = output_section_index;
+
+    try atom.allocate(macho_file);
+    // TODO rename and re-audit this method
+    errdefer self.freeDeclMetadata(macho_file, sym_index);
+
+    sym.value = 0;
+    nlist.n_value = 0;
+
+    const sect = macho_file.sections.items(.header)[output_section_index];
+    const file_offset = sect.offset + atom.value - sect.addr;
+    try macho_file.base.file.?.pwriteAll(code, file_offset);
+
+    return .{ .ok = sym_index };
 }
 
 pub fn updateExports(
@@ -692,6 +816,91 @@ pub fn updateExports(
     }
 }
 
+fn updateLazySymbol(
+    self: *ZigObject,
+    macho_file: *MachO,
+    lazy_sym: link.File.LazySymbol,
+    symbol_index: Symbol.Index,
+) !void {
+    const gpa = macho_file.base.comp.gpa;
+    const mod = macho_file.base.comp.module.?;
+
+    var required_alignment: InternPool.Alignment = .none;
+    var code_buffer = std.ArrayList(u8).init(gpa);
+    defer code_buffer.deinit();
+
+    const name_str_index = blk: {
+        const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
+            @tagName(lazy_sym.kind),
+            lazy_sym.ty.fmt(mod),
+        });
+        defer gpa.free(name);
+        break :blk try macho_file.strings.insert(gpa, name);
+    };
+
+    const src = if (lazy_sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
+        mod.declPtr(owner_decl).srcLoc(mod)
+    else
+        Module.SrcLoc{
+            .file_scope = undefined,
+            .parent_decl_node = undefined,
+            .lazy = .unneeded,
+        };
+    const res = try codegen.generateLazySymbol(
+        &macho_file.base,
+        src,
+        lazy_sym,
+        &required_alignment,
+        &code_buffer,
+        .none,
+        .{ .parent_atom_index = symbol_index },
+    );
+    const code = switch (res) {
+        .ok => code_buffer.items,
+        .fail => |em| {
+            log.err("{s}", .{em.msg});
+            return error.CodegenFail;
+        },
+    };
+
+    const output_section_index = switch (lazy_sym.kind) {
+        .code => macho_file.zig_text_section_index.?,
+        .const_data => macho_file.zig_const_section_index.?,
+    };
+    const sym = macho_file.getSymbol(symbol_index);
+    sym.name = name_str_index;
+    sym.out_n_sect = output_section_index;
+
+    const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
+    nlist.n_strx = name_str_index;
+    nlist.n_type = macho.N_SECT;
+    nlist.n_sect = output_section_index + 1;
+    self.symtab.items(.size)[sym.nlist_idx] = code.len;
+
+    const atom = sym.getAtom(macho_file).?;
+    atom.flags.alive = true;
+    atom.name = name_str_index;
+    atom.alignment = required_alignment;
+    atom.size = code.len;
+    atom.out_n_sect = output_section_index;
+
+    try atom.allocate(macho_file);
+    errdefer self.freeDeclMetadata(macho_file, symbol_index);
+
+    sym.value = 0;
+    sym.flags.needs_zig_got = true;
+    nlist.st_value = 0;
+
+    if (!macho_file.base.isRelocatable()) {
+        const gop = try sym.getOrCreateZigGotEntry(symbol_index, macho_file);
+        try macho_file.zig_got.writeOne(macho_file, gop.index);
+    }
+
+    const sect = macho_file.sections.items(.header)[output_section_index];
+    const file_offset = sect.offset + atom.value - sect.addr;
+    try macho_file.base.file.?.pwriteAll(code, file_offset);
+}
+
 /// Must be called only after a successful call to `updateDecl`.
 pub fn updateDeclLineNumber(
     self: *ZigObject,
@@ -701,7 +910,7 @@ pub fn updateDeclLineNumber(
     _ = self;
     _ = mod;
     _ = decl_index;
-    @panic("TODO updateDeclLineNumber");
+    // TODO: Dwarf
 }
 
 pub fn deleteDeclExport(
@@ -751,6 +960,46 @@ pub fn getOrCreateMetadataForDecl(
     return gop.value_ptr.symbol_index;
 }
 
+pub fn getOrCreateMetadataForLazySymbol(
+    self: *ZigObject,
+    macho_file: *MachO,
+    lazy_sym: link.File.LazySymbol,
+) !Symbol.Index {
+    const gpa = macho_file.base.comp.gpa;
+    const mod = macho_file.base.comp.module.?;
+    const gop = try self.lazy_syms.getOrPut(gpa, lazy_sym.getDecl(mod));
+    errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
+    if (!gop.found_existing) gop.value_ptr.* = .{};
+    const metadata: struct {
+        symbol_index: *Symbol.Index,
+        state: *LazySymbolMetadata.State,
+    } = switch (lazy_sym.kind) {
+        .code => .{
+            .symbol_index = &gop.value_ptr.text_symbol_index,
+            .state = &gop.value_ptr.text_state,
+        },
+        .const_data => .{
+            .symbol_index = &gop.value_ptr.const_symbol_index,
+            .state = &gop.value_ptr.const_state,
+        },
+    };
+    switch (metadata.state.*) {
+        .unused => {
+            const symbol_index = try self.addAtom(macho_file);
+            const sym = macho_file.getSymbol(symbol_index);
+            sym.flags.needs_zig_got = true;
+            metadata.symbol_index.* = symbol_index;
+        },
+        .pending_flush => return metadata.symbol_index.*,
+        .flushed => {},
+    }
+    metadata.state.* = .pending_flush;
+    const symbol_index = metadata.symbol_index.*;
+    // anyerror needs to be deferred until flushModule
+    if (lazy_sym.getDecl(mod) != .none) try self.updateLazySymbol(macho_file, lazy_sym, symbol_index);
+    return symbol_index;
+}
+
 pub fn asFile(self: *ZigObject) File {
     return .{ .zig_object = self };
 }
@@ -822,9 +1071,9 @@ const DeclMetadata = struct {
 const LazySymbolMetadata = struct {
     const State = enum { unused, pending_flush, flushed };
     text_symbol_index: Symbol.Index = undefined,
-    data_const_symbol_index: Symbol.Index = undefined,
+    const_symbol_index: Symbol.Index = undefined,
     text_state: State = .unused,
-    rodata_state: State = .unused,
+    const_state: State = .unused,
 };
 
 const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata);