| ... | ... | @@ -16,9 +16,9 @@ const build_options = @import("build_options"); |
| 16 | 16 | const std = @import("std"); |
| 17 | 17 | const Allocator = std.mem.Allocator; |
| 18 | 18 | const mem = std.mem; |
| 19 | | const leb = std.leb; |
| 20 | 19 | const log = std.log.scoped(.link); |
| 21 | 20 | const assert = std.debug.assert; |
| 21 | const Writer = std.Io.Writer; |
| 22 | 22 | |
| 23 | 23 | /// Ordered list of data segments that will appear in the final binary. |
| 24 | 24 | /// When sorted, to-be-merged segments will be made adjacent. |
| ... | ... | @@ -557,13 +557,12 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 557 | 557 | // Index of the data section. Used to tell relocation table where the section lives. |
| 558 | 558 | var data_section_index: ?u32 = null; |
| 559 | 559 | |
| 560 | | const binary_bytes = &f.binary_bytes; |
| 561 | | assert(binary_bytes.items.len == 0); |
| 560 | assert(f.binary_bytes.items.len == 0); |
| 561 | var aw: Writer.Allocating = .fromArrayList(gpa, &f.binary_bytes); |
| 562 | defer f.binary_bytes = aw.toArrayList(); |
| 563 | const w = &aw.writer; |
| 562 | 564 | |
| 563 | | try binary_bytes.appendSlice(gpa, &std.wasm.magic ++ &std.wasm.version); |
| 564 | | assert(binary_bytes.items.len == 8); |
| 565 | | |
| 566 | | const binary_writer = binary_bytes.writer(gpa); |
| 565 | try w.writeAll(&std.wasm.magic ++ &std.wasm.version); |
| 567 | 566 | |
| 568 | 567 | // Type section. |
| 569 | 568 | for (f.function_imports.values()) |id| { |
| ... | ... | @@ -573,22 +572,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 573 | 572 | try f.func_types.put(gpa, function.typeIndex(wasm), {}); |
| 574 | 573 | } |
| 575 | 574 | if (f.func_types.entries.len != 0) { |
| 576 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 575 | const header_offset = try reserveVecSectionHeader(w); |
| 577 | 576 | for (f.func_types.keys()) |func_type_index| { |
| 578 | 577 | const func_type = func_type_index.ptr(wasm); |
| 579 | | try leb.writeUleb128(binary_writer, std.wasm.function_type); |
| 578 | try w.writeLeb128(std.wasm.function_type); |
| 580 | 579 | const params = func_type.params.slice(wasm); |
| 581 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(params.len))); |
| 582 | | for (params) |param_ty| { |
| 583 | | try leb.writeUleb128(binary_writer, @intFromEnum(param_ty)); |
| 584 | | } |
| 580 | try w.writeLeb128(params.len); |
| 581 | for (params) |param_ty| try w.writeLeb128(@intFromEnum(param_ty)); |
| 585 | 582 | const returns = func_type.returns.slice(wasm); |
| 586 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(returns.len))); |
| 587 | | for (returns) |ret_ty| { |
| 588 | | try leb.writeUleb128(binary_writer, @intFromEnum(ret_ty)); |
| 589 | | } |
| 583 | try w.writeLeb128(returns.len); |
| 584 | for (returns) |ret_ty| try w.writeLeb128(@intFromEnum(ret_ty)); |
| 590 | 585 | } |
| 591 | | replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(f.func_types.entries.len)); |
| 586 | replaceVecSectionHeader(&aw, header_offset, .type, @intCast(f.func_types.entries.len)); |
| 592 | 587 | section_index += 1; |
| 593 | 588 | } |
| 594 | 589 | |
| ... | ... | @@ -601,42 +596,42 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 601 | 596 | // Import section |
| 602 | 597 | { |
| 603 | 598 | var total_imports: usize = 0; |
| 604 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 599 | const header_offset = try reserveVecSectionHeader(w); |
| 605 | 600 | |
| 606 | 601 | for (f.function_imports.values()) |id| { |
| 607 | 602 | const module_name = id.moduleName(wasm).slice(wasm).?; |
| 608 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); |
| 609 | | try binary_writer.writeAll(module_name); |
| 603 | try w.writeLeb128(module_name.len); |
| 604 | try w.writeAll(module_name); |
| 610 | 605 | |
| 611 | 606 | const name = id.importName(wasm).slice(wasm); |
| 612 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 613 | | try binary_writer.writeAll(name); |
| 607 | try w.writeLeb128(name.len); |
| 608 | try w.writeAll(name); |
| 614 | 609 | |
| 615 | | try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function)); |
| 610 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.function)); |
| 616 | 611 | const type_index: FuncTypeIndex = .fromTypeIndex(id.functionType(wasm), f); |
| 617 | | try leb.writeUleb128(binary_writer, @intFromEnum(type_index)); |
| 612 | try w.writeLeb128(@intFromEnum(type_index)); |
| 618 | 613 | } |
| 619 | 614 | total_imports += f.function_imports.entries.len; |
| 620 | 615 | |
| 621 | 616 | for (wasm.table_imports.values()) |id| { |
| 622 | 617 | const table_import = id.value(wasm); |
| 623 | 618 | const module_name = table_import.module_name.slice(wasm); |
| 624 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); |
| 625 | | try binary_writer.writeAll(module_name); |
| 619 | try w.writeLeb128(module_name.len); |
| 620 | try w.writeAll(module_name); |
| 626 | 621 | |
| 627 | 622 | const name = table_import.name.slice(wasm); |
| 628 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 629 | | try binary_writer.writeAll(name); |
| 623 | try w.writeLeb128(name.len); |
| 624 | try w.writeAll(name); |
| 630 | 625 | |
| 631 | | try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table)); |
| 632 | | try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to()))); |
| 633 | | try emitLimits(gpa, binary_bytes, table_import.limits()); |
| 626 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.table)); |
| 627 | try w.writeLeb128(@intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to()))); |
| 628 | try emitLimits(w, table_import.limits()); |
| 634 | 629 | } |
| 635 | 630 | total_imports += wasm.table_imports.entries.len; |
| 636 | 631 | |
| 637 | 632 | if (import_memory) { |
| 638 | 633 | const name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory; |
| 639 | | try emitMemoryImport(wasm, binary_bytes, name, &.{ |
| 634 | try emitMemoryImport(wasm, w, name, &.{ |
| 640 | 635 | // TODO the import_memory option needs to specify from which module |
| 641 | 636 | .module_name = wasm.object_host_name.unwrap().?, |
| 642 | 637 | .limits_min = wasm.memories.limits.min, |
| ... | ... | @@ -650,215 +645,209 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 650 | 645 | |
| 651 | 646 | for (f.global_imports.values()) |id| { |
| 652 | 647 | const module_name = id.moduleName(wasm).slice(wasm).?; |
| 653 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); |
| 654 | | try binary_writer.writeAll(module_name); |
| 648 | try w.writeLeb128(module_name.len); |
| 649 | try w.writeAll(module_name); |
| 655 | 650 | |
| 656 | 651 | const name = id.importName(wasm).slice(wasm); |
| 657 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 658 | | try binary_writer.writeAll(name); |
| 652 | try w.writeLeb128(name.len); |
| 653 | try w.writeAll(name); |
| 659 | 654 | |
| 660 | | try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global)); |
| 655 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.global)); |
| 661 | 656 | const global_type = id.globalType(wasm); |
| 662 | | try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype))); |
| 663 | | try binary_writer.writeByte(@intFromBool(global_type.mutable)); |
| 657 | try w.writeLeb128(@intFromEnum(global_type.valtype)); |
| 658 | try w.writeByte(@intFromBool(global_type.mutable)); |
| 664 | 659 | } |
| 665 | 660 | total_imports += f.global_imports.entries.len; |
| 666 | 661 | |
| 667 | 662 | if (total_imports > 0) { |
| 668 | | replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports)); |
| 663 | replaceVecSectionHeader(&aw, header_offset, .import, @intCast(total_imports)); |
| 669 | 664 | section_index += 1; |
| 670 | 665 | } else { |
| 671 | | binary_bytes.shrinkRetainingCapacity(header_offset); |
| 666 | aw.shrinkRetainingCapacity(header_offset); |
| 672 | 667 | } |
| 673 | 668 | } |
| 674 | 669 | |
| 675 | 670 | // Function section |
| 676 | 671 | if (wasm.functions.count() != 0) { |
| 677 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 672 | const header_offset = try reserveVecSectionHeader(w); |
| 678 | 673 | for (wasm.functions.keys()) |function| { |
| 679 | 674 | const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f); |
| 680 | | try leb.writeUleb128(binary_writer, @intFromEnum(index)); |
| 675 | try w.writeLeb128(@intFromEnum(index)); |
| 681 | 676 | } |
| 682 | 677 | |
| 683 | | replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count())); |
| 678 | replaceVecSectionHeader(&aw, header_offset, .function, @intCast(wasm.functions.count())); |
| 684 | 679 | section_index += 1; |
| 685 | 680 | } |
| 686 | 681 | |
| 687 | 682 | // Table section |
| 688 | 683 | if (wasm.tables.entries.len > 0) { |
| 689 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 684 | const header_offset = try reserveVecSectionHeader(w); |
| 690 | 685 | |
| 691 | 686 | for (wasm.tables.keys()) |table| { |
| 692 | | try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm)))); |
| 693 | | try emitLimits(gpa, binary_bytes, table.limits(wasm)); |
| 687 | try w.writeLeb128(@intFromEnum(table.refType(wasm))); |
| 688 | try emitLimits(w, table.limits(wasm)); |
| 694 | 689 | } |
| 695 | 690 | |
| 696 | | replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len)); |
| 691 | replaceVecSectionHeader(&aw, header_offset, .table, @intCast(wasm.tables.entries.len)); |
| 697 | 692 | section_index += 1; |
| 698 | 693 | } |
| 699 | 694 | |
| 700 | 695 | // Memory section. wasm currently only supports 1 linear memory segment. |
| 701 | 696 | if (!import_memory) { |
| 702 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 703 | | try emitLimits(gpa, binary_bytes, wasm.memories.limits); |
| 704 | | replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1); |
| 697 | const header_offset = try reserveVecSectionHeader(w); |
| 698 | try emitLimits(w, wasm.memories.limits); |
| 699 | replaceVecSectionHeader(&aw, header_offset, .memory, 1); |
| 705 | 700 | section_index += 1; |
| 706 | 701 | } |
| 707 | 702 | |
| 708 | 703 | // Global section. |
| 709 | 704 | const globals_len: u32 = @intCast(wasm.globals.entries.len); |
| 710 | 705 | if (globals_len > 0) { |
| 711 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 706 | const header_offset = try reserveVecSectionHeader(w); |
| 712 | 707 | |
| 713 | 708 | for (wasm.globals.keys()) |global_resolution| { |
| 714 | 709 | switch (global_resolution.unpack(wasm)) { |
| 715 | 710 | .unresolved => unreachable, |
| 716 | | .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base), |
| 717 | | .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end), |
| 718 | | .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer), |
| 719 | | .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)), |
| 720 | | .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?), |
| 721 | | .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?), |
| 711 | .__heap_base => try appendGlobal(w, false, virtual_addrs.heap_base), |
| 712 | .__heap_end => try appendGlobal(w, false, virtual_addrs.heap_end), |
| 713 | .__stack_pointer => try appendGlobal(w, true, virtual_addrs.stack_pointer), |
| 714 | .__tls_align => try appendGlobal(w, false, @intCast(virtual_addrs.tls_align.toByteUnits().?)), |
| 715 | .__tls_base => try appendGlobal(w, true, virtual_addrs.tls_base.?), |
| 716 | .__tls_size => try appendGlobal(w, false, virtual_addrs.tls_size.?), |
| 722 | 717 | .object_global => |i| { |
| 723 | 718 | const global = i.ptr(wasm); |
| 724 | | try binary_bytes.appendSlice(gpa, &.{ |
| 719 | try w.writeAll(&.{ |
| 725 | 720 | @intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())), |
| 726 | 721 | @intFromBool(global.flags.global_type.mutable), |
| 727 | 722 | }); |
| 728 | | try emitExpr(wasm, binary_bytes, global.expr); |
| 723 | try emitExpr(wasm, w, global.expr); |
| 729 | 724 | }, |
| 730 | 725 | .nav_exe => unreachable, // Zig source code currently cannot represent this. |
| 731 | 726 | .nav_obj => unreachable, // Zig source code currently cannot represent this. |
| 732 | 727 | } |
| 733 | 728 | } |
| 734 | 729 | |
| 735 | | replaceVecSectionHeader(binary_bytes, header_offset, .global, globals_len); |
| 730 | replaceVecSectionHeader(&aw, header_offset, .global, globals_len); |
| 736 | 731 | section_index += 1; |
| 737 | 732 | } |
| 738 | 733 | |
| 739 | 734 | // Export section |
| 740 | 735 | { |
| 741 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 736 | const header_offset = try reserveVecSectionHeader(w); |
| 742 | 737 | var exports_len: usize = 0; |
| 743 | 738 | |
| 744 | 739 | for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| { |
| 745 | 740 | const name = exp_name.slice(wasm); |
| 746 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 747 | | try binary_bytes.appendSlice(gpa, name); |
| 748 | | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); |
| 741 | try w.writeLeb128(name.len); |
| 742 | try w.writeAll(name); |
| 743 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.function)); |
| 749 | 744 | const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index); |
| 750 | | try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); |
| 745 | try w.writeLeb128(@intFromEnum(func_index)); |
| 751 | 746 | } |
| 752 | 747 | exports_len += wasm.function_exports.entries.len; |
| 753 | 748 | |
| 754 | 749 | if (wasm.export_table and f.indirect_function_table.entries.len > 0) { |
| 755 | 750 | const name = "__indirect_function_table"; |
| 756 | 751 | const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); |
| 757 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 758 | | try binary_bytes.appendSlice(gpa, name); |
| 759 | | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table)); |
| 760 | | try leb.writeUleb128(binary_writer, index); |
| 752 | try w.writeLeb128(name.len); |
| 753 | try w.writeAll(name); |
| 754 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.table)); |
| 755 | try w.writeLeb128(index); |
| 761 | 756 | exports_len += 1; |
| 762 | 757 | } |
| 763 | 758 | |
| 764 | 759 | if (export_memory) { |
| 765 | 760 | const name = "memory"; |
| 766 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 767 | | try binary_bytes.appendSlice(gpa, name); |
| 768 | | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); |
| 769 | | try leb.writeUleb128(binary_writer, @as(u32, 0)); |
| 761 | try w.writeLeb128(name.len); |
| 762 | try w.writeAll(name); |
| 763 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.memory)); |
| 764 | try w.writeUleb128(0); |
| 770 | 765 | exports_len += 1; |
| 771 | 766 | } |
| 772 | 767 | |
| 773 | 768 | for (wasm.global_exports.items) |exp| { |
| 774 | 769 | const name = exp.name.slice(wasm); |
| 775 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 776 | | try binary_bytes.appendSlice(gpa, name); |
| 777 | | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global)); |
| 778 | | try leb.writeUleb128(binary_writer, @intFromEnum(exp.global_index)); |
| 770 | try w.writeLeb128(name.len); |
| 771 | try w.writeAll(name); |
| 772 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.global)); |
| 773 | try w.writeLeb128(@intFromEnum(exp.global_index)); |
| 779 | 774 | } |
| 780 | 775 | exports_len += wasm.global_exports.items.len; |
| 781 | 776 | |
| 782 | 777 | if (exports_len > 0) { |
| 783 | | replaceVecSectionHeader(binary_bytes, header_offset, .@"export", @intCast(exports_len)); |
| 778 | replaceVecSectionHeader(&aw, header_offset, .@"export", @intCast(exports_len)); |
| 784 | 779 | section_index += 1; |
| 785 | 780 | } else { |
| 786 | | binary_bytes.shrinkRetainingCapacity(header_offset); |
| 781 | aw.shrinkRetainingCapacity(header_offset); |
| 787 | 782 | } |
| 788 | 783 | } |
| 789 | 784 | |
| 790 | 785 | // start section |
| 791 | 786 | if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| { |
| 792 | | try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @enumFromInt(func_index))); |
| 787 | try emitStartSection(&aw, .fromFunctionIndex(wasm, @enumFromInt(func_index))); |
| 793 | 788 | } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| { |
| 794 | | try emitStartSection(gpa, binary_bytes, func_index); |
| 789 | try emitStartSection(&aw, func_index); |
| 795 | 790 | } |
| 796 | 791 | |
| 797 | 792 | // element section |
| 798 | 793 | if (f.indirect_function_table.entries.len > 0) { |
| 799 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 794 | const header_offset = try reserveVecSectionHeader(w); |
| 800 | 795 | |
| 801 | 796 | // indirect function table elements |
| 802 | 797 | const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); |
| 803 | 798 | // passive with implicit 0-index table or set table index manually |
| 804 | 799 | const flags: u32 = if (table_index == 0) 0x0 else 0x02; |
| 805 | | try leb.writeUleb128(binary_writer, flags); |
| 806 | | if (flags == 0x02) { |
| 807 | | try leb.writeUleb128(binary_writer, table_index); |
| 808 | | } |
| 800 | try w.writeLeb128(flags); |
| 801 | if (flags == 0x02) try w.writeLeb128(table_index); |
| 809 | 802 | // We start at index 1, so unresolved function pointers are invalid |
| 810 | | try emitInit(binary_writer, .{ .i32_const = 1 }); |
| 811 | | if (flags == 0x02) { |
| 812 | | try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref |
| 813 | | } |
| 814 | | try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.entries.len))); |
| 815 | | for (f.indirect_function_table.keys()) |func_index| { |
| 816 | | try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); |
| 817 | | } |
| 803 | try emitInit(w, .{ .i32_const = 1 }); |
| 804 | if (flags == 0x02) try w.writeUleb128(0); // represents funcref |
| 805 | try w.writeLeb128(f.indirect_function_table.entries.len); |
| 806 | for (f.indirect_function_table.keys()) |func_index| try w.writeLeb128(@intFromEnum(func_index)); |
| 818 | 807 | |
| 819 | | replaceVecSectionHeader(binary_bytes, header_offset, .element, 1); |
| 808 | replaceVecSectionHeader(&aw, header_offset, .element, 1); |
| 820 | 809 | section_index += 1; |
| 821 | 810 | } |
| 822 | 811 | |
| 823 | 812 | // When the shared-memory option is enabled, we *must* emit the 'data count' section. |
| 824 | 813 | if (f.data_segment_groups.items.len > 0) { |
| 825 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 826 | | replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len)); |
| 814 | const header_offset = try reserveVecSectionHeader(w); |
| 815 | replaceVecSectionHeader(&aw, header_offset, .data_count, @intCast(f.data_segment_groups.items.len)); |
| 827 | 816 | } |
| 828 | 817 | |
| 829 | 818 | // Code section. |
| 830 | 819 | if (wasm.functions.count() != 0) { |
| 831 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 820 | const header_offset = try reserveVecSectionHeader(w); |
| 832 | 821 | |
| 833 | 822 | for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) { |
| 834 | 823 | .unresolved => unreachable, |
| 835 | 824 | .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"), |
| 836 | 825 | .__wasm_call_ctors => { |
| 837 | | const code_start = try reserveSize(gpa, binary_bytes); |
| 838 | | defer replaceSize(binary_bytes, code_start); |
| 839 | | try emitCallCtorsFunction(wasm, binary_bytes); |
| 826 | const code_start = try reserveSizeHeader(w); |
| 827 | defer replaceSizeHeader(&aw, code_start); |
| 828 | try emitCallCtorsFunction(wasm, w); |
| 840 | 829 | }, |
| 841 | 830 | .__wasm_init_memory => { |
| 842 | | const code_start = try reserveSize(gpa, binary_bytes); |
| 843 | | defer replaceSize(binary_bytes, code_start); |
| 844 | | try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs); |
| 831 | const code_start = try reserveSizeHeader(w); |
| 832 | defer replaceSizeHeader(&aw, code_start); |
| 833 | try emitInitMemoryFunction(wasm, w, &virtual_addrs); |
| 845 | 834 | }, |
| 846 | 835 | .__wasm_init_tls => { |
| 847 | | const code_start = try reserveSize(gpa, binary_bytes); |
| 848 | | defer replaceSize(binary_bytes, code_start); |
| 849 | | try emitInitTlsFunction(wasm, binary_bytes); |
| 836 | const code_start = try reserveSizeHeader(w); |
| 837 | defer replaceSizeHeader(&aw, code_start); |
| 838 | try emitInitTlsFunction(wasm, w); |
| 850 | 839 | }, |
| 851 | 840 | .object_function => |i| { |
| 852 | 841 | const ptr = i.ptr(wasm); |
| 853 | 842 | const code = ptr.code.slice(wasm); |
| 854 | | try leb.writeUleb128(binary_writer, code.len); |
| 855 | | const code_start = binary_bytes.items.len; |
| 856 | | try binary_bytes.appendSlice(gpa, code); |
| 857 | | if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); |
| 843 | try w.writeLeb128(code.len); |
| 844 | const code_start = w.end; |
| 845 | try w.writeAll(code); |
| 846 | if (!is_obj) applyRelocs(aw.getWritten()[code_start..], ptr.offset, ptr.relocations(wasm), wasm); |
| 858 | 847 | }, |
| 859 | 848 | .zcu_func => |i| { |
| 860 | | const code_start = try reserveSize(gpa, binary_bytes); |
| 861 | | defer replaceSize(binary_bytes, code_start); |
| 849 | const code_start = try reserveSizeHeader(w); |
| 850 | defer replaceSizeHeader(&aw, code_start); |
| 862 | 851 | |
| 863 | 852 | log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?}); |
| 864 | 853 | |
| ... | ... | @@ -867,7 +856,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 867 | 856 | const ip_index = i.key(wasm).*; |
| 868 | 857 | switch (ip.indexToKey(ip_index)) { |
| 869 | 858 | .enum_type => { |
| 870 | | try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); |
| 859 | try emitTagNameFunction(wasm, w, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); |
| 871 | 860 | }, |
| 872 | 861 | else => { |
| 873 | 862 | const func = i.value(wasm).function; |
| ... | ... | @@ -882,13 +871,13 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 882 | 871 | .func_tys = undefined, |
| 883 | 872 | .error_name_table_ref_count = undefined, |
| 884 | 873 | }; |
| 885 | | try mir.lower(wasm, binary_bytes); |
| 874 | try mir.lower(wasm, w); |
| 886 | 875 | }, |
| 887 | 876 | } |
| 888 | 877 | }, |
| 889 | 878 | }; |
| 890 | 879 | |
| 891 | | replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len)); |
| 880 | replaceVecSectionHeader(&aw, header_offset, .code, @intCast(wasm.functions.entries.len)); |
| 892 | 881 | code_section_index = section_index; |
| 893 | 882 | section_index += 1; |
| 894 | 883 | } |
| ... | ... | @@ -924,7 +913,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 924 | 913 | |
| 925 | 914 | // Data section. |
| 926 | 915 | if (f.data_segment_groups.items.len != 0) { |
| 927 | | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 916 | const header_offset = try reserveVecSectionHeader(w); |
| 928 | 917 | |
| 929 | 918 | var group_index: u32 = 0; |
| 930 | 919 | var segment_offset: u32 = 0; |
| ... | ... | @@ -932,7 +921,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 932 | 921 | var group_end_addr = f.data_segment_groups.items[group_index].end_addr; |
| 933 | 922 | for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| { |
| 934 | 923 | if (segment_vaddr >= group_end_addr) { |
| 935 | | try binary_bytes.appendNTimes(gpa, 0, group_end_addr - group_start_addr - segment_offset); |
| 924 | try w.splatByteAll(0, group_end_addr - group_start_addr - segment_offset); |
| 936 | 925 | group_index += 1; |
| 937 | 926 | if (group_index >= f.data_segment_groups.items.len) { |
| 938 | 927 | // All remaining segments are zero. |
| ... | ... | @@ -946,12 +935,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 946 | 935 | const group_size = group_end_addr - group_start_addr; |
| 947 | 936 | log.debug("emit data section group, {d} bytes", .{group_size}); |
| 948 | 937 | const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active; |
| 949 | | try leb.writeUleb128(binary_writer, @intFromEnum(flags)); |
| 938 | try w.writeLeb128(@intFromEnum(flags)); |
| 950 | 939 | // Passive segments are initialized at runtime. |
| 951 | | if (flags != .passive) { |
| 952 | | try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) }); |
| 953 | | } |
| 954 | | try leb.writeUleb128(binary_writer, group_size); |
| 940 | if (flags != .passive) try emitInit(w, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) }); |
| 941 | try w.writeLeb128(group_size); |
| 955 | 942 | } |
| 956 | 943 | if (segment_id.isEmpty(wasm)) { |
| 957 | 944 | // It counted for virtual memory but it does not go into the binary. |
| ... | ... | @@ -960,62 +947,62 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 960 | 947 | |
| 961 | 948 | // Padding for alignment. |
| 962 | 949 | const needed_offset = segment_vaddr - group_start_addr; |
| 963 | | try binary_bytes.appendNTimes(gpa, 0, needed_offset - segment_offset); |
| 950 | try w.splatByteAll(0, needed_offset - segment_offset); |
| 964 | 951 | segment_offset = needed_offset; |
| 965 | 952 | |
| 966 | | const code_start = binary_bytes.items.len; |
| 953 | const code_start = w.end; |
| 967 | 954 | append: { |
| 968 | 955 | const code = switch (segment_id.unpack(wasm)) { |
| 969 | 956 | .__heap_base => { |
| 970 | | mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_base, .little); |
| 957 | try w.writeInt(u32, virtual_addrs.heap_base, .little); |
| 971 | 958 | break :append; |
| 972 | 959 | }, |
| 973 | 960 | .__heap_end => { |
| 974 | | mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_end, .little); |
| 961 | try w.writeInt(u32, virtual_addrs.heap_end, .little); |
| 975 | 962 | break :append; |
| 976 | 963 | }, |
| 977 | 964 | .__zig_error_names => { |
| 978 | | try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items); |
| 965 | try w.writeAll(wasm.error_name_bytes.items); |
| 979 | 966 | break :append; |
| 980 | 967 | }, |
| 981 | 968 | .__zig_error_name_table => { |
| 982 | 969 | if (is_obj) @panic("TODO error name table reloc"); |
| 983 | 970 | const base = f.data_segments.get(.__zig_error_names).?; |
| 984 | 971 | if (!is64) { |
| 985 | | try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32); |
| 972 | try emitTagNameTable(w, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32); |
| 986 | 973 | } else { |
| 987 | | try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64); |
| 974 | try emitTagNameTable(w, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64); |
| 988 | 975 | } |
| 989 | 976 | break :append; |
| 990 | 977 | }, |
| 991 | 978 | .__zig_tag_names => { |
| 992 | | try binary_bytes.appendSlice(gpa, wasm.tag_name_bytes.items); |
| 979 | try w.writeAll(wasm.tag_name_bytes.items); |
| 993 | 980 | break :append; |
| 994 | 981 | }, |
| 995 | 982 | .__zig_tag_name_table => { |
| 996 | 983 | if (is_obj) @panic("TODO tag name table reloc"); |
| 997 | 984 | const base = f.data_segments.get(.__zig_tag_names).?; |
| 998 | 985 | if (!is64) { |
| 999 | | try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32); |
| 986 | try emitTagNameTable(w, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32); |
| 1000 | 987 | } else { |
| 1001 | | try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64); |
| 988 | try emitTagNameTable(w, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64); |
| 1002 | 989 | } |
| 1003 | 990 | break :append; |
| 1004 | 991 | }, |
| 1005 | 992 | .object => |i| { |
| 1006 | 993 | const ptr = i.ptr(wasm); |
| 1007 | | try binary_bytes.appendSlice(gpa, ptr.payload.slice(wasm)); |
| 1008 | | if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); |
| 994 | try w.writeAll(ptr.payload.slice(wasm)); |
| 995 | if (!is_obj) applyRelocs(aw.getWritten()[code_start..], ptr.offset, ptr.relocations(wasm), wasm); |
| 1009 | 996 | break :append; |
| 1010 | 997 | }, |
| 1011 | 998 | inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code, |
| 1012 | 999 | }; |
| 1013 | | try binary_bytes.appendSlice(gpa, code.slice(wasm)); |
| 1000 | try w.writeAll(code.slice(wasm)); |
| 1014 | 1001 | } |
| 1015 | | segment_offset += @intCast(binary_bytes.items.len - code_start); |
| 1002 | segment_offset += @intCast(w.end - code_start); |
| 1016 | 1003 | } |
| 1017 | 1004 | |
| 1018 | | replaceVecSectionHeader(binary_bytes, header_offset, .data, @intCast(f.data_segment_groups.items.len)); |
| 1005 | replaceVecSectionHeader(&aw, header_offset, .data, @intCast(f.data_segment_groups.items.len)); |
| 1019 | 1006 | data_section_index = section_index; |
| 1020 | 1007 | section_index += 1; |
| 1021 | 1008 | } |
| ... | ... | @@ -1023,7 +1010,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 1023 | 1010 | if (is_obj) { |
| 1024 | 1011 | @panic("TODO emit link section for object file and emit modified relocations"); |
| 1025 | 1012 | } else if (comp.config.debug_format != .strip) { |
| 1026 | | try emitNameSection(wasm, f.data_segment_groups.items, binary_bytes); |
| 1013 | try emitNameSection(wasm, &aw, f.data_segment_groups.items); |
| 1027 | 1014 | } |
| 1028 | 1015 | |
| 1029 | 1016 | if (comp.config.debug_format != .strip) { |
| ... | ... | @@ -1033,17 +1020,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 1033 | 1020 | .none => {}, |
| 1034 | 1021 | .fast => { |
| 1035 | 1022 | var id: [16]u8 = undefined; |
| 1036 | | std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{}); |
| 1023 | std.crypto.hash.sha3.TurboShake128(null).hash(w.buffered(), &id, .{}); |
| 1037 | 1024 | var uuid: [36]u8 = undefined; |
| 1038 | 1025 | _ = try std.fmt.bufPrint(&uuid, "{x}-{x}-{x}-{x}-{x}", .{ |
| 1039 | 1026 | id[0..4], id[4..6], id[6..8], id[8..10], id[10..], |
| 1040 | 1027 | }); |
| 1041 | | try emitBuildIdSection(gpa, binary_bytes, &uuid); |
| 1028 | try emitBuildIdSection(&aw, &uuid); |
| 1042 | 1029 | }, |
| 1043 | 1030 | .hexstring => |hs| { |
| 1044 | 1031 | var buffer: [32 * 2]u8 = undefined; |
| 1045 | 1032 | const str = std.fmt.bufPrint(&buffer, "{x}", .{hs.toSlice()}) catch unreachable; |
| 1046 | | try emitBuildIdSection(gpa, binary_bytes, str); |
| 1033 | try emitBuildIdSection(&aw, str); |
| 1047 | 1034 | }, |
| 1048 | 1035 | else => |mode| { |
| 1049 | 1036 | var err = try diags.addErrorWithNotes(0); |
| ... | ... | @@ -1054,14 +1041,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 1054 | 1041 | var debug_bytes = std.ArrayList(u8).init(gpa); |
| 1055 | 1042 | defer debug_bytes.deinit(); |
| 1056 | 1043 | |
| 1057 | | try emitProducerSection(gpa, binary_bytes); |
| 1058 | | try emitFeaturesSection(gpa, binary_bytes, target); |
| 1044 | try emitProducerSection(&aw); |
| 1045 | emitFeaturesSection(&aw, target) catch |err| switch (err) { |
| 1046 | error.WriteFailed => return error.OutOfMemory, |
| 1047 | }; |
| 1059 | 1048 | } |
| 1060 | 1049 | |
| 1061 | 1050 | // Finally, write the entire binary into the file. |
| 1062 | 1051 | const file = wasm.base.file.?; |
| 1063 | | try file.pwriteAll(binary_bytes.items, 0); |
| 1064 | | try file.setEndPos(binary_bytes.items.len); |
| 1052 | const contents = aw.getWritten(); |
| 1053 | try file.setEndPos(contents.len); |
| 1054 | try file.pwriteAll(contents, 0); |
| 1065 | 1055 | } |
| 1066 | 1056 | |
| 1067 | 1057 | const VirtualAddrs = struct { |
| ... | ... | @@ -1076,170 +1066,155 @@ const VirtualAddrs = struct { |
| 1076 | 1066 | |
| 1077 | 1067 | fn emitNameSection( |
| 1078 | 1068 | wasm: *Wasm, |
| 1069 | aw: *Writer.Allocating, |
| 1079 | 1070 | data_segment_groups: []const DataSegmentGroup, |
| 1080 | | binary_bytes: *std.ArrayListUnmanaged(u8), |
| 1081 | 1071 | ) !void { |
| 1082 | 1072 | const f = &wasm.flush_buffer; |
| 1083 | | const comp = wasm.base.comp; |
| 1084 | | const gpa = comp.gpa; |
| 1073 | const w = &aw.writer; |
| 1074 | const header_offset = try reserveSectionHeader(w); |
| 1075 | defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom)); |
| 1085 | 1076 | |
| 1086 | | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1087 | | defer writeCustomSectionHeader(binary_bytes, header_offset); |
| 1088 | | |
| 1089 | | const name_name = "name"; |
| 1090 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len)); |
| 1091 | | try binary_bytes.appendSlice(gpa, name_name); |
| 1077 | const section_name = "name"; |
| 1078 | try w.writeLeb128(section_name.len); |
| 1079 | try w.writeAll(section_name); |
| 1092 | 1080 | |
| 1093 | 1081 | { |
| 1094 | | const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1095 | | defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function)); |
| 1096 | | |
| 1097 | | const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len); |
| 1098 | | try leb.writeUleb128(binary_bytes.writer(gpa), total_functions); |
| 1082 | const sub_header_offset = try reserveSectionHeader(w); |
| 1083 | defer replaceSectionHeader(aw, sub_header_offset, @intFromEnum(std.wasm.NameSubsection.function)); |
| 1099 | 1084 | |
| 1085 | try w.writeLeb128(f.function_imports.entries.len + wasm.functions.entries.len); |
| 1100 | 1086 | for (f.function_imports.keys(), 0..) |name_index, function_index| { |
| 1101 | 1087 | const name = name_index.slice(wasm); |
| 1102 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); |
| 1103 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); |
| 1104 | | try binary_bytes.appendSlice(gpa, name); |
| 1088 | try w.writeLeb128(function_index); |
| 1089 | try w.writeLeb128(name.len); |
| 1090 | try w.writeAll(name); |
| 1105 | 1091 | } |
| 1106 | 1092 | for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| { |
| 1107 | 1093 | const name = resolution.name(wasm).?; |
| 1108 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); |
| 1109 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); |
| 1110 | | try binary_bytes.appendSlice(gpa, name); |
| 1094 | try w.writeLeb128(function_index); |
| 1095 | try w.writeLeb128(name.len); |
| 1096 | try w.writeAll(name); |
| 1111 | 1097 | } |
| 1112 | 1098 | } |
| 1113 | 1099 | |
| 1114 | 1100 | { |
| 1115 | | const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1116 | | defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global)); |
| 1117 | | |
| 1118 | | const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len); |
| 1119 | | try leb.writeUleb128(binary_bytes.writer(gpa), total_globals); |
| 1101 | const sub_header_offset = try reserveSectionHeader(w); |
| 1102 | defer replaceSectionHeader(aw, sub_header_offset, @intFromEnum(std.wasm.NameSubsection.global)); |
| 1120 | 1103 | |
| 1104 | try w.writeLeb128(f.global_imports.entries.len + wasm.globals.entries.len); |
| 1121 | 1105 | for (f.global_imports.keys(), 0..) |name_index, global_index| { |
| 1122 | 1106 | const name = name_index.slice(wasm); |
| 1123 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); |
| 1124 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); |
| 1125 | | try binary_bytes.appendSlice(gpa, name); |
| 1107 | try w.writeLeb128(global_index); |
| 1108 | try w.writeLeb128(name.len); |
| 1109 | try w.writeAll(name); |
| 1126 | 1110 | } |
| 1127 | 1111 | for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { |
| 1128 | 1112 | const name = resolution.name(wasm).?; |
| 1129 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); |
| 1130 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); |
| 1131 | | try binary_bytes.appendSlice(gpa, name); |
| 1113 | try w.writeLeb128(global_index); |
| 1114 | try w.writeLeb128(name.len); |
| 1115 | try w.writeAll(name); |
| 1132 | 1116 | } |
| 1133 | 1117 | } |
| 1134 | 1118 | |
| 1135 | 1119 | { |
| 1136 | | const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1137 | | defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment)); |
| 1120 | const sub_header_offset = try reserveSectionHeader(w); |
| 1121 | defer replaceSectionHeader(aw, sub_header_offset, @intFromEnum(std.wasm.NameSubsection.data_segment)); |
| 1138 | 1122 | |
| 1139 | | const total_data_segments: u32 = @intCast(data_segment_groups.len); |
| 1140 | | try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments); |
| 1141 | | |
| 1142 | | for (data_segment_groups, 0..) |group, i| { |
| 1123 | try w.writeLeb128(data_segment_groups.len); |
| 1124 | for (data_segment_groups, 0..) |group, group_index| { |
| 1143 | 1125 | const name, _ = splitSegmentName(group.first_segment.name(wasm)); |
| 1144 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i))); |
| 1145 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); |
| 1146 | | try binary_bytes.appendSlice(gpa, name); |
| 1126 | try w.writeLeb128(group_index); |
| 1127 | try w.writeLeb128(name.len); |
| 1128 | try w.writeAll(name); |
| 1147 | 1129 | } |
| 1148 | 1130 | } |
| 1149 | 1131 | } |
| 1150 | 1132 | |
| 1151 | | fn emitFeaturesSection( |
| 1152 | | gpa: Allocator, |
| 1153 | | binary_bytes: *std.ArrayListUnmanaged(u8), |
| 1154 | | target: *const std.Target, |
| 1155 | | ) Allocator.Error!void { |
| 1133 | fn emitFeaturesSection(aw: *Writer.Allocating, target: *const std.Target) !void { |
| 1156 | 1134 | const feature_count = target.cpu.features.count(); |
| 1157 | 1135 | if (feature_count == 0) return; |
| 1158 | 1136 | |
| 1159 | | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1160 | | defer writeCustomSectionHeader(binary_bytes, header_offset); |
| 1161 | | |
| 1162 | | const writer = binary_bytes.writer(gpa); |
| 1163 | | const target_features = "target_features"; |
| 1164 | | try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len))); |
| 1165 | | try writer.writeAll(target_features); |
| 1137 | const w = &aw.writer; |
| 1138 | const header_offset = try reserveSectionHeader(w); |
| 1139 | defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom)); |
| 1166 | 1140 | |
| 1167 | | try leb.writeUleb128(writer, @as(u32, @intCast(feature_count))); |
| 1141 | const section_name = "target_features"; |
| 1142 | try w.writeLeb128(section_name.len); |
| 1143 | try w.writeAll(section_name); |
| 1168 | 1144 | |
| 1145 | try w.writeLeb128(feature_count); |
| 1169 | 1146 | var safety_count = feature_count; |
| 1170 | 1147 | for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| { |
| 1171 | 1148 | if (!target.cpu.has(.wasm, @as(std.Target.wasm.Feature, @enumFromInt(i)))) continue; |
| 1172 | 1149 | safety_count -= 1; |
| 1173 | 1150 | |
| 1174 | | try leb.writeUleb128(writer, @as(u32, '+')); |
| 1151 | try w.writeUleb128('+'); |
| 1175 | 1152 | // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions. |
| 1176 | 1153 | const name = feature.llvm_name.?; |
| 1177 | | try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); |
| 1178 | | try writer.writeAll(name); |
| 1154 | try w.writeLeb128(name.len); |
| 1155 | try w.writeAll(name); |
| 1179 | 1156 | } |
| 1180 | 1157 | assert(safety_count == 0); |
| 1181 | 1158 | } |
| 1182 | 1159 | |
| 1183 | | fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void { |
| 1184 | | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1185 | | defer writeCustomSectionHeader(binary_bytes, header_offset); |
| 1160 | fn emitBuildIdSection(aw: *Writer.Allocating, build_id: []const u8) !void { |
| 1161 | const w = &aw.writer; |
| 1162 | const header_offset = try reserveSectionHeader(w); |
| 1163 | defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom)); |
| 1186 | 1164 | |
| 1187 | | const writer = binary_bytes.writer(gpa); |
| 1188 | | const hdr_build_id = "build_id"; |
| 1189 | | try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len))); |
| 1190 | | try writer.writeAll(hdr_build_id); |
| 1165 | const section_name = "build_id"; |
| 1166 | try w.writeLeb128(section_name.len); |
| 1167 | try w.writeAll(section_name); |
| 1191 | 1168 | |
| 1192 | | try leb.writeUleb128(writer, @as(u32, 1)); |
| 1193 | | try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len))); |
| 1194 | | try writer.writeAll(build_id); |
| 1169 | try w.writeUleb128(1); |
| 1170 | try w.writeLeb128(build_id.len); |
| 1171 | try w.writeAll(build_id); |
| 1195 | 1172 | } |
| 1196 | 1173 | |
| 1197 | | fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void { |
| 1198 | | const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); |
| 1199 | | defer writeCustomSectionHeader(binary_bytes, header_offset); |
| 1200 | | |
| 1201 | | const writer = binary_bytes.writer(gpa); |
| 1202 | | const producers = "producers"; |
| 1203 | | try leb.writeUleb128(writer, @as(u32, @intCast(producers.len))); |
| 1204 | | try writer.writeAll(producers); |
| 1174 | fn emitProducerSection(aw: *Writer.Allocating) !void { |
| 1175 | const w = &aw.writer; |
| 1176 | const header_offset = try reserveSectionHeader(w); |
| 1177 | defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom)); |
| 1205 | 1178 | |
| 1206 | | try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by |
| 1179 | const section_name = "producers"; |
| 1180 | try w.writeLeb128(section_name.len); |
| 1181 | try w.writeAll(section_name); |
| 1207 | 1182 | |
| 1208 | | // language field |
| 1183 | try w.writeUleb128(2); // 2 fields: language + processed-by |
| 1209 | 1184 | { |
| 1210 | | const language = "language"; |
| 1211 | | try leb.writeUleb128(writer, @as(u32, @intCast(language.len))); |
| 1212 | | try writer.writeAll(language); |
| 1185 | const field_name = "language"; |
| 1186 | try w.writeLeb128(field_name.len); |
| 1187 | try w.writeAll(field_name); |
| 1213 | 1188 | |
| 1214 | 1189 | // field_value_count (TODO: Parse object files for producer sections to detect their language) |
| 1215 | | try leb.writeUleb128(writer, @as(u32, 1)); |
| 1190 | try w.writeUleb128(1); |
| 1216 | 1191 | |
| 1217 | 1192 | // versioned name |
| 1218 | 1193 | { |
| 1219 | | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" |
| 1220 | | try writer.writeAll("Zig"); |
| 1194 | const field_value = "Zig"; |
| 1195 | try w.writeLeb128(field_value.len); |
| 1196 | try w.writeAll(field_value); |
| 1221 | 1197 | |
| 1222 | | try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); |
| 1223 | | try writer.writeAll(build_options.version); |
| 1198 | try w.writeLeb128(build_options.version.len); |
| 1199 | try w.writeAll(build_options.version); |
| 1224 | 1200 | } |
| 1225 | 1201 | } |
| 1226 | | |
| 1227 | | // processed-by field |
| 1228 | 1202 | { |
| 1229 | | const processed_by = "processed-by"; |
| 1230 | | try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len))); |
| 1231 | | try writer.writeAll(processed_by); |
| 1203 | const field_name = "processed-by"; |
| 1204 | try w.writeLeb128(field_name.len); |
| 1205 | try w.writeAll(field_name); |
| 1232 | 1206 | |
| 1233 | 1207 | // field_value_count (TODO: Parse object files for producer sections to detect other used tools) |
| 1234 | | try leb.writeUleb128(writer, @as(u32, 1)); |
| 1208 | try w.writeUleb128(1); |
| 1235 | 1209 | |
| 1236 | 1210 | // versioned name |
| 1237 | 1211 | { |
| 1238 | | try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" |
| 1239 | | try writer.writeAll("Zig"); |
| 1212 | const field_value = "Zig"; |
| 1213 | try w.writeLeb128(field_value.len); |
| 1214 | try w.writeAll(field_value); |
| 1240 | 1215 | |
| 1241 | | try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); |
| 1242 | | try writer.writeAll(build_options.version); |
| 1216 | try w.writeLeb128(build_options.version.len); |
| 1217 | try w.writeAll(build_options.version); |
| 1243 | 1218 | } |
| 1244 | 1219 | } |
| 1245 | 1220 | } |
| ... | ... | @@ -1277,170 +1252,130 @@ fn wantSegmentMerge( |
| 1277 | 1252 | } |
| 1278 | 1253 | |
| 1279 | 1254 | /// section id + fixed leb contents size + fixed leb vector length |
| 1280 | | const section_header_reserve_size = 1 + 5 + 5; |
| 1281 | | const section_header_size = 5 + 1; |
| 1255 | const vec_section_header_size = section_header_size + size_header_size; |
| 1282 | 1256 | |
| 1283 | | fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { |
| 1284 | | try bytes.appendNTimes(gpa, 0, section_header_reserve_size); |
| 1285 | | return @intCast(bytes.items.len - section_header_reserve_size); |
| 1257 | fn reserveVecSectionHeader(w: *Writer) Writer.Error!u32 { |
| 1258 | const offset = w.end; |
| 1259 | _ = try w.writableSlice(vec_section_header_size); |
| 1260 | return @intCast(offset); |
| 1286 | 1261 | } |
| 1287 | 1262 | |
| 1288 | 1263 | fn replaceVecSectionHeader( |
| 1289 | | bytes: *std.ArrayListUnmanaged(u8), |
| 1264 | aw: *Writer.Allocating, |
| 1290 | 1265 | offset: u32, |
| 1291 | 1266 | section: std.wasm.Section, |
| 1292 | 1267 | n_items: u32, |
| 1293 | 1268 | ) void { |
| 1294 | | const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items)); |
| 1295 | | var buf: [section_header_reserve_size]u8 = undefined; |
| 1296 | | var fbw = std.io.fixedBufferStream(&buf); |
| 1297 | | const w = fbw.writer(); |
| 1298 | | w.writeByte(@intFromEnum(section)) catch unreachable; |
| 1299 | | leb.writeUleb128(w, size) catch unreachable; |
| 1300 | | leb.writeUleb128(w, n_items) catch unreachable; |
| 1301 | | bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten()); |
| 1269 | const header = aw.getWritten()[offset..][0..vec_section_header_size]; |
| 1270 | header[0] = @intFromEnum(section); |
| 1271 | std.leb.writeUnsignedFixed(5, header[1..6], @intCast(aw.writer.end - offset - section_header_size)); |
| 1272 | std.leb.writeUnsignedFixed(5, header[6..], n_items); |
| 1302 | 1273 | } |
| 1303 | 1274 | |
| 1304 | | fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { |
| 1305 | | try bytes.appendNTimes(gpa, 0, section_header_size); |
| 1306 | | return @intCast(bytes.items.len - section_header_size); |
| 1307 | | } |
| 1275 | const section_header_size = 1 + size_header_size; |
| 1308 | 1276 | |
| 1309 | | fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void { |
| 1310 | | return replaceHeader(bytes, offset, 0); // 0 = 'custom' section |
| 1277 | fn reserveSectionHeader(w: *Writer) Writer.Error!u32 { |
| 1278 | const offset = w.end; |
| 1279 | _ = try w.writableSlice(section_header_size); |
| 1280 | return @intCast(offset); |
| 1311 | 1281 | } |
| 1312 | 1282 | |
| 1313 | | fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void { |
| 1314 | | const size: u32 = @intCast(bytes.items.len - offset - section_header_size); |
| 1315 | | var buf: [section_header_size]u8 = undefined; |
| 1316 | | var fbw = std.io.fixedBufferStream(&buf); |
| 1317 | | const w = fbw.writer(); |
| 1318 | | w.writeByte(tag) catch unreachable; |
| 1319 | | leb.writeUleb128(w, size) catch unreachable; |
| 1320 | | bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten()); |
| 1283 | fn replaceSectionHeader(aw: *Writer.Allocating, offset: u32, section: u8) void { |
| 1284 | const header = aw.getWritten()[offset..][0..section_header_size]; |
| 1285 | header[0] = section; |
| 1286 | std.leb.writeUnsignedFixed(5, header[1..6], @intCast(aw.writer.end - offset - section_header_size)); |
| 1321 | 1287 | } |
| 1322 | 1288 | |
| 1323 | | const max_size_encoding = 5; |
| 1289 | const size_header_size = 5; |
| 1324 | 1290 | |
| 1325 | | fn reserveSize(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { |
| 1326 | | try bytes.appendNTimes(gpa, 0, max_size_encoding); |
| 1327 | | return @intCast(bytes.items.len - max_size_encoding); |
| 1291 | fn reserveSizeHeader(w: *Writer) Writer.Error!u32 { |
| 1292 | const offset = w.end; |
| 1293 | _ = try w.writableSlice(size_header_size); |
| 1294 | return @intCast(offset); |
| 1328 | 1295 | } |
| 1329 | 1296 | |
| 1330 | | fn replaceSize(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void { |
| 1331 | | const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding); |
| 1332 | | var buf: [max_size_encoding]u8 = undefined; |
| 1333 | | var fbw = std.io.fixedBufferStream(&buf); |
| 1334 | | leb.writeUleb128(fbw.writer(), size) catch unreachable; |
| 1335 | | bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten()); |
| 1297 | fn replaceSizeHeader(aw: *Writer.Allocating, offset: u32) void { |
| 1298 | const header = aw.getWritten()[offset..][0..size_header_size]; |
| 1299 | std.leb.writeUnsignedFixed(5, header[0..5], @intCast(aw.writer.end - offset - size_header_size)); |
| 1336 | 1300 | } |
| 1337 | 1301 | |
| 1338 | | fn emitLimits( |
| 1339 | | gpa: Allocator, |
| 1340 | | binary_bytes: *std.ArrayListUnmanaged(u8), |
| 1341 | | limits: std.wasm.Limits, |
| 1342 | | ) Allocator.Error!void { |
| 1343 | | try binary_bytes.append(gpa, @bitCast(limits.flags)); |
| 1344 | | try leb.writeUleb128(binary_bytes.writer(gpa), limits.min); |
| 1345 | | if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max); |
| 1302 | fn emitLimits(w: *Writer, limits: std.wasm.Limits) Writer.Error!void { |
| 1303 | try w.writeByte(@bitCast(limits.flags)); |
| 1304 | try w.writeLeb128(limits.min); |
| 1305 | if (limits.flags.has_max) try w.writeLeb128(limits.max); |
| 1346 | 1306 | } |
| 1347 | 1307 | |
| 1348 | 1308 | fn emitMemoryImport( |
| 1349 | 1309 | wasm: *Wasm, |
| 1350 | | binary_bytes: *std.ArrayListUnmanaged(u8), |
| 1310 | w: *Writer, |
| 1351 | 1311 | name_index: String, |
| 1352 | 1312 | memory_import: *const Wasm.MemoryImport, |
| 1353 | | ) Allocator.Error!void { |
| 1354 | | const gpa = wasm.base.comp.gpa; |
| 1313 | ) Writer.Error!void { |
| 1355 | 1314 | const module_name = memory_import.module_name.slice(wasm); |
| 1356 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len))); |
| 1357 | | try binary_bytes.appendSlice(gpa, module_name); |
| 1315 | try w.writeLeb128(module_name.len); |
| 1316 | try w.writeAll(module_name); |
| 1358 | 1317 | |
| 1359 | 1318 | const name = name_index.slice(wasm); |
| 1360 | | try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); |
| 1361 | | try binary_bytes.appendSlice(gpa, name); |
| 1319 | try w.writeLeb128(name.len); |
| 1320 | try w.writeAll(name); |
| 1362 | 1321 | |
| 1363 | | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); |
| 1364 | | try emitLimits(gpa, binary_bytes, memory_import.limits()); |
| 1322 | try w.writeByte(@intFromEnum(std.wasm.ExternalKind.memory)); |
| 1323 | try emitLimits(w, memory_import.limits()); |
| 1365 | 1324 | } |
| 1366 | 1325 | |
| 1367 | | pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { |
| 1326 | pub fn emitInit(w: *Writer, init_expr: std.wasm.InitExpression) Writer.Error!void { |
| 1368 | 1327 | switch (init_expr) { |
| 1369 | | .i32_const => |val| { |
| 1370 | | try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1371 | | try leb.writeIleb128(writer, val); |
| 1372 | | }, |
| 1373 | | .i64_const => |val| { |
| 1374 | | try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const)); |
| 1375 | | try leb.writeIleb128(writer, val); |
| 1376 | | }, |
| 1377 | | .f32_const => |val| { |
| 1378 | | try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const)); |
| 1379 | | try writer.writeInt(u32, @bitCast(val), .little); |
| 1380 | | }, |
| 1381 | | .f64_const => |val| { |
| 1382 | | try writer.writeByte(@intFromEnum(std.wasm.Opcode.f64_const)); |
| 1383 | | try writer.writeInt(u64, @bitCast(val), .little); |
| 1384 | | }, |
| 1385 | | .global_get => |val| { |
| 1386 | | try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get)); |
| 1387 | | try leb.writeUleb128(writer, val); |
| 1328 | inline else => |val, tag| { |
| 1329 | try w.writeByte(@intFromEnum(@field(std.wasm.Opcode, @tagName(tag)))); |
| 1330 | switch (@typeInfo(@TypeOf(val))) { |
| 1331 | .int => try w.writeLeb128(val), |
| 1332 | .float => |float| try w.writeInt( |
| 1333 | @Type(.{ .int = .{ .signedness = .unsigned, .bits = float.bits } }), |
| 1334 | @bitCast(val), |
| 1335 | .little, |
| 1336 | ), |
| 1337 | else => comptime unreachable, |
| 1338 | } |
| 1388 | 1339 | }, |
| 1389 | 1340 | } |
| 1390 | | try writer.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1341 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1391 | 1342 | } |
| 1392 | 1343 | |
| 1393 | | pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void { |
| 1394 | | const gpa = wasm.base.comp.gpa; |
| 1344 | pub fn emitExpr(wasm: *const Wasm, w: *Writer, expr: Wasm.Expr) Writer.Error!void { |
| 1395 | 1345 | const slice = expr.slice(wasm); |
| 1396 | | try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode |
| 1346 | try w.writeAll(slice[0 .. slice.len + 1]); // +1 to include end opcode |
| 1397 | 1347 | } |
| 1398 | 1348 | |
| 1399 | | fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { |
| 1400 | | const gpa = wasm.base.comp.gpa; |
| 1401 | | const writer = binary_bytes.writer(gpa); |
| 1402 | | try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info)); |
| 1403 | | const segment_offset = binary_bytes.items.len; |
| 1349 | fn emitSegmentInfo(wasm: *Wasm, aw: *Writer) Writer.Error!void { |
| 1350 | const w = &aw.writer; |
| 1351 | const header_offset = try reserveSectionHeader(w); |
| 1352 | defer replaceSectionHeader(aw, header_offset, @intFromEnum(Wasm.SubsectionType.segment_info)); |
| 1404 | 1353 | |
| 1405 | | try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count()))); |
| 1354 | try w.writeLeb128(wasm.segment_info.count()); |
| 1406 | 1355 | for (wasm.segment_info.values()) |segment_info| { |
| 1407 | 1356 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| 1408 | 1357 | segment_info.name, |
| 1409 | 1358 | segment_info.alignment, |
| 1410 | 1359 | segment_info.flags, |
| 1411 | 1360 | }); |
| 1412 | | try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len))); |
| 1413 | | try writer.writeAll(segment_info.name); |
| 1414 | | try leb.writeUleb128(writer, segment_info.alignment.toLog2Units()); |
| 1415 | | try leb.writeUleb128(writer, segment_info.flags); |
| 1361 | try w.writeLeb128(segment_info.name.len); |
| 1362 | try w.writeAll(segment_info.name); |
| 1363 | try w.writeLeb128(segment_info.alignment.toLog2Units()); |
| 1364 | try w.writeLeb128(segment_info.flags); |
| 1416 | 1365 | } |
| 1417 | | |
| 1418 | | var buf: [5]u8 = undefined; |
| 1419 | | leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset))); |
| 1420 | | try binary_bytes.insertSlice(segment_offset, &buf); |
| 1421 | | } |
| 1422 | | |
| 1423 | | fn uleb128size(x: u32) u32 { |
| 1424 | | var value = x; |
| 1425 | | var size: u32 = 0; |
| 1426 | | while (value != 0) : (size += 1) value >>= 7; |
| 1427 | | return size; |
| 1428 | 1366 | } |
| 1429 | 1367 | |
| 1430 | 1368 | fn emitTagNameTable( |
| 1431 | | gpa: Allocator, |
| 1432 | | code: *std.ArrayListUnmanaged(u8), |
| 1369 | w: *Writer, |
| 1433 | 1370 | tag_name_offs: []const u32, |
| 1434 | 1371 | tag_name_bytes: []const u8, |
| 1435 | 1372 | base: u32, |
| 1436 | 1373 | comptime Int: type, |
| 1437 | | ) error{OutOfMemory}!void { |
| 1438 | | const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8); |
| 1439 | | try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * tag_name_offs.len); |
| 1374 | ) Writer.Error!void { |
| 1440 | 1375 | for (tag_name_offs) |off| { |
| 1441 | 1376 | const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?); |
| 1442 | | mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little); |
| 1443 | | mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little); |
| 1377 | try w.writeInt(Int, base + off, .little); |
| 1378 | try w.writeInt(Int, name_len, .little); |
| 1444 | 1379 | } |
| 1445 | 1380 | } |
| 1446 | 1381 | |
| ... | ... | @@ -1525,11 +1460,11 @@ fn reloc_u64_table_index(code: []u8, i: IndirectFunctionTableIndex) void { |
| 1525 | 1460 | } |
| 1526 | 1461 | |
| 1527 | 1462 | fn reloc_sleb_table_index(code: []u8, i: IndirectFunctionTableIndex) void { |
| 1528 | | leb.writeSignedFixed(5, code[0..5], i.toAbi()); |
| 1463 | std.leb.writeSignedFixed(5, code[0..5], i.toAbi()); |
| 1529 | 1464 | } |
| 1530 | 1465 | |
| 1531 | 1466 | fn reloc_sleb64_table_index(code: []u8, i: IndirectFunctionTableIndex) void { |
| 1532 | | leb.writeSignedFixed(11, code[0..11], i.toAbi()); |
| 1467 | std.leb.writeSignedFixed(11, code[0..11], i.toAbi()); |
| 1533 | 1468 | } |
| 1534 | 1469 | |
| 1535 | 1470 | fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void { |
| ... | ... | @@ -1537,7 +1472,7 @@ fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void { |
| 1537 | 1472 | } |
| 1538 | 1473 | |
| 1539 | 1474 | fn reloc_leb_function(code: []u8, function: Wasm.OutputFunctionIndex) void { |
| 1540 | | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function)); |
| 1475 | std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function)); |
| 1541 | 1476 | } |
| 1542 | 1477 | |
| 1543 | 1478 | fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void { |
| ... | ... | @@ -1545,7 +1480,7 @@ fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void { |
| 1545 | 1480 | } |
| 1546 | 1481 | |
| 1547 | 1482 | fn reloc_leb_global(code: []u8, global: Wasm.GlobalIndex) void { |
| 1548 | | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global)); |
| 1483 | std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global)); |
| 1549 | 1484 | } |
| 1550 | 1485 | |
| 1551 | 1486 | const RelocAddr = struct { |
| ... | ... | @@ -1581,35 +1516,31 @@ fn reloc_u64_addr(code: []u8, ra: RelocAddr) void { |
| 1581 | 1516 | } |
| 1582 | 1517 | |
| 1583 | 1518 | fn reloc_leb_addr(code: []u8, ra: RelocAddr) void { |
| 1584 | | leb.writeUnsignedFixed(5, code[0..5], ra.addr); |
| 1519 | std.leb.writeUnsignedFixed(5, code[0..5], ra.addr); |
| 1585 | 1520 | } |
| 1586 | 1521 | |
| 1587 | 1522 | fn reloc_leb64_addr(code: []u8, ra: RelocAddr) void { |
| 1588 | | leb.writeUnsignedFixed(11, code[0..11], ra.addr); |
| 1523 | std.leb.writeUnsignedFixed(11, code[0..11], ra.addr); |
| 1589 | 1524 | } |
| 1590 | 1525 | |
| 1591 | 1526 | fn reloc_sleb_addr(code: []u8, ra: RelocAddr) void { |
| 1592 | | leb.writeSignedFixed(5, code[0..5], ra.addr); |
| 1527 | std.leb.writeSignedFixed(5, code[0..5], ra.addr); |
| 1593 | 1528 | } |
| 1594 | 1529 | |
| 1595 | 1530 | fn reloc_sleb64_addr(code: []u8, ra: RelocAddr) void { |
| 1596 | | leb.writeSignedFixed(11, code[0..11], ra.addr); |
| 1531 | std.leb.writeSignedFixed(11, code[0..11], ra.addr); |
| 1597 | 1532 | } |
| 1598 | 1533 | |
| 1599 | 1534 | fn reloc_leb_table(code: []u8, table: Wasm.TableIndex) void { |
| 1600 | | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table)); |
| 1535 | std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table)); |
| 1601 | 1536 | } |
| 1602 | 1537 | |
| 1603 | 1538 | fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void { |
| 1604 | | leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index)); |
| 1539 | std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index)); |
| 1605 | 1540 | } |
| 1606 | 1541 | |
| 1607 | | fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { |
| 1608 | | const gpa = wasm.base.comp.gpa; |
| 1609 | | |
| 1610 | | try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1); |
| 1611 | | appendReservedUleb32(binary_bytes, 0); // no locals |
| 1612 | | |
| 1542 | fn emitCallCtorsFunction(wasm: *const Wasm, w: *Writer) Writer.Error!void { |
| 1543 | try w.writeUleb128(0); // no locals |
| 1613 | 1544 | for (wasm.object_init_funcs.items) |init_func| { |
| 1614 | 1545 | const func = init_func.function_index.ptr(wasm); |
| 1615 | 1546 | if (!func.object_index.ptr(wasm).is_included) continue; |
| ... | ... | @@ -1617,25 +1548,18 @@ fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanage |
| 1617 | 1548 | const n_returns = ty.returns.slice(wasm).len; |
| 1618 | 1549 | |
| 1619 | 1550 | // Call function by its function index |
| 1620 | | try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + n_returns + 1); |
| 1621 | 1551 | const call_index: Wasm.OutputFunctionIndex = .fromObjectFunction(wasm, init_func.function_index); |
| 1622 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); |
| 1623 | | appendReservedUleb32(binary_bytes, @intFromEnum(call_index)); |
| 1552 | try w.writeByte(@intFromEnum(std.wasm.Opcode.call)); |
| 1553 | try w.writeLeb128(@intFromEnum(call_index)); |
| 1624 | 1554 | |
| 1625 | 1555 | // drop all returned values from the stack as __wasm_call_ctors has no return value |
| 1626 | | binary_bytes.appendNTimesAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop), n_returns); |
| 1556 | try w.splatByteAll(@intFromEnum(std.wasm.Opcode.drop), n_returns); |
| 1627 | 1557 | } |
| 1628 | | |
| 1629 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end function body |
| 1558 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); // end function body |
| 1630 | 1559 | } |
| 1631 | 1560 | |
| 1632 | | fn emitInitMemoryFunction( |
| 1633 | | wasm: *const Wasm, |
| 1634 | | binary_bytes: *std.ArrayListUnmanaged(u8), |
| 1635 | | virtual_addrs: *const VirtualAddrs, |
| 1636 | | ) Allocator.Error!void { |
| 1561 | fn emitInitMemoryFunction(wasm: *const Wasm, w: *Writer, virtual_addrs: *const VirtualAddrs) Writer.Error!void { |
| 1637 | 1562 | const comp = wasm.base.comp; |
| 1638 | | const gpa = comp.gpa; |
| 1639 | 1563 | const shared_memory = comp.config.shared_memory; |
| 1640 | 1564 | |
| 1641 | 1565 | // Passive segments are used to avoid memory being reinitialized on each |
| ... | ... | @@ -1645,39 +1569,40 @@ fn emitInitMemoryFunction( |
| 1645 | 1569 | // function. |
| 1646 | 1570 | assert(wasm.any_passive_inits); |
| 1647 | 1571 | |
| 1648 | | try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1); |
| 1649 | | appendReservedUleb32(binary_bytes, 0); // no locals |
| 1572 | try w.writeUleb128(0); // no locals |
| 1650 | 1573 | |
| 1651 | 1574 | if (virtual_addrs.init_memory_flag) |flag_address| { |
| 1652 | 1575 | assert(shared_memory); |
| 1653 | | try binary_bytes.ensureUnusedCapacity(gpa, 2 * 3 + 6 * 3 + 1 + 6 * 3 + 1 + 5 * 4 + 1 + 1); |
| 1654 | 1576 | // destination blocks |
| 1655 | 1577 | // based on values we jump to corresponding label |
| 1656 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $drop |
| 1657 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1658 | | |
| 1659 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $wait |
| 1660 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1661 | | |
| 1662 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $init |
| 1663 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1578 | try w.writeAll(&.{ |
| 1579 | @intFromEnum(std.wasm.Opcode.block), // $drop |
| 1580 | @intFromEnum(std.wasm.BlockType.empty), |
| 1581 | @intFromEnum(std.wasm.Opcode.block), // $wait |
| 1582 | @intFromEnum(std.wasm.BlockType.empty), |
| 1583 | @intFromEnum(std.wasm.Opcode.block), // $init |
| 1584 | @intFromEnum(std.wasm.BlockType.empty), |
| 1585 | }); |
| 1664 | 1586 | |
| 1665 | 1587 | // atomically check |
| 1666 | | appendReservedI32Const(binary_bytes, flag_address); |
| 1667 | | appendReservedI32Const(binary_bytes, 0); |
| 1668 | | appendReservedI32Const(binary_bytes, 1); |
| 1669 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1670 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_rmw_cmpxchg)); |
| 1671 | | appendReservedUleb32(binary_bytes, 2); // alignment |
| 1672 | | appendReservedUleb32(binary_bytes, 0); // offset |
| 1588 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1589 | try w.writeLeb128(@as(i32, @bitCast(flag_address))); |
| 1590 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1591 | try w.writeSleb128(0); |
| 1592 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1593 | try w.writeSleb128(1); |
| 1594 | try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1595 | try w.writeLeb128(@intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_rmw_cmpxchg)); |
| 1596 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1597 | try w.writeUleb128(0); // offset |
| 1673 | 1598 | |
| 1674 | 1599 | // based on the value from the atomic check, jump to the label. |
| 1675 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table)); |
| 1676 | | appendReservedUleb32(binary_bytes, 2); // length of the table (we have 3 blocks but because of the mandatory default the length is 2). |
| 1677 | | appendReservedUleb32(binary_bytes, 0); // $init |
| 1678 | | appendReservedUleb32(binary_bytes, 1); // $wait |
| 1679 | | appendReservedUleb32(binary_bytes, 2); // $drop |
| 1680 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1600 | try w.writeByte(@intFromEnum(std.wasm.Opcode.br_table)); |
| 1601 | try w.writeUleb128(3 - 1); // length of the table (we have 3 blocks but because of the mandatory default the length is 2). |
| 1602 | try w.writeUleb128(0); // $init |
| 1603 | try w.writeUleb128(1); // $wait |
| 1604 | try w.writeUleb128(2); // $drop |
| 1605 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1681 | 1606 | } |
| 1682 | 1607 | |
| 1683 | 1608 | const segment_groups = wasm.flush_buffer.data_segment_groups.items; |
| ... | ... | @@ -1690,74 +1615,82 @@ fn emitInitMemoryFunction( |
| 1690 | 1615 | const start_addr: u32 = @intCast(segment.alignment(wasm).forward(prev_end)); |
| 1691 | 1616 | const segment_size: u32 = group.end_addr - start_addr; |
| 1692 | 1617 | |
| 1693 | | try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 5 + 6 + 6 + 1 + 6 * 2 + 1 + 1); |
| 1694 | | |
| 1695 | 1618 | // For passive BSS segments we can simply issue a memory.fill(0). For |
| 1696 | 1619 | // non-BSS segments we do a memory.init. Both instructions take as |
| 1697 | 1620 | // their first argument the destination address. |
| 1698 | | appendReservedI32Const(binary_bytes, start_addr); |
| 1621 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1622 | try w.writeLeb128(@as(i32, @bitCast(start_addr))); |
| 1699 | 1623 | |
| 1700 | 1624 | if (shared_memory and segment.isTls(wasm)) { |
| 1701 | 1625 | // When we initialize the TLS segment we also set the `__tls_base` |
| 1702 | 1626 | // global. This allows the runtime to use this static copy of the |
| 1703 | 1627 | // TLS data for the first/main thread. |
| 1704 | | appendReservedI32Const(binary_bytes, start_addr); |
| 1705 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); |
| 1706 | | appendReservedUleb32(binary_bytes, virtual_addrs.tls_base.?); |
| 1628 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1629 | try w.writeLeb128(@as(i32, @bitCast(start_addr))); |
| 1630 | try w.writeByte(@intFromEnum(std.wasm.Opcode.global_set)); |
| 1631 | try w.writeLeb128(virtual_addrs.tls_base.?); |
| 1707 | 1632 | } |
| 1708 | 1633 | |
| 1709 | | appendReservedI32Const(binary_bytes, 0); |
| 1710 | | appendReservedI32Const(binary_bytes, segment_size); |
| 1711 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 1634 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1635 | try w.writeSleb128(0); |
| 1636 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1637 | try w.writeLeb128(@as(i32, @bitCast(segment_size))); |
| 1638 | try w.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 1712 | 1639 | if (segment.isBss(wasm)) { |
| 1713 | 1640 | // fill bss segment with zeroes |
| 1714 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_fill)); |
| 1641 | try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.memory_fill)); |
| 1715 | 1642 | } else { |
| 1716 | 1643 | // initialize the segment |
| 1717 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init)); |
| 1718 | | appendReservedUleb32(binary_bytes, @intCast(segment_index)); |
| 1644 | try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.memory_init)); |
| 1645 | try w.writeLeb128(segment_index); |
| 1719 | 1646 | } |
| 1720 | | binary_bytes.appendAssumeCapacity(0); // memory index immediate |
| 1647 | try w.writeByte(0); // memory index immediate |
| 1721 | 1648 | } |
| 1722 | 1649 | |
| 1723 | 1650 | if (virtual_addrs.init_memory_flag) |flag_address| { |
| 1724 | 1651 | assert(shared_memory); |
| 1725 | | try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 3 * 5 + 6 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 5 + 1 + 6 * 2 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 1); |
| 1652 | |
| 1726 | 1653 | // we set the init memory flag to value '2' |
| 1727 | | appendReservedI32Const(binary_bytes, flag_address); |
| 1728 | | appendReservedI32Const(binary_bytes, 2); |
| 1729 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1730 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_store)); |
| 1731 | | appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment |
| 1732 | | appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset |
| 1654 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1655 | try w.writeLeb128(@as(i32, @bitCast(flag_address))); |
| 1656 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1657 | try w.writeSleb128(2); |
| 1658 | try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1659 | try w.writeLeb128(@intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_store)); |
| 1660 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1661 | try w.writeUleb128(0); // offset |
| 1733 | 1662 | |
| 1734 | 1663 | // notify any waiters for segment initialization completion |
| 1735 | | appendReservedI32Const(binary_bytes, flag_address); |
| 1736 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1737 | | leb.writeIleb128(binary_bytes.fixedWriter(), @as(i32, -1)) catch unreachable; // number of waiters |
| 1738 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1739 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify)); |
| 1740 | | appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment |
| 1741 | | appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset |
| 1742 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop)); |
| 1664 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1665 | try w.writeLeb128(@as(i32, @bitCast(flag_address))); |
| 1666 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1667 | try w.writeSleb128(-1); // number of waiters |
| 1668 | |
| 1669 | try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1670 | try w.writeLeb128(@intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify)); |
| 1671 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1672 | try w.writeUleb128(0); // offset |
| 1673 | try w.writeByte(@intFromEnum(std.wasm.Opcode.drop)); |
| 1743 | 1674 | |
| 1744 | 1675 | // branch and drop segments |
| 1745 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br)); |
| 1746 | | appendReservedUleb32(binary_bytes, @as(u32, 1)); |
| 1676 | try w.writeByte(@intFromEnum(std.wasm.Opcode.br)); |
| 1677 | try w.writeUleb128(1); |
| 1747 | 1678 | |
| 1748 | 1679 | // wait for thread to initialize memory segments |
| 1749 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $wait |
| 1750 | | appendReservedI32Const(binary_bytes, flag_address); |
| 1751 | | appendReservedI32Const(binary_bytes, 1); // expected flag value |
| 1752 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); |
| 1753 | | leb.writeIleb128(binary_bytes.fixedWriter(), @as(i64, -1)) catch unreachable; // timeout |
| 1754 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1755 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32)); |
| 1756 | | appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment |
| 1757 | | appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset |
| 1758 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop)); |
| 1759 | | |
| 1760 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $drop |
| 1680 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); // end $wait |
| 1681 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1682 | try w.writeLeb128(@as(i32, @bitCast(flag_address))); |
| 1683 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1684 | try w.writeSleb128(1); // expected flag value |
| 1685 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_const)); |
| 1686 | try w.writeSleb128(-1); // timeout |
| 1687 | try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix)); |
| 1688 | try w.writeByte(@intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32)); |
| 1689 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1690 | try w.writeUleb128(0); // offset |
| 1691 | try w.writeByte(@intFromEnum(std.wasm.Opcode.drop)); |
| 1692 | |
| 1693 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); // end $drop |
| 1761 | 1694 | } |
| 1762 | 1695 | |
| 1763 | 1696 | for (segment_groups, 0..) |group, segment_index| { |
| ... | ... | @@ -1768,26 +1701,20 @@ fn emitInitMemoryFunction( |
| 1768 | 1701 | // during the initialization of each thread (__wasm_init_tls). |
| 1769 | 1702 | if (shared_memory and segment.isTls(wasm)) continue; |
| 1770 | 1703 | |
| 1771 | | try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + 5 + 1); |
| 1772 | | |
| 1773 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 1774 | | appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.data_drop)); |
| 1775 | | appendReservedUleb32(binary_bytes, @intCast(segment_index)); |
| 1704 | try w.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 1705 | try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.data_drop)); |
| 1706 | try w.writeLeb128(segment_index); |
| 1776 | 1707 | } |
| 1777 | 1708 | |
| 1778 | 1709 | // End of the function body |
| 1779 | | binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1710 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1780 | 1711 | } |
| 1781 | 1712 | |
| 1782 | | fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { |
| 1713 | fn emitInitTlsFunction(wasm: *const Wasm, w: *Writer) Writer.Error!void { |
| 1783 | 1714 | const comp = wasm.base.comp; |
| 1784 | | const gpa = comp.gpa; |
| 1785 | | |
| 1786 | 1715 | assert(comp.config.shared_memory); |
| 1787 | 1716 | |
| 1788 | | try bytes.ensureUnusedCapacity(gpa, 5 * 10 + 8); |
| 1789 | | |
| 1790 | | appendReservedUleb32(bytes, 0); // no locals |
| 1717 | try w.writeUleb128(0); // no locals |
| 1791 | 1718 | |
| 1792 | 1719 | // If there's a TLS segment, initialize it during runtime using the bulk-memory feature |
| 1793 | 1720 | // TLS segment is always the first one due to how we sort the data segments. |
| ... | ... | @@ -1796,36 +1723,35 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al |
| 1796 | 1723 | const start_addr = wasm.flush_buffer.data_segments.values()[0]; |
| 1797 | 1724 | const end_addr = wasm.flush_buffer.data_segment_groups.items[0].end_addr; |
| 1798 | 1725 | const group_size = end_addr - start_addr; |
| 1799 | | const data_segment_index = 0; |
| 1726 | const data_segment_index: u32 = 0; |
| 1800 | 1727 | |
| 1801 | 1728 | const param_local: u32 = 0; |
| 1802 | 1729 | |
| 1803 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1804 | | appendReservedUleb32(bytes, param_local); |
| 1730 | try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1731 | try w.writeLeb128(param_local); |
| 1805 | 1732 | |
| 1806 | 1733 | const tls_base_global_index: Wasm.GlobalIndex = @enumFromInt(wasm.globals.getIndex(.__tls_base).?); |
| 1807 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); |
| 1808 | | appendReservedUleb32(bytes, @intFromEnum(tls_base_global_index)); |
| 1734 | try w.writeByte(@intFromEnum(std.wasm.Opcode.global_set)); |
| 1735 | try w.writeLeb128(@intFromEnum(tls_base_global_index)); |
| 1809 | 1736 | |
| 1810 | 1737 | // load stack values for the bulk-memory operation |
| 1811 | 1738 | { |
| 1812 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1813 | | appendReservedUleb32(bytes, param_local); |
| 1739 | try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1740 | try w.writeLeb128(param_local); |
| 1814 | 1741 | |
| 1815 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1816 | | appendReservedUleb32(bytes, 0); //segment offset |
| 1742 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1743 | try w.writeSleb128(0); // segment offset |
| 1817 | 1744 | |
| 1818 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1819 | | appendReservedUleb32(bytes, group_size); //segment offset |
| 1745 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1746 | try w.writeLeb128(@as(i32, @bitCast(group_size))); // segment offset |
| 1820 | 1747 | } |
| 1821 | 1748 | |
| 1822 | 1749 | // perform the bulk-memory operation to initialize the data segment |
| 1823 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 1824 | | appendReservedUleb32(bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init)); |
| 1750 | try w.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix)); |
| 1751 | try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.memory_init)); |
| 1825 | 1752 | // segment immediate |
| 1826 | | appendReservedUleb32(bytes, data_segment_index); |
| 1827 | | // memory index immediate (always 0) |
| 1828 | | appendReservedUleb32(bytes, 0); |
| 1753 | try w.writeLeb128(data_segment_index); |
| 1754 | try w.writeByte(0); // memory index immediate |
| 1829 | 1755 | } |
| 1830 | 1756 | |
| 1831 | 1757 | // If we have to perform any TLS relocations, call the corresponding function |
| ... | ... | @@ -1833,56 +1759,59 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al |
| 1833 | 1759 | // generated by the linker. |
| 1834 | 1760 | if (wasm.functions.getIndex(.__wasm_apply_global_tls_relocs)) |function_index| { |
| 1835 | 1761 | const output_function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex(wasm, @enumFromInt(function_index)); |
| 1836 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call)); |
| 1837 | | appendReservedUleb32(bytes, @intFromEnum(output_function_index)); |
| 1762 | try w.writeByte(@intFromEnum(std.wasm.Opcode.call)); |
| 1763 | try w.writeLeb128(@intFromEnum(output_function_index)); |
| 1838 | 1764 | } |
| 1839 | 1765 | |
| 1840 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1766 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1841 | 1767 | } |
| 1842 | 1768 | |
| 1843 | | fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void { |
| 1844 | | const header_offset = try reserveVecSectionHeader(gpa, bytes); |
| 1845 | | replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); |
| 1769 | fn emitStartSection(aw: *Writer.Allocating, i: Wasm.OutputFunctionIndex) !void { |
| 1770 | const header_offset = try reserveVecSectionHeader(&aw.writer); |
| 1771 | defer replaceVecSectionHeader(aw, header_offset, .start, @intFromEnum(i)); |
| 1846 | 1772 | } |
| 1847 | 1773 | |
| 1848 | 1774 | fn emitTagNameFunction( |
| 1849 | 1775 | wasm: *Wasm, |
| 1850 | | code: *std.ArrayListUnmanaged(u8), |
| 1776 | w: *Writer, |
| 1851 | 1777 | table_base_addr: u32, |
| 1852 | 1778 | table_index: u32, |
| 1853 | 1779 | enum_type_ip: InternPool.Index, |
| 1854 | 1780 | ) !void { |
| 1855 | 1781 | const comp = wasm.base.comp; |
| 1856 | | const gpa = comp.gpa; |
| 1857 | 1782 | const diags = &comp.link_diags; |
| 1858 | 1783 | const zcu = comp.zcu.?; |
| 1859 | 1784 | const ip = &zcu.intern_pool; |
| 1860 | 1785 | const enum_type = ip.loadEnumType(enum_type_ip); |
| 1861 | 1786 | const tag_values = enum_type.values.get(ip); |
| 1862 | 1787 | |
| 1863 | | try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6); |
| 1864 | | appendReservedUleb32(code, 0); // no locals |
| 1788 | try w.writeUleb128(0); // no locals |
| 1865 | 1789 | |
| 1866 | | const slice_abi_size = 8; |
| 1867 | | const encoded_alignment = @ctz(@as(u32, 4)); |
| 1790 | const slice_abi_size: u32 = 8; |
| 1868 | 1791 | if (tag_values.len == 0) { |
| 1869 | 1792 | // Then it's auto-numbered and therefore a direct table lookup. |
| 1870 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1871 | | appendReservedUleb32(code, 0); |
| 1793 | try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1794 | try w.writeUleb128(0); |
| 1872 | 1795 | |
| 1873 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1874 | | appendReservedUleb32(code, 1); |
| 1796 | try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1797 | try w.writeUleb128(1); |
| 1875 | 1798 | |
| 1876 | | appendReservedI32Const(code, slice_abi_size); |
| 1877 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_mul)); |
| 1799 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1800 | if (std.math.isPowerOfTwo(slice_abi_size)) { |
| 1801 | try w.writeLeb128(@as(i32, @bitCast(@as(u32, std.math.log2_int(u32, slice_abi_size))))); |
| 1802 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_shl)); |
| 1803 | } else { |
| 1804 | try w.writeLeb128(@as(i32, @bitCast(slice_abi_size))); |
| 1805 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_mul)); |
| 1806 | } |
| 1878 | 1807 | |
| 1879 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load)); |
| 1880 | | appendReservedUleb32(code, encoded_alignment); |
| 1881 | | appendReservedUleb32(code, table_base_addr + table_index * 8); |
| 1808 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_load)); |
| 1809 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1810 | try w.writeLeb128(table_base_addr + slice_abi_size * table_index); |
| 1882 | 1811 | |
| 1883 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); |
| 1884 | | appendReservedUleb32(code, encoded_alignment); |
| 1885 | | appendReservedUleb32(code, 0); |
| 1812 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_store)); |
| 1813 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1814 | try w.writeUleb128(0); |
| 1886 | 1815 | } else { |
| 1887 | 1816 | const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu); |
| 1888 | 1817 | const outer_block_type: std.wasm.BlockType = switch (int_info.bits) { |
| ... | ... | @@ -1891,94 +1820,80 @@ fn emitTagNameFunction( |
| 1891 | 1820 | else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}), |
| 1892 | 1821 | }; |
| 1893 | 1822 | |
| 1894 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1895 | | appendReservedUleb32(code, 0); |
| 1823 | try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1824 | try w.writeUleb128(0); |
| 1896 | 1825 | |
| 1897 | 1826 | // Outer block that computes table offset. |
| 1898 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); |
| 1899 | | code.appendAssumeCapacity(@intFromEnum(outer_block_type)); |
| 1827 | try w.writeByte(@intFromEnum(std.wasm.Opcode.block)); |
| 1828 | try w.writeByte(@intFromEnum(outer_block_type)); |
| 1900 | 1829 | |
| 1901 | 1830 | for (tag_values, 0..) |tag_value, tag_index| { |
| 1902 | 1831 | // block for this if case |
| 1903 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); |
| 1904 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty)); |
| 1832 | try w.writeByte(@intFromEnum(std.wasm.Opcode.block)); |
| 1833 | try w.writeByte(@intFromEnum(std.wasm.BlockType.empty)); |
| 1905 | 1834 | |
| 1906 | 1835 | // Tag value whose name should be returned. |
| 1907 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1908 | | appendReservedUleb32(code, 1); |
| 1836 | try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get)); |
| 1837 | try w.writeUleb128(1); |
| 1909 | 1838 | |
| 1910 | 1839 | const val: Zcu.Value = .fromInterned(tag_value); |
| 1911 | 1840 | switch (outer_block_type) { |
| 1912 | 1841 | .i32 => { |
| 1913 | | const x: u32 = switch (int_info.signedness) { |
| 1914 | | .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))), |
| 1915 | | .unsigned => @intCast(val.toUnsignedInt(zcu)), |
| 1916 | | }; |
| 1917 | | appendReservedI32Const(code, x); |
| 1918 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne)); |
| 1842 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1843 | try w.writeLeb128(@as(i32, switch (int_info.signedness) { |
| 1844 | .signed => @intCast(val.toSignedInt(zcu)), |
| 1845 | .unsigned => @bitCast(@as(u32, @intCast(val.toUnsignedInt(zcu)))), |
| 1846 | })); |
| 1847 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_ne)); |
| 1919 | 1848 | }, |
| 1920 | 1849 | .i64 => { |
| 1921 | | const x: u64 = switch (int_info.signedness) { |
| 1922 | | .signed => @bitCast(val.toSignedInt(zcu)), |
| 1923 | | .unsigned => val.toUnsignedInt(zcu), |
| 1924 | | }; |
| 1925 | | appendReservedI64Const(code, x); |
| 1926 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne)); |
| 1850 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_const)); |
| 1851 | try w.writeLeb128(@as(i64, switch (int_info.signedness) { |
| 1852 | .signed => val.toSignedInt(zcu), |
| 1853 | .unsigned => @bitCast(val.toUnsignedInt(zcu)), |
| 1854 | })); |
| 1855 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_ne)); |
| 1927 | 1856 | }, |
| 1928 | 1857 | else => unreachable, |
| 1929 | 1858 | } |
| 1930 | 1859 | |
| 1931 | 1860 | // if they're not equal, break out of current branch |
| 1932 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if)); |
| 1933 | | appendReservedUleb32(code, 0); |
| 1861 | try w.writeByte(@intFromEnum(std.wasm.Opcode.br_if)); |
| 1862 | try w.writeUleb128(0); |
| 1934 | 1863 | |
| 1935 | 1864 | // Put the table offset of the result on the stack. |
| 1936 | | appendReservedI32Const(code, @intCast(tag_index * slice_abi_size)); |
| 1865 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1866 | try w.writeLeb128(@as(i32, @bitCast(@as(u32, @intCast(slice_abi_size * tag_index))))); |
| 1937 | 1867 | |
| 1938 | 1868 | // break outside blocks |
| 1939 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br)); |
| 1940 | | appendReservedUleb32(code, 1); |
| 1869 | try w.writeByte(@intFromEnum(std.wasm.Opcode.br)); |
| 1870 | try w.writeUleb128(1); |
| 1941 | 1871 | |
| 1942 | 1872 | // end the block for this case |
| 1943 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1873 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1944 | 1874 | } |
| 1945 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable")); |
| 1946 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1875 | try w.writeByte(@intFromEnum(std.wasm.Opcode.@"unreachable")); |
| 1876 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1947 | 1877 | |
| 1948 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load)); |
| 1949 | | appendReservedUleb32(code, encoded_alignment); |
| 1950 | | appendReservedUleb32(code, table_base_addr + table_index * 8); |
| 1878 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_load)); |
| 1879 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1880 | try w.writeLeb128(table_base_addr + slice_abi_size * table_index); |
| 1951 | 1881 | |
| 1952 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store)); |
| 1953 | | appendReservedUleb32(code, encoded_alignment); |
| 1954 | | appendReservedUleb32(code, 0); |
| 1882 | try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_store)); |
| 1883 | try w.writeLeb128(comptime Alignment.@"4".toLog2Units()); |
| 1884 | try w.writeUleb128(0); |
| 1955 | 1885 | } |
| 1956 | 1886 | |
| 1957 | 1887 | // End of the function body |
| 1958 | | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1959 | | } |
| 1960 | | |
| 1961 | | /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. |
| 1962 | | fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { |
| 1963 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1964 | | leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable; |
| 1965 | | } |
| 1966 | | |
| 1967 | | /// Writes an unsigned 64-bit integer as a LEB128-encoded 'i64.const' value. |
| 1968 | | fn appendReservedI64Const(bytes: *std.ArrayListUnmanaged(u8), val: u64) void { |
| 1969 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); |
| 1970 | | leb.writeIleb128(bytes.fixedWriter(), @as(i64, @bitCast(val))) catch unreachable; |
| 1971 | | } |
| 1972 | | |
| 1973 | | fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { |
| 1974 | | leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable; |
| 1888 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1975 | 1889 | } |
| 1976 | 1890 | |
| 1977 | | fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8, val: u32) Allocator.Error!void { |
| 1978 | | try bytes.ensureUnusedCapacity(gpa, 9); |
| 1979 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32)); |
| 1980 | | bytes.appendAssumeCapacity(mutable); |
| 1981 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 1982 | | appendReservedUleb32(bytes, val); |
| 1983 | | bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); |
| 1891 | fn appendGlobal(w: *Writer, mutable: bool, val: u32) Writer.Error!void { |
| 1892 | try w.writeAll(&.{ |
| 1893 | @intFromEnum(std.wasm.Valtype.i32), |
| 1894 | @intFromBool(mutable), |
| 1895 | @intFromEnum(std.wasm.Opcode.i32_const), |
| 1896 | }); |
| 1897 | try w.writeLeb128(val); |
| 1898 | try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); |
| 1984 | 1899 | } |