| ... | @@ -118,6 +118,9 @@ memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, | ... | @@ -118,6 +118,9 @@ memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, |
| 118 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, | 118 | tables: std.ArrayListUnmanaged(std.wasm.Table) = .{}, |
| 119 | /// Output export section | 119 | /// Output export section |
| 120 | exports: std.ArrayListUnmanaged(types.Export) = .{}, | 120 | exports: std.ArrayListUnmanaged(types.Export) = .{}, |
| | 121 | /// List of initialization functions. These must be called in order of priority |
| | 122 | /// by the (synthetic) __wasm_call_ctors function. |
| | 123 | init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{}, |
| 121 | | 124 | |
| 122 | /// Indirect function table, used to call function pointers | 125 | /// Indirect function table, used to call function pointers |
| 123 | /// When this is non-zero, we must emit a table entry, | 126 | /// When this is non-zero, we must emit a table entry, |
| ... | @@ -238,6 +241,34 @@ pub const SymbolLoc = struct { | ... | @@ -238,6 +241,34 @@ pub const SymbolLoc = struct { |
| 238 | } | 241 | } |
| 239 | }; | 242 | }; |
| 240 | | 243 | |
| | 244 | // Contains the location of the function symbol, as well as |
| | 245 | /// the priority itself of the initialization function. |
| | 246 | pub const InitFuncLoc = struct { |
| | 247 | /// object file index in the list of objects. |
| | 248 | /// Unlike `SymbolLoc` this cannot be `null` as we never define |
| | 249 | /// our own ctors. |
| | 250 | file: u16, |
| | 251 | /// Symbol index within the corresponding object file. |
| | 252 | index: u32, |
| | 253 | /// The priority in which the constructor must be called. |
| | 254 | priority: u32, |
| | 255 | |
| | 256 | /// From a given `InitFuncLoc` returns the corresponding function symbol |
| | 257 | fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol { |
| | 258 | return getSymbolLoc(loc).getSymbol(wasm); |
| | 259 | } |
| | 260 | |
| | 261 | /// Turns the given `InitFuncLoc` into a `SymbolLoc` |
| | 262 | fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc { |
| | 263 | return .{ .file = loc.file, .index = loc.index }; |
| | 264 | } |
| | 265 | |
| | 266 | /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`. |
| | 267 | fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool { |
| | 268 | _ = ctx; |
| | 269 | return lhs.priority < rhs.priority; |
| | 270 | } |
| | 271 | }; |
| 241 | /// Generic string table that duplicates strings | 272 | /// Generic string table that duplicates strings |
| 242 | /// and converts them into offsets instead. | 273 | /// and converts them into offsets instead. |
| 243 | pub const StringTable = struct { | 274 | pub const StringTable = struct { |
| ... | @@ -393,6 +424,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -393,6 +424,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 393 | } | 424 | } |
| 394 | } | 425 | } |
| 395 | | 426 | |
| | 427 | // create __wasm_call_ctors |
| | 428 | { |
| | 429 | const loc = try wasm_bin.createSyntheticSymbol("__wasm_call_ctors", .function); |
| | 430 | const symbol = loc.getSymbol(wasm_bin); |
| | 431 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| | 432 | // we do not know the function index until after we merged all sections. |
| | 433 | // Therefore we set `symbol.index` and create its corresponding references |
| | 434 | // at the end during `initializeCallCtorsFunction`. |
| | 435 | } |
| | 436 | |
| 396 | if (!options.strip and options.module != null) { | 437 | if (!options.strip and options.module != null) { |
| 397 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); | 438 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); |
| 398 | try wasm_bin.initDebugSections(); | 439 | try wasm_bin.initDebugSections(); |
| ... | @@ -434,7 +475,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol | ... | @@ -434,7 +475,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 434 | .index = undefined, | 475 | .index = undefined, |
| 435 | }); | 476 | }); |
| 436 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); | 477 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); |
| 437 | try wasm.globals.putNoClobber(wasm.base.allocator, name_offset, loc); | 478 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); |
| 438 | return loc; | 479 | return loc; |
| 439 | } | 480 | } |
| 440 | /// Initializes symbols and atoms for the debug sections | 481 | /// Initializes symbols and atoms for the debug sections |
| ... | @@ -600,27 +641,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -600,27 +641,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 600 | } | 641 | } |
| 601 | | 642 | |
| 602 | if (existing_sym.isUndefined() and symbol.isUndefined()) { | 643 | if (existing_sym.isUndefined() and symbol.isUndefined()) { |
| 603 | const existing_name = if (existing_loc.file) |file_index| blk: { | 644 | // only verify module/import name for function symbols |
| 604 | const obj = wasm.objects.items[file_index]; | 645 | if (symbol.tag == .function) { |
| 605 | const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; | 646 | const existing_name = if (existing_loc.file) |file_index| blk: { |
| 606 | break :blk obj.string_table.get(name_index); | 647 | const obj = wasm.objects.items[file_index]; |
| 607 | } else blk: { | 648 | const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name; |
| 608 | const name_index = wasm.imports.get(existing_loc).?.module_name; | 649 | break :blk obj.string_table.get(name_index); |
| 609 | break :blk wasm.string_table.get(name_index); | 650 | } else blk: { |
| 610 | }; | 651 | const name_index = wasm.imports.get(existing_loc).?.module_name; |
| | 652 | break :blk wasm.string_table.get(name_index); |
| | 653 | }; |
| 611 | | 654 | |
| 612 | const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name; | 655 | const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name; |
| 613 | const module_name = object.string_table.get(module_index); | 656 | const module_name = object.string_table.get(module_index); |
| 614 | if (!mem.eql(u8, existing_name, module_name)) { | 657 | if (!mem.eql(u8, existing_name, module_name)) { |
| 615 | log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ | 658 | log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ |
| 616 | sym_name, | 659 | sym_name, |
| 617 | existing_name, | 660 | existing_name, |
| 618 | module_name, | 661 | module_name, |
| 619 | }); | 662 | }); |
| 620 | log.err(" first definition in '{s}'", .{existing_file_path}); | 663 | log.err(" first definition in '{s}'", .{existing_file_path}); |
| 621 | log.err(" next definition in '{s}'", .{object.name}); | 664 | log.err(" next definition in '{s}'", .{object.name}); |
| 622 | return error.ModuleNameMismatch; | 665 | return error.ModuleNameMismatch; |
| | 666 | } |
| 623 | } | 667 | } |
| | 668 | |
| | 669 | // both undefined so skip overwriting existing symbol and discard the new symbol |
| | 670 | try wasm.discarded.put(wasm.base.allocator, location, existing_loc); |
| | 671 | continue; |
| 624 | } | 672 | } |
| 625 | | 673 | |
| 626 | if (existing_sym.tag == .global) { | 674 | if (existing_sym.tag == .global) { |
| ... | @@ -646,8 +694,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { | ... | @@ -646,8 +694,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { |
| 646 | } | 694 | } |
| 647 | } | 695 | } |
| 648 | | 696 | |
| 649 | // when both symbols are weak, we skip overwriting | 697 | // when both symbols are weak, we skip overwriting unless the existing |
| 650 | if (existing_sym.isWeak() and symbol.isWeak()) { | 698 | // symbol is weak and the new one isn't, in which case we *do* overwrite it. |
| | 699 | if (existing_sym.isWeak() and symbol.isWeak()) blk: { |
| | 700 | if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk; |
| 651 | try wasm.discarded.put(wasm.base.allocator, location, existing_loc); | 701 | try wasm.discarded.put(wasm.base.allocator, location, existing_loc); |
| 652 | continue; | 702 | continue; |
| 653 | } | 703 | } |
| ... | @@ -801,6 +851,51 @@ fn validateFeatures( | ... | @@ -801,6 +851,51 @@ fn validateFeatures( |
| 801 | to_emit.* = allowed; | 851 | to_emit.* = allowed; |
| 802 | } | 852 | } |
| 803 | | 853 | |
| | 854 | /// Creates synthetic linker-symbols, but only if they are being referenced from |
| | 855 | /// any object file. For instance, the `__heap_base` symbol will only be created, |
| | 856 | /// if one or multiple undefined references exist. When none exist, the symbol will |
| | 857 | /// not be created, ensuring we don't unneccesarily emit unreferenced symbols. |
| | 858 | fn resolveLazySymbols(wasm: *Wasm) !void { |
| | 859 | if (wasm.undefs.fetchSwapRemove("__heap_base")) |kv| { |
| | 860 | const loc = try wasm.createSyntheticSymbol("__heap_base", .data); |
| | 861 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| | 862 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. |
| | 863 | |
| | 864 | const atom = try wasm.base.allocator.create(Atom); |
| | 865 | errdefer wasm.base.allocator.destroy(atom); |
| | 866 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| | 867 | atom.* = Atom.empty; |
| | 868 | atom.sym_index = loc.index; |
| | 869 | atom.alignment = 1; |
| | 870 | |
| | 871 | try wasm.parseAtom(atom, .{ .data = .synthetic }); |
| | 872 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| | 873 | } |
| | 874 | |
| | 875 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { |
| | 876 | const loc = try wasm.createSyntheticSymbol("__heap_end", .data); |
| | 877 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| | 878 | _ = wasm.resolved_symbols.swapRemove(loc); |
| | 879 | |
| | 880 | const atom = try wasm.base.allocator.create(Atom); |
| | 881 | errdefer wasm.base.allocator.destroy(atom); |
| | 882 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| | 883 | atom.* = Atom.empty; |
| | 884 | atom.sym_index = loc.index; |
| | 885 | atom.alignment = 1; |
| | 886 | |
| | 887 | try wasm.parseAtom(atom, .{ .data = .synthetic }); |
| | 888 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| | 889 | } |
| | 890 | } |
| | 891 | |
| | 892 | // Tries to find a global symbol by its name. Returns null when not found, |
| | 893 | /// and its location when it is found. |
| | 894 | fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { |
| | 895 | const offset = wasm.string_table.getOffset(name) orelse return null; |
| | 896 | return wasm.globals.get(offset); |
| | 897 | } |
| | 898 | |
| 804 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { | 899 | fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 805 | if (wasm.base.options.output_mode == .Obj) return; | 900 | if (wasm.base.options.output_mode == .Obj) return; |
| 806 | if (wasm.base.options.import_symbols) return; | 901 | if (wasm.base.options.import_symbols) return; |
| ... | @@ -813,12 +908,8 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { | ... | @@ -813,12 +908,8 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { |
| 813 | const file_name = if (undef.file) |file_index| name: { | 908 | const file_name = if (undef.file) |file_index| name: { |
| 814 | break :name wasm.objects.items[file_index].name; | 909 | break :name wasm.objects.items[file_index].name; |
| 815 | } else wasm.name; | 910 | } else wasm.name; |
| 816 | const import_name = if (undef.file) |file_index| name: { | 911 | const symbol_name = undef.getName(wasm); |
| 817 | const obj = wasm.objects.items[file_index]; | 912 | log.err("could not resolve undefined symbol '{s}'", .{symbol_name}); |
| 818 | const name_index = obj.findImport(symbol.tag.externalType(), symbol.index).name; | | |
| 819 | break :name obj.string_table.get(name_index); | | |
| 820 | } else wasm.string_table.get(wasm.imports.get(undef).?.name); | | |
| 821 | log.err("could not resolve undefined symbol '{s}'", .{import_name}); | | |
| 822 | log.err(" defined in '{s}'", .{file_name}); | 913 | log.err(" defined in '{s}'", .{file_name}); |
| 823 | } | 914 | } |
| 824 | } | 915 | } |
| ... | @@ -885,6 +976,7 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -885,6 +976,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 885 | wasm.wasm_globals.deinit(gpa); | 976 | wasm.wasm_globals.deinit(gpa); |
| 886 | wasm.function_table.deinit(gpa); | 977 | wasm.function_table.deinit(gpa); |
| 887 | wasm.tables.deinit(gpa); | 978 | wasm.tables.deinit(gpa); |
| | 979 | wasm.init_funcs.deinit(gpa); |
| 888 | wasm.exports.deinit(gpa); | 980 | wasm.exports.deinit(gpa); |
| 889 | | 981 | |
| 890 | wasm.string_table.deinit(gpa); | 982 | wasm.string_table.deinit(gpa); |
| ... | @@ -1405,14 +1497,13 @@ fn mapFunctionTable(wasm: *Wasm) void { | ... | @@ -1405,14 +1497,13 @@ fn mapFunctionTable(wasm: *Wasm) void { |
| 1405 | } | 1497 | } |
| 1406 | | 1498 | |
| 1407 | if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) { | 1499 | if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) { |
| 1408 | const sym_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; | 1500 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1409 | const import = wasm.imports.getPtr(sym_loc).?; | 1501 | const import = wasm.imports.getPtr(sym_loc).?; |
| 1410 | import.kind.table.limits.min = index - 1; // we start at index 1. | 1502 | import.kind.table.limits.min = index - 1; // we start at index 1. |
| 1411 | } else if (index > 1) { | 1503 | } else if (index > 1) { |
| 1412 | log.debug("Appending indirect function table", .{}); | 1504 | log.debug("Appending indirect function table", .{}); |
| 1413 | const offset = wasm.string_table.getOffset("__indirect_function_table").?; | 1505 | const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 1414 | const sym_with_loc = wasm.globals.get(offset).?; | 1506 | const symbol = sym_loc.getSymbol(wasm); |
| 1415 | const symbol = sym_with_loc.getSymbol(wasm); | | |
| 1416 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; | 1507 | const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; |
| 1417 | table.limits = .{ .min = index, .max = index }; | 1508 | table.limits = .{ .min = index, .max = index }; |
| 1418 | } | 1509 | } |
| ... | @@ -1491,6 +1582,7 @@ const Kind = union(enum) { | ... | @@ -1491,6 +1582,7 @@ const Kind = union(enum) { |
| 1491 | read_only, | 1582 | read_only, |
| 1492 | uninitialized, | 1583 | uninitialized, |
| 1493 | initialized, | 1584 | initialized, |
| | 1585 | synthetic, |
| 1494 | }, | 1586 | }, |
| 1495 | function: FnData, | 1587 | function: FnData, |
| 1496 | | 1588 | |
| ... | @@ -1501,6 +1593,7 @@ const Kind = union(enum) { | ... | @@ -1501,6 +1593,7 @@ const Kind = union(enum) { |
| 1501 | .read_only => return ".rodata.", | 1593 | .read_only => return ".rodata.", |
| 1502 | .uninitialized => return ".bss.", | 1594 | .uninitialized => return ".bss.", |
| 1503 | .initialized => return ".data.", | 1595 | .initialized => return ".data.", |
| | 1596 | .synthetic => return ".synthetic", |
| 1504 | } | 1597 | } |
| 1505 | } | 1598 | } |
| 1506 | }; | 1599 | }; |
| ... | @@ -1637,9 +1730,14 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -1637,9 +1730,14 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1637 | var offset: u32 = 0; | 1730 | var offset: u32 = 0; |
| 1638 | while (true) { | 1731 | while (true) { |
| 1639 | const symbol_loc = atom.symbolLoc(); | 1732 | const symbol_loc = atom.symbolLoc(); |
| 1640 | if (!wasm.resolved_symbols.contains(symbol_loc)) { | 1733 | if (wasm.code_section_index) |index| { |
| 1641 | atom = atom.next orelse break; | 1734 | if (index == entry.key_ptr.*) { |
| 1642 | continue; | 1735 | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| | 1736 | // only allocate resolved function body's. |
| | 1737 | atom = atom.next orelse break; |
| | 1738 | continue; |
| | 1739 | } |
| | 1740 | } |
| 1643 | } | 1741 | } |
| 1644 | offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); | 1742 | offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); |
| 1645 | atom.offset = offset; | 1743 | atom.offset = offset; |
| ... | @@ -1674,6 +1772,7 @@ fn sortDataSegments(wasm: *Wasm) !void { | ... | @@ -1674,6 +1772,7 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1674 | if (mem.startsWith(u8, name, ".rodata")) return 0; | 1772 | if (mem.startsWith(u8, name, ".rodata")) return 0; |
| 1675 | if (mem.startsWith(u8, name, ".data")) return 1; | 1773 | if (mem.startsWith(u8, name, ".data")) return 1; |
| 1676 | if (mem.startsWith(u8, name, ".text")) return 2; | 1774 | if (mem.startsWith(u8, name, ".text")) return 2; |
| | 1775 | if (mem.startsWith(u8, name, ".synthetic")) return 100; // always at end |
| 1677 | return 3; | 1776 | return 3; |
| 1678 | } | 1777 | } |
| 1679 | }; | 1778 | }; |
| ... | @@ -1687,6 +1786,125 @@ fn sortDataSegments(wasm: *Wasm) !void { | ... | @@ -1687,6 +1786,125 @@ fn sortDataSegments(wasm: *Wasm) !void { |
| 1687 | wasm.data_segments = new_mapping; | 1786 | wasm.data_segments = new_mapping; |
| 1688 | } | 1787 | } |
| 1689 | | 1788 | |
| | 1789 | /// Obtains all initfuncs from each object file, verifies its function signature, |
| | 1790 | /// and then appends it to our final `init_funcs` list. |
| | 1791 | /// After all functions have been inserted, the functions will be ordered based |
| | 1792 | /// on their priority. |
| | 1793 | /// NOTE: This function must be called before we merged any other section. |
| | 1794 | /// This is because all init funcs in the object files contain references to the |
| | 1795 | /// original functions and their types. We need to know the type to verify it doesn't |
| | 1796 | /// contain any parameters. |
| | 1797 | fn setupInitFunctions(wasm: *Wasm) !void { |
| | 1798 | for (wasm.objects.items) |object, file_index| { |
| | 1799 | try wasm.init_funcs.ensureUnusedCapacity(wasm.base.allocator, object.init_funcs.len); |
| | 1800 | for (object.init_funcs) |init_func| { |
| | 1801 | const symbol = object.symtable[init_func.symbol_index]; |
| | 1802 | const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { |
| | 1803 | const imp: types.Import = object.findImport(.function, symbol.index); |
| | 1804 | break :ty object.func_types[imp.kind.function]; |
| | 1805 | } else ty: { |
| | 1806 | const func_index = symbol.index - object.importedCountByKind(.function); |
| | 1807 | const func = object.functions[func_index]; |
| | 1808 | break :ty object.func_types[func.type_index]; |
| | 1809 | }; |
| | 1810 | if (ty.params.len != 0) { |
| | 1811 | log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)}); |
| | 1812 | return error.InvalidInitFunc; |
| | 1813 | } |
| | 1814 | log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); |
| | 1815 | wasm.init_funcs.appendAssumeCapacity(.{ |
| | 1816 | .index = init_func.symbol_index, |
| | 1817 | .file = @intCast(u16, file_index), |
| | 1818 | .priority = init_func.priority, |
| | 1819 | }); |
| | 1820 | } |
| | 1821 | } |
| | 1822 | |
| | 1823 | // sort the initfunctions based on their priority |
| | 1824 | std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); |
| | 1825 | } |
| | 1826 | |
| | 1827 | /// Creates a function body for the `__wasm_call_ctors` symbol. |
| | 1828 | /// Loops over all constructors found in `init_funcs` and calls them |
| | 1829 | /// respectively based on their priority which was sorted by `setupInitFunctions`. |
| | 1830 | /// NOTE: This function must be called after we merged all sections to ensure the |
| | 1831 | /// references to the function stored in the symbol have been finalized so we end |
| | 1832 | /// up calling the resolved function. |
| | 1833 | fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| | 1834 | // No code to emit, so also no ctors to call |
| | 1835 | if (wasm.code_section_index == null) { |
| | 1836 | // Make sure to remove it from the resolved symbols so we do not emit |
| | 1837 | // it within any section. TODO: Remove this once we implement garbage collection. |
| | 1838 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; |
| | 1839 | std.debug.assert(wasm.resolved_symbols.swapRemove(loc)); |
| | 1840 | return; |
| | 1841 | } |
| | 1842 | |
| | 1843 | var function_body = std.ArrayList(u8).init(wasm.base.allocator); |
| | 1844 | defer function_body.deinit(); |
| | 1845 | const writer = function_body.writer(); |
| | 1846 | |
| | 1847 | // Create the function body |
| | 1848 | { |
| | 1849 | // Write locals count (we have none) |
| | 1850 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 1851 | |
| | 1852 | // call constructors |
| | 1853 | for (wasm.init_funcs.items) |init_func_loc| { |
| | 1854 | const symbol = init_func_loc.getSymbol(wasm); |
| | 1855 | const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count]; |
| | 1856 | const ty = wasm.func_types.items[func.type_index]; |
| | 1857 | |
| | 1858 | // Call function by its function index |
| | 1859 | try writer.writeByte(std.wasm.opcode(.call)); |
| | 1860 | try leb.writeULEB128(writer, symbol.index); |
| | 1861 | |
| | 1862 | // drop all returned values from the stack as __wasm_call_ctors has no return value |
| | 1863 | for (ty.returns) |_| { |
| | 1864 | try writer.writeByte(std.wasm.opcode(.drop)); |
| | 1865 | } |
| | 1866 | } |
| | 1867 | |
| | 1868 | // End function body |
| | 1869 | try writer.writeByte(std.wasm.opcode(.end)); |
| | 1870 | } |
| | 1871 | |
| | 1872 | const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; |
| | 1873 | const symbol = loc.getSymbol(wasm); |
| | 1874 | // create type (() -> nil) as we do not have any parameters or return value. |
| | 1875 | const ty_index = try wasm.putOrGetFuncType(.{ .params = &[_]std.wasm.Valtype{}, .returns = &[_]std.wasm.Valtype{} }); |
| | 1876 | // create function with above type |
| | 1877 | const func_index = wasm.imported_functions_count + @intCast(u32, wasm.functions.count()); |
| | 1878 | try wasm.functions.putNoClobber( |
| | 1879 | wasm.base.allocator, |
| | 1880 | .{ .file = null, .index = func_index }, |
| | 1881 | .{ .type_index = ty_index }, |
| | 1882 | ); |
| | 1883 | symbol.index = func_index; |
| | 1884 | |
| | 1885 | // create the atom that will be output into the final binary |
| | 1886 | const atom = try wasm.base.allocator.create(Atom); |
| | 1887 | errdefer wasm.base.allocator.destroy(atom); |
| | 1888 | atom.* = .{ |
| | 1889 | .size = @intCast(u32, function_body.items.len), |
| | 1890 | .offset = 0, |
| | 1891 | .sym_index = loc.index, |
| | 1892 | .file = null, |
| | 1893 | .alignment = 1, |
| | 1894 | .next = null, |
| | 1895 | .prev = null, |
| | 1896 | .code = function_body.moveToUnmanaged(), |
| | 1897 | .dbg_info_atom = undefined, |
| | 1898 | }; |
| | 1899 | try wasm.managed_atoms.append(wasm.base.allocator, atom); |
| | 1900 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom); |
| | 1901 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); |
| | 1902 | |
| | 1903 | // `allocateAtoms` has already been called, set the atom's offset manually. |
| | 1904 | // This is fine to do manually as we insert the atom at the very end. |
| | 1905 | atom.offset = atom.prev.?.offset + atom.prev.?.size; |
| | 1906 | } |
| | 1907 | |
| 1690 | fn setupImports(wasm: *Wasm) !void { | 1908 | fn setupImports(wasm: *Wasm) !void { |
| 1691 | log.debug("Merging imports", .{}); | 1909 | log.debug("Merging imports", .{}); |
| 1692 | var discarded_it = wasm.discarded.keyIterator(); | 1910 | var discarded_it = wasm.discarded.keyIterator(); |
| ... | @@ -1859,16 +2077,12 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -1859,16 +2077,12 @@ fn setupExports(wasm: *Wasm) !void { |
| 1859 | | 2077 | |
| 1860 | const force_exp_names = wasm.base.options.export_symbol_names; | 2078 | const force_exp_names = wasm.base.options.export_symbol_names; |
| 1861 | if (force_exp_names.len > 0) { | 2079 | if (force_exp_names.len > 0) { |
| 1862 | var failed_exports = try std.ArrayList([]const u8).initCapacity(wasm.base.allocator, force_exp_names.len); | 2080 | var failed_exports = false; |
| 1863 | defer failed_exports.deinit(); | | |
| 1864 | | 2081 | |
| 1865 | for (force_exp_names) |exp_name| { | 2082 | for (force_exp_names) |exp_name| { |
| 1866 | const name_index = wasm.string_table.getOffset(exp_name) orelse { | 2083 | const loc = wasm.findGlobalSymbol(exp_name) orelse { |
| 1867 | failed_exports.appendAssumeCapacity(exp_name); | 2084 | log.err("could not export '{s}', symbol not found", .{exp_name}); |
| 1868 | continue; | 2085 | failed_exports = true; |
| 1869 | }; | | |
| 1870 | const loc = wasm.globals.get(name_index) orelse { | | |
| 1871 | failed_exports.appendAssumeCapacity(exp_name); | | |
| 1872 | continue; | 2086 | continue; |
| 1873 | }; | 2087 | }; |
| 1874 | | 2088 | |
| ... | @@ -1876,10 +2090,7 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -1876,10 +2090,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 1876 | symbol.setFlag(.WASM_SYM_EXPORTED); | 2090 | symbol.setFlag(.WASM_SYM_EXPORTED); |
| 1877 | } | 2091 | } |
| 1878 | | 2092 | |
| 1879 | if (failed_exports.items.len > 0) { | 2093 | if (failed_exports) { |
| 1880 | for (failed_exports.items) |exp_name| { | | |
| 1881 | log.err("could not export '{s}', symbol not found", .{exp_name}); | | |
| 1882 | } | | |
| 1883 | return error.MissingSymbol; | 2094 | return error.MissingSymbol; |
| 1884 | } | 2095 | } |
| 1885 | } | 2096 | } |
| ... | @@ -1925,7 +2136,7 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -1925,7 +2136,7 @@ fn setupExports(wasm: *Wasm) !void { |
| 1925 | fn setupStart(wasm: *Wasm) !void { | 2136 | fn setupStart(wasm: *Wasm) !void { |
| 1926 | const entry_name = wasm.base.options.entry orelse "_start"; | 2137 | const entry_name = wasm.base.options.entry orelse "_start"; |
| 1927 | | 2138 | |
| 1928 | const symbol_name_offset = wasm.string_table.getOffset(entry_name) orelse { | 2139 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { |
| 1929 | if (wasm.base.options.output_mode == .Exe) { | 2140 | if (wasm.base.options.output_mode == .Exe) { |
| 1930 | if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors | 2141 | if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors |
| 1931 | } else { | 2142 | } else { |
| ... | @@ -1935,7 +2146,6 @@ fn setupStart(wasm: *Wasm) !void { | ... | @@ -1935,7 +2146,6 @@ fn setupStart(wasm: *Wasm) !void { |
| 1935 | return error.MissingSymbol; | 2146 | return error.MissingSymbol; |
| 1936 | }; | 2147 | }; |
| 1937 | | 2148 | |
| 1938 | const symbol_loc = wasm.globals.get(symbol_name_offset).?; | | |
| 1939 | const symbol = symbol_loc.getSymbol(wasm); | 2149 | const symbol = symbol_loc.getSymbol(wasm); |
| 1940 | if (symbol.tag != .function) { | 2150 | if (symbol.tag != .function) { |
| 1941 | log.err("Entry symbol '{s}' is not a function", .{entry_name}); | 2151 | log.err("Entry symbol '{s}' is not a function", .{entry_name}); |
| ... | @@ -1955,6 +2165,8 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -1955,6 +2165,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 1955 | // Use the user-provided stack size or else we use 1MB by default | 2165 | // Use the user-provided stack size or else we use 1MB by default |
| 1956 | const stack_size = wasm.base.options.stack_size_override orelse page_size * 16; | 2166 | const stack_size = wasm.base.options.stack_size_override orelse page_size * 16; |
| 1957 | const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention | 2167 | const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention |
| | 2168 | const heap_alignment = 16; // wasm's heap alignment as specified by tool-convention |
| | 2169 | |
| 1958 | // Always place the stack at the start by default | 2170 | // Always place the stack at the start by default |
| 1959 | // unless the user specified the global-base flag | 2171 | // unless the user specified the global-base flag |
| 1960 | var place_stack_first = true; | 2172 | var place_stack_first = true; |
| ... | @@ -1973,8 +2185,13 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -1973,8 +2185,13 @@ fn setupMemory(wasm: *Wasm) !void { |
| 1973 | } | 2185 | } |
| 1974 | | 2186 | |
| 1975 | var offset: u32 = @intCast(u32, memory_ptr); | 2187 | var offset: u32 = @intCast(u32, memory_ptr); |
| 1976 | for (wasm.data_segments.values()) |segment_index| { | 2188 | var data_seg_it = wasm.data_segments.iterator(); |
| 1977 | const segment = &wasm.segments.items[segment_index]; | 2189 | while (data_seg_it.next()) |entry| { |
| | 2190 | if (mem.eql(u8, entry.key_ptr.*, ".synthetic")) { |
| | 2191 | // do not update synthetic segments as they are not part of the output |
| | 2192 | continue; |
| | 2193 | } |
| | 2194 | const segment = &wasm.segments.items[entry.value_ptr.*]; |
| 1978 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); | 2195 | memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); |
| 1979 | memory_ptr += segment.size; | 2196 | memory_ptr += segment.size; |
| 1980 | segment.offset = offset; | 2197 | segment.offset = offset; |
| ... | @@ -1987,6 +2204,16 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -1987,6 +2204,16 @@ fn setupMemory(wasm: *Wasm) !void { |
| 1987 | wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); | 2204 | wasm.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr)); |
| 1988 | } | 2205 | } |
| 1989 | | 2206 | |
| | 2207 | // One of the linked object files has a reference to the __heap_base symbol. |
| | 2208 | // We must set its virtual address so it can be used in relocations. |
| | 2209 | if (wasm.findGlobalSymbol("__heap_base")) |loc| { |
| | 2210 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| | 2211 | const segment = &wasm.segments.items[segment_index]; |
| | 2212 | segment.offset = 0; // for simplicity we store the entire VA into atom's offset. |
| | 2213 | const atom = wasm.symbol_atom.get(loc).?; |
| | 2214 | atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); |
| | 2215 | } |
| | 2216 | |
| 1990 | // Setup the max amount of pages | 2217 | // Setup the max amount of pages |
| 1991 | // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 | 2218 | // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 |
| 1992 | const max_memory_allowed: u64 = (1 << 32) - 1; | 2219 | const max_memory_allowed: u64 = (1 << 32) - 1; |
| ... | @@ -2006,12 +2233,20 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2006,12 +2233,20 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2006 | } | 2233 | } |
| 2007 | memory_ptr = initial_memory; | 2234 | memory_ptr = initial_memory; |
| 2008 | } | 2235 | } |
| 2009 | | 2236 | memory_ptr = mem.alignForwardGeneric(u64, memory_ptr, std.wasm.page_size); |
| 2010 | // In case we do not import memory, but define it ourselves, | 2237 | // In case we do not import memory, but define it ourselves, |
| 2011 | // set the minimum amount of pages on the memory section. | 2238 | // set the minimum amount of pages on the memory section. |
| 2012 | wasm.memories.limits.min = @intCast(u32, std.mem.alignForwardGeneric(u64, memory_ptr, page_size) / page_size); | 2239 | wasm.memories.limits.min = @intCast(u32, memory_ptr / page_size); |
| 2013 | log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); | 2240 | log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); |
| 2014 | | 2241 | |
| | 2242 | if (wasm.findGlobalSymbol("__heap_end")) |loc| { |
| | 2243 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| | 2244 | const segment = &wasm.segments.items[segment_index]; |
| | 2245 | segment.offset = 0; |
| | 2246 | const atom = wasm.symbol_atom.get(loc).?; |
| | 2247 | atom.offset = @intCast(u32, memory_ptr); |
| | 2248 | } |
| | 2249 | |
| 2015 | if (wasm.base.options.max_memory) |max_memory| { | 2250 | if (wasm.base.options.max_memory) |max_memory| { |
| 2016 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { | 2251 | if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { |
| 2017 | log.err("Maximum memory must be {d}-byte aligned", .{page_size}); | 2252 | log.err("Maximum memory must be {d}-byte aligned", .{page_size}); |
| ... | @@ -2488,8 +2723,10 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2488,8 +2723,10 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2488 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; | 2723 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 2489 | try wasm.validateFeatures(&enabled_features, &emit_features_count); | 2724 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 2490 | try wasm.resolveSymbolsInArchives(); | 2725 | try wasm.resolveSymbolsInArchives(); |
| | 2726 | try wasm.resolveLazySymbols(); |
| 2491 | try wasm.checkUndefinedSymbols(); | 2727 | try wasm.checkUndefinedSymbols(); |
| 2492 | | 2728 | |
| | 2729 | try wasm.setupInitFunctions(); |
| 2493 | try wasm.setupStart(); | 2730 | try wasm.setupStart(); |
| 2494 | try wasm.setupImports(); | 2731 | try wasm.setupImports(); |
| 2495 | | 2732 | |
| ... | @@ -2502,6 +2739,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -2502,6 +2739,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 2502 | wasm.mapFunctionTable(); | 2739 | wasm.mapFunctionTable(); |
| 2503 | try wasm.mergeSections(); | 2740 | try wasm.mergeSections(); |
| 2504 | try wasm.mergeTypes(); | 2741 | try wasm.mergeTypes(); |
| | 2742 | try wasm.initializeCallCtorsFunction(); |
| 2505 | try wasm.setupExports(); | 2743 | try wasm.setupExports(); |
| 2506 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 2744 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2507 | | 2745 | |
| ... | @@ -2569,11 +2807,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2569,11 +2807,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2569 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; | 2807 | var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; |
| 2570 | try wasm.validateFeatures(&enabled_features, &emit_features_count); | 2808 | try wasm.validateFeatures(&enabled_features, &emit_features_count); |
| 2571 | try wasm.resolveSymbolsInArchives(); | 2809 | try wasm.resolveSymbolsInArchives(); |
| | 2810 | try wasm.resolveLazySymbols(); |
| 2572 | try wasm.checkUndefinedSymbols(); | 2811 | try wasm.checkUndefinedSymbols(); |
| 2573 | | 2812 | |
| 2574 | // When we finish/error we reset the state of the linker | 2813 | // When we finish/error we reset the state of the linker |
| 2575 | // So we can rebuild the binary file on each incremental update | 2814 | // So we can rebuild the binary file on each incremental update |
| 2576 | defer wasm.resetState(); | 2815 | defer wasm.resetState(); |
| | 2816 | try wasm.setupInitFunctions(); |
| 2577 | try wasm.setupStart(); | 2817 | try wasm.setupStart(); |
| 2578 | try wasm.setupImports(); | 2818 | try wasm.setupImports(); |
| 2579 | if (wasm.base.options.module) |mod| { | 2819 | if (wasm.base.options.module) |mod| { |
| ... | @@ -2616,6 +2856,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2616,6 +2856,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2616 | wasm.mapFunctionTable(); | 2856 | wasm.mapFunctionTable(); |
| 2617 | try wasm.mergeSections(); | 2857 | try wasm.mergeSections(); |
| 2618 | try wasm.mergeTypes(); | 2858 | try wasm.mergeTypes(); |
| | 2859 | try wasm.initializeCallCtorsFunction(); |
| 2619 | try wasm.setupExports(); | 2860 | try wasm.setupExports(); |
| 2620 | try wasm.writeToFile(enabled_features, emit_features_count, arena); | 2861 | try wasm.writeToFile(enabled_features, emit_features_count, arena); |
| 2621 | } | 2862 | } |
| ... | @@ -2810,7 +3051,7 @@ fn writeToFile( | ... | @@ -2810,7 +3051,7 @@ fn writeToFile( |
| 2810 | if (wasm.function_table.count() > 0) { | 3051 | if (wasm.function_table.count() > 0) { |
| 2811 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 3052 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 2812 | | 3053 | |
| 2813 | const table_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?; | 3054 | const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?; |
| 2814 | const table_sym = table_loc.getSymbol(wasm); | 3055 | const table_sym = table_loc.getSymbol(wasm); |
| 2815 | | 3056 | |
| 2816 | var flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually | 3057 | var flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually |
| ... | @@ -2849,10 +3090,12 @@ fn writeToFile( | ... | @@ -2849,10 +3090,12 @@ fn writeToFile( |
| 2849 | defer sorted_atoms.deinit(); | 3090 | defer sorted_atoms.deinit(); |
| 2850 | | 3091 | |
| 2851 | while (true) { | 3092 | while (true) { |
| 2852 | if (!is_obj) { | 3093 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { |
| 2853 | atom.resolveRelocs(wasm); | 3094 | if (!is_obj) { |
| | 3095 | atom.resolveRelocs(wasm); |
| | 3096 | } |
| | 3097 | sorted_atoms.appendAssumeCapacity(atom); |
| 2854 | } | 3098 | } |
| 2855 | sorted_atoms.appendAssumeCapacity(atom); | | |
| 2856 | atom = atom.next orelse break; | 3099 | atom = atom.next orelse break; |
| 2857 | } | 3100 | } |
| 2858 | | 3101 | |
| ... | @@ -2893,10 +3136,11 @@ fn writeToFile( | ... | @@ -2893,10 +3136,11 @@ fn writeToFile( |
| 2893 | // do not output 'bss' section unless we import memory and therefore | 3136 | // do not output 'bss' section unless we import memory and therefore |
| 2894 | // want to guarantee the data is zero initialized | 3137 | // want to guarantee the data is zero initialized |
| 2895 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; | 3138 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; |
| 2896 | segment_count += 1; | | |
| 2897 | const atom_index = entry.value_ptr.*; | 3139 | const atom_index = entry.value_ptr.*; |
| 2898 | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); | | |
| 2899 | const segment = wasm.segments.items[atom_index]; | 3140 | const segment = wasm.segments.items[atom_index]; |
| | 3141 | if (segment.size == 0) continue; // do not emit empty segments |
| | 3142 | segment_count += 1; |
| | 3143 | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); |
| 2900 | | 3144 | |
| 2901 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) | 3145 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) |
| 2902 | try leb.writeULEB128(binary_writer, @as(u32, 0)); | 3146 | try leb.writeULEB128(binary_writer, @as(u32, 0)); |
| ... | @@ -3166,6 +3410,8 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem | ... | @@ -3166,6 +3410,8 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem |
| 3166 | // bss section is not emitted when this condition holds true, so we also | 3410 | // bss section is not emitted when this condition holds true, so we also |
| 3167 | // do not output a name for it. | 3411 | // do not output a name for it. |
| 3168 | if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; | 3412 | if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; |
| | 3413 | // Synthetic segments are not emitted |
| | 3414 | if (std.mem.eql(u8, key, ".synthetic")) continue; |
| 3169 | segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); | 3415 | segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); |
| 3170 | data_segment_index += 1; | 3416 | data_segment_index += 1; |
| 3171 | } | 3417 | } |
| ... | @@ -3896,8 +4142,8 @@ pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { | ... | @@ -3896,8 +4142,8 @@ pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { |
| 3896 | return null; | 4142 | return null; |
| 3897 | } | 4143 | } |
| 3898 | | 4144 | |
| 3899 | /// Searches for an a matching function signature, when not found | 4145 | /// Searches for a matching function signature. When no matching signature is found, |
| 3900 | /// a new entry will be made. The index of the existing/new signature will be returned. | 4146 | /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. |
| 3901 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { | 4147 | pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { |
| 3902 | if (wasm.getTypeIndex(func_type)) |index| { | 4148 | if (wasm.getTypeIndex(func_type)) |index| { |
| 3903 | return index; | 4149 | return index; |