Commit 8d03e4fc6b

Luuk de Gram <luuk@degram.dev>
2022-05-21 20:44:01
link: Implement API to get global symbol index
1 parent 359b61a
Changed files (5)
src/arch/wasm/CodeGen.zig
@@ -1737,7 +1737,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
             var func_type = try genFunctype(self.gpa, ext_decl.ty.fnInfo(), self.target);
             defer func_type.deinit(self.gpa);
             ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
-            try self.bin_file.addOrUpdateImport(ext_decl);
+            try self.bin_file.addOrUpdateImport(
+                mem.sliceTo(ext_decl.name, 0),
+                ext_decl.link.wasm.sym_index,
+                ext_decl.getExternFn().?.lib_name,
+                ext_decl.fn_link.wasm.type_index,
+            );
             break :blk ext_decl;
         } else if (func_val.castTag(.decl_ref)) |decl_ref| {
             break :blk module.declPtr(decl_ref.data);
@@ -5107,13 +5112,15 @@ fn callIntrinsic(
     args: []const WValue,
 ) InnerError!WValue {
     assert(param_types.len == args.len);
-    const symbol_index = @intCast(u32, try self.bin_file.getIntrinsicSymbol(name));
-    var pt_tmp = try self.gpa.dupe(Type, param_types);
-    defer self.gpa.free(pt_tmp);
+    const symbol_index = self.bin_file.base.getGlobalSymbol(name) catch |err| {
+        return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
+    };
 
     // TODO: have genFunctype accept individual params so we don't,
     // need to initialize a fake Fn.Data instance.
-    const func_type = try genFunctype(self.base.allocator, .{
+    var pt_tmp = try self.gpa.dupe(Type, param_types);
+    defer self.gpa.free(pt_tmp);
+    var func_type = try genFunctype(self.gpa, .{
         .param_types = pt_tmp,
         .comptime_params = undefined,
         .return_type = return_type,
@@ -5122,9 +5129,9 @@ fn callIntrinsic(
         .is_var_args = false,
         .is_generic = false,
     }, self.target);
-    defer func_type.deinit(self.base.allocator);
+    defer func_type.deinit(self.gpa);
     const func_type_index = try self.bin_file.putOrGetFuncType(func_type);
-    try self.bin_file.addOrUpdateImport(symbol_index, func_type_index);
+    try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
 
     const want_sret_param = firstParamSRet(.C, return_type, self.target);
     // if we want return as first param, we allocate a pointer to stack,
src/link/Wasm/Atom.zig
@@ -171,7 +171,10 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
             }
             std.debug.assert(symbol.tag == .data);
             const merge_segment = wasm_bin.base.options.output_mode != .Obj;
-            const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);
+            const segment_info = if (self.file) |object_index| blk: {
+                break :blk wasm_bin.objects.items[object_index].segment_info;
+            } else wasm_bin.segment_info.items;
+            const segment_name = segment_info[symbol.index].outputName(merge_segment);
             const atom_index = wasm_bin.data_segments.get(segment_name).?;
             const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
             const segment = wasm_bin.segments.items[atom_index];
src/link/Wasm/Object.zig
@@ -302,12 +302,16 @@ fn Parser(comptime ReaderType: type) type {
         }
 
         fn parseObject(self: *Self, gpa: Allocator, is_object_file: *bool) Error!void {
+            errdefer self.object.deinit(gpa);
             try self.verifyMagicBytes();
             const version = try self.reader.reader().readIntLittle(u32);
 
             self.object.version = version;
             var relocatable_data = std.ArrayList(RelocatableData).init(gpa);
-            defer relocatable_data.deinit();
+
+            errdefer while (relocatable_data.popOrNull()) |rel_data| {
+                gpa.free(rel_data.data[0..rel_data.size]);
+            } else relocatable_data.deinit();
 
             var section_index: u32 = 0;
             while (self.reader.reader().readByte()) |byte| : (section_index += 1) {
@@ -808,26 +812,29 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
         kind: Symbol.Tag,
         index: u32,
     };
-    var symbol_for_segment = std.AutoArrayHashMap(Key, u32).init(gpa);
+    var symbol_for_segment = std.AutoArrayHashMap(Key, std.ArrayList(u32)).init(gpa);
     defer symbol_for_segment.deinit();
 
     for (self.symtable) |symbol, symbol_index| {
         switch (symbol.tag) {
             .function, .data => if (!symbol.isUndefined()) {
-                try symbol_for_segment.putNoClobber(
-                    .{ .kind = symbol.tag, .index = symbol.index },
-                    @intCast(u32, symbol_index),
-                );
+                const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
+                const sym_idx = @intCast(u32, symbol_index);
+                if (!gop.found_existing) {
+                    gop.value_ptr.* = std.ArrayList(u32).init(gpa);
+                }
+                try gop.value_ptr.*.append(sym_idx);
             },
             else => continue,
         }
     }
 
     for (self.relocatable_data) |relocatable_data, index| {
-        const sym_index = symbol_for_segment.get(.{
+        const symbols = symbol_for_segment.getPtr(.{
             .kind = relocatable_data.getSymbolKind(),
             .index = @intCast(u32, relocatable_data.index),
         }) orelse continue; // encountered a segment we do not create an atom for
+        const sym_index = symbols.pop();
         const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index));
 
         const atom = try gpa.create(Atom);
@@ -862,6 +869,16 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
         }
 
         try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
+
+        // symbols referencing the same atom will be added as alias
+        // or as 'parent' when they are global.
+        while (symbols.popOrNull()) |idx| {
+            const alias_symbol = self.symtable[idx];
+            const symbol = self.symtable[atom.sym_index];
+            if (alias_symbol.isGlobal() and symbol.isLocal()) {
+                atom.sym_index = idx;
+            }
+        }
         try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
 
         const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
src/link/Wasm.zig
@@ -433,6 +433,13 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
             continue; // Do not overwrite defined symbols with undefined symbols
         }
 
+        if (symbol.tag != existing_sym.tag) {
+            log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) });
+            log.err("  first definition in '{s}'", .{existing_file_path});
+            log.err("  next definition in '{s}'", .{object.name});
+            return error.SymbolMismatchingType;
+        }
+
         // when both symbols are weak, we skip overwriting
         if (existing_sym.isWeak() and symbol.isWeak()) {
             continue;
@@ -755,7 +762,7 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
 /// Returns the symbol index from the name of an intrinsic.
 /// If the symbol does not yet exist, creates a new one symbol instead
 /// and then returns the index to it.
-pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 {
+pub fn getGlobalSymbol(self: *Wasm, name: []const u8) !u32 {
     const name_index = try self.string_table.put(self.base.allocator, name);
     const gop = try self.globals.getOrPut(self.base.allocator, name_index);
     if (gop.found_existing) {
@@ -769,7 +776,7 @@ pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 {
         .tag = .function,
     };
     symbol.setGlobal(true);
-    symbol.setFlag(.WASM_SYM_UNDEFINED);
+    symbol.setUndefined(true);
 
     const sym_index = if (self.symbols_free_list.popOrNull()) |index| index else blk: {
         var index = @intCast(u32, self.symbols.items.len);
@@ -779,7 +786,7 @@ pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 {
     };
     self.symbols.items[sym_index] = symbol;
     gop.value_ptr.* = .{ .index = sym_index, .file = null };
-
+    try self.resolved_symbols.put(self.base.allocator, gop.value_ptr.*, {});
     return sym_index;
 }
 
@@ -982,10 +989,24 @@ fn mapFunctionTable(self: *Wasm) void {
     }
 }
 
-pub fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
+/// Either creates a new import, or updates one if existing.
+/// When `type_index` is non-null, we assume an external function.
+/// In all other cases, a data-symbol will be created instead.
+pub fn addOrUpdateImport(
+    self: *Wasm,
+    /// Name of the import
+    name: []const u8,
+    /// Symbol index that is external
+    symbol_index: u32,
+    /// Optional library name (i.e. `extern "c" fn foo() void`
+    lib_name: ?[*:0]const u8,
+    /// The index of the type that represents the function signature
+    /// when the extern is a function. When this is null, a data-symbol
+    /// is asserted instead.
+    type_index: ?u32,
+) !void {
     // For the import name itself, we use the decl's name, rather than the fully qualified name
-    const decl_name_index = try self.string_table.put(self.base.allocator, mem.sliceTo(decl.name, 0));
-    const symbol_index = decl.link.wasm.sym_index;
+    const decl_name_index = try self.string_table.put(self.base.allocator, name);
     const symbol: *Symbol = &self.symbols.items[symbol_index];
     symbol.setUndefined(true);
     symbol.setGlobal(true);
@@ -996,22 +1017,19 @@ pub fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
         try self.resolved_symbols.put(self.base.allocator, loc, {});
     }
 
-    switch (decl.ty.zigTypeTag()) {
-        .Fn => {
-            const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
-            const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: {
-                break :blk mem.sliceTo(lib_name, 0);
-            } else self.host_name;
-            if (!gop.found_existing) {
-                gop.value_ptr.* = .{
-                    .module_name = try self.string_table.put(self.base.allocator, module_name),
-                    .name = decl_name_index,
-                    .kind = .{ .function = decl.fn_link.wasm.type_index },
-                };
-            }
-        },
-        else => @panic("TODO: Implement undefined symbols for non-function declarations"),
-    }
+    if (type_index) |ty_index| {
+        const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
+        const module_name = if (lib_name) |l_name| blk: {
+            break :blk mem.sliceTo(l_name, 0);
+        } else self.host_name;
+        if (!gop.found_existing) {
+            gop.value_ptr.* = .{
+                .module_name = try self.string_table.put(self.base.allocator, module_name),
+                .name = decl_name_index,
+                .kind = .{ .function = ty_index },
+            };
+        }
+    } else @panic("TODO: Implement undefined symbols for non-function declarations");
 }
 
 const Kind = union(enum) {
@@ -1251,7 +1269,7 @@ fn mergeSections(self: *Wasm) !void {
                 symbol.index = @intCast(u32, self.tables.items.len) + self.imported_tables_count;
                 try self.tables.append(self.base.allocator, original_table);
             },
-            else => {},
+            else => unreachable,
         }
     }
 
src/link.zig
@@ -438,6 +438,24 @@ pub const File = struct {
         }
     }
 
+    /// Called from within CodeGen to retrieve the symbol index of a global symbol.
+    /// If no symbol exists yet with this name, a new one will be created instead.
+    pub fn getGlobalSymbol(base: *File, name: []const u8) UpdateDeclError!u32 {
+        log.debug("getGlobalSymbol '{s}'", .{name});
+        switch (base.tag) {
+            // zig fmt: off
+            .coff  => unreachable,
+            .elf   => unreachable,
+            .macho => unreachable,
+            .plan9 => unreachable,
+            .spirv => unreachable,
+            .c     => unreachable,
+            .wasm  => return @fieldParentPtr(Wasm,  "base", base).getGlobalSymbol(name),
+            .nvptx => unreachable,
+            // zig fmt: on
+        }
+    }
+
     /// May be called before or after updateDeclExports but must be called
     /// after allocateDeclIndexes for any given Decl.
     pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {