link: Implement API to get global symbol index

This commit is contained in:
Luuk de Gram
2022-05-21 20:44:01 +02:00
parent 359b61aec3
commit 8d03e4fc6b
5 changed files with 101 additions and 38 deletions

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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,
}
}

View File

@@ -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];

View File

@@ -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];